web-dev-qa-db-ja.com

JPA継承はサブクラスのIDを要求します

Jpaドメインモデルに問題があります。単純なPerson基本クラスとCustomerサブクラスを使用する単純な継承を試してみています。公式ドキュメント(JPAとEclipseLinkの両方)によると、基本クラスのID属性/列のみが必要です。 しかし、テストを実行すると、顧客に@Idがないというエラーが常に表示されますか?

最初はプライベートだったので、問題はid属性の可視性にあると思いました。しかし、保護に変更した後でも(サブクラスが直接アクセスできるように)、機能しません。

人:

@Entity @Table(name="Persons")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "TYPE")
public class Person {

    @Id
    @GeneratedValue
    protected int id;
    @Column(nullable = false)
    protected String firstName;
    @Column(nullable = false)
    protected String lastName;

お客様:

@Entity @Table(name = "Customers")
@DiscriminatorValue("C")
public class Customer extends Person {

    //no id needed here

検討すべきアイデアやリソースが不足しています。それはかなり単純な問題であるはずですが、私はそれを見ていません。

25
lostiniceland

MappedSuperclassを作成して自分で解決しました

@MappedSuperclass
public abstract class EntityBase{
   @Id
   @GeneratedValue
   private int id;

   ...setter/getter
}

すべてのエンティティはこのクラスから継承しています。チュートリアルでこれについて言及されていない理由はまだ疑問ですが、JPA2の実装で改善される可能性があります。

32
lostiniceland

私はまったく同じ問題を抱えていました。私が得ていたサブクラスの場合:

Entity class [...] has no primary key specified. It should define either an @Id, @EmbeddedId or an @IdClass.

私の場合、ルートクラスをpersistence.xmlに追加するのを忘れていたことがわかりました。

個人と顧客の両方が次のように定義されていることを確認してください。

<persistence>
  <persistence-unit>
    ...
    <class>package.Person</class>
    <class>package.Customer</class>
    ...
  </persistence-unit>
</persistence>
9
tk.luczak

私はこれが古いことを知っていますが、それでも有効です。私は同じ問題に遭遇しました。ただし、@ MappedSuperClassは単一継承テーブルと同じではありません。 MappedSuperClassは、サブクラスごとに個別のテーブルを作成します(私が理解しているように)

正確な理由はわかりませんが、継承されたクラスが1つしかない場合は、問題はありませんでした。ただし、2番目と3番目を追加すると、同じエラーが発生しました。子テーブルで@Idアノテーションを指定すると、再び機能し始めました。

私のレイアウトはシンプルで、会社、エージェント、クライアントの連絡先情報でした。

親テーブル:

...
@Entity
@Inheritance(strategy= InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="USER_TYPE", length=10, discriminatorType= DiscriminatorType.STRING)
@Table(name="CONTACTS")
public abstract class AbstractContact implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    @Column (length=10, nullable=false)
    protected String mapType;
    @Column (length=120, nullable=false)
    protected String mapValue;
...

エージェントの連絡先

@Entity
@DiscriminatorValue("Agent")
public class AgentContact extends AbstractContact implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="USER_ID")
    protected Agents agent;
}

会社の連絡先:

@Entity
@DiscriminatorValue("Company")
public class CompanyContact extends AbstractContact implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="USER_ID")
    protected Companies company;

}

クライアントの連絡先:

@Entity
@DiscriminatorValue("Client")
public class ClientContact extends AbstractContact implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    //Client table not built yet so... no mapping
}

クライアントテーブルはまだ作成されていないため、マッピング情報はありませんが、要点はわかります。

MySQLの説明を共有したかったのですが、Windowsコマンドプロンプトは切り取り/コピー/貼り付けには価値がありません。基本的に、そのID(int pri)USER_TYPE(VARCHAR(10))USER_ID(INT)MAPTYPE(VARCHAR(10))MAPVALUE(VARCHAR(120))

私はまだすべてのテストをセットアップする必要がありますが、これまでのところそれは良さそうです(すべてのテストを行うまで待つと、これを投稿するのを忘れてしまうのではないかと心配しています)

4
Alan B. Dee

JPAは、継承を適用​​する2つの異なる方法を知っています。

  • 結合されたテーブル継承
  • 単一テーブル継承

単一テーブル継承では、テーブルの行を区別するための識別子列が必要になります。

結合されたテーブル継承では、各クラスが独自のテーブルを取得するため、識別子列は必要ありません。

あなたの問題は、2つの概念を混ぜ合わせたことだと思います。だからどちらか:

  • 識別子列を定義し、 `InheritanceType.SINGLE_TABLE`を使用しますしないでくださいサブクラスで` @ Table`を使用します
  • または `InheritanceType.JOINED`を使用しますが、しないでください識別子の列と値を指定してください!
3

役立つかもしれないが完全な答えではないいくつかの答えを見たので、私はそれを提供しようとします。

受け入れられた回答が述べているように、基本クラスで継承を宣言する必要があります。それを行うには4つの異なる方法があります。

  • @MappedSuperclass

各具象クラスをテーブルにマップします

  • @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)

@MappedSuperclassに似ていますが、スーパークラスもエンティティです

  • @Inheritance(strategy = InheritanceType.SINGLE_TABLE)

すべての具象クラスは同じテーブルによってマップされます

  • @Inheritance(strategy = InheritanceType.JOINED)

すべてのクラスは独自のテーブルを取得します。結合されたフィールドは、抽象スーパークラスのテーブルによってマップされます。

JPA継承戦略の詳細については、次を参照してください。
https://www.thoughts-on-Java.org/complete-guide-inheritance-strategies-jpa-hibernate/


クラス構造が異なるプロジェクトに分割されている場合、tk.luczakが彼の回答で述べているように、persistence.xmlでスーパークラスを宣言する必要があるかもしれません。

0
stefan

Glassfish/EclipseLinkを使用している場合、このエラーには別の理由がある可能性があります。EclipseLink<2.6.0には 厄介なバグ があり、ラムダ式を含むクラスが無視されます。これにより、ここで説明したような紛らわしいエラーや他の同様のエラーが発生する可能性があります(たとえば、EclipseLinkは、@ Entityアノテーションを持つクラスがエンティティではないことを通知する場合があります)。

Glassfish4.1にはEclipseLink2.5.xが含まれているため、Glassfishを使用している場合、このバグ(および他の多くのバグ)が問題になります。 REST Webサービスで検証を使用できなくなった別のバグのため、4.1.1の代わりにこのバージョンを使用していました。正気を保ちたい場合は、Glassfishからできるだけ離れてください。

0
José González

エンティティに基づいてデータベーススキーマを生成していない場合、このタイプのエラーが発生する可能性があります。その場合:

1番目-スーパークラスには常に@Idが必要です

2番目-サブクラスには、拡張クラス(スーパークラス@Id)を識別する列が必要です。

3番目-上記のケースの最も簡単な解決策は、対応する外部キー制約を使用して、サブクラステーブルに列IDを追加することです。

これが誰かを助けることを願っています!いいぞ!

0
João Rodrigues