JPA
(Java Persistence API)仕様には、エンティティ複合キーを指定する2つの異なる方法があります:@IdClass
および@EmbeddedId
。
マップされたエンティティで両方の注釈を使用していますが、JPA
にあまり詳しくない人にとっては大きな混乱になることがわかりました。
複合キーを指定する方法を1つだけ採用したい。どれが本当に最高ですか?どうして?
@EmbeddedId
では、フィールドアクセス演算子を使用して主キーオブジェクト全体にアクセスできないため、@IdClass
はおそらくより冗長であると考えます。 @EmbeddedId
を使用すると、次のようにできます。
@Embeddable class EmployeeId { name, dataOfBirth }
@Entity class Employee {
@EmbeddedId EmployeeId employeeId;
...
}
これは、フィールドアクセス演算子を介してアクセスされるクラスにすべて集約されるため、複合キーを作成するフィールドの明確な概念を提供します。
@IdClass
と@EmbeddedId
のもう1つの違いは、HQLを作成するときです。
@IdClass
を使用して次のように記述します。
従業員eからe.nameを選択します
@EmbeddedId
を使用すると、次のように記述する必要があります。
従業員eからe.employeeId.nameを選択します
同じクエリに対してより多くのテキストを記述する必要があります。これは、IdClass
によって促進されるようなより自然な言語とは異なると主張する人もいるかもしれません。しかし、ほとんどの場合、特定のフィールドが複合キーの一部であることをクエリから理解することは非常に役立ちます。
IdClassの代わりにEmbeddedIdを使用する必要があるインスタンスを発見しました。このシナリオには、追加の列が定義された結合テーブルがあります。 IdClassを使用してこの問題を解決し、結合テーブルの行を明示的に表すエンティティのキーを表現しようとしました。この方法で動作させることはできませんでした。ありがたいことに「Java Persistence With Hibernate」には、このトピック専用のセクションがあります。提案されたソリューションの1つは、私のものと非常に似ていましたが、代わりにEmbeddedIdを使用しました。オブジェクトを本の中でモデル化して、今では正しく動作するようになりました。
複合主キーを使用するには、次の3つの戦略があります。
@Embeddable
としてマークし、エンティティクラスに@Id
でマークされた通常のプロパティを追加します。@EmbeddedId
でマークされた通常のプロパティを追加します。@Id
でマークし、@IdClass
でエンティティクラスをマークして、主キークラスのクラスを指定します。@Id
とマークされたクラスで@Embeddable
を使用するのが最も自然なアプローチです。 @Embeddable
タグは、とにかく非プライマリキーの埋め込み可能な値に使用できます。複合主キーを単一のプロパティとして扱うことができ、他のテーブルで@Embeddable
クラスを再利用できます。
次に自然なアプローチは、@EmbeddedId
タグの使用です。ここでは、主キークラスは@Embeddable
エンティティではないため、他のテーブルでは使用できませんが、キーをあるクラスの単一の属性として扱うことができます。
最後に、@IdClass
および@Id
アノテーションを使用すると、プライマリキークラスのプロパティの名前に対応するエンティティ自体のプロパティを使用して、複合プライマリキークラスをマップできます。名前は対応している必要があり(これをオーバーライドするメカニズムはありません)、主キークラスは他の2つの手法と同じ義務を果たす必要があります。このアプローチの唯一の利点は、主エンティティを含むエンティティのインターフェイスから主キークラスの使用を「隠す」機能です。 @IdClass
アノテーションは、クラスタイプの値パラメーターを取ります。これは、複合主キーとして使用されるクラスでなければなりません。使用する主キークラスのプロパティに対応するフィールドには、すべて@Id
の注釈を付ける必要があります。
私の知る限り、コンポジットPKにFKが含まれている場合、@IdClass
を使用する方が簡単で簡単です
@EmbeddedId
を使用すると、FK列のマッピングを2回、@Embeddedable
に1回、そして@ManyToOne
に1回、@ManyToOne
を読み取り専用にする必要があります(@PrimaryKeyJoinColumn
)1つの列を2つの変数に設定することはできないため(競合の可能性)。
したがって、@Embeddedable
の単純型を使用してFKを設定する必要があります。
@IdClass
を使用する他のサイトでは、この状況は OneToOneおよびManyToOne関係を介したプライマリキー に示すようにはるかに簡単に処理できます。
JPA 2.0 ManyToOne idアノテーションの例
...
@Entity
@IdClass(PhonePK.class)
public class Phone {
@Id
private String type;
@ManyToOne
@Id
@JoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
private Employee owner;
...
}
JPA 2.0 idクラスの例
...
public class PhonePK {
private String type;
private long owner;
public PhonePK() {}
public PhonePK(String type, long owner) {
this.type = type;
this.owner = owner;
}
public boolean equals(Object object) {
if (object instanceof PhonePK) {
PhonePK pk = (PhonePK)object;
return type.equals(pk.type) && owner == pk.owner;
} else {
return false;
}
}
public int hashCode() {
return type.hashCode() + owner;
}
}
主な利点は、@GeneratedValue
を使用するときにIDに@IdClass
を使用できることです。 @GeneratedValue
に@EmbeddedId
を使用できないと確信しています。
@Id
を使用する場合、複合キーに@EmbeddedId
プロパティを含めることはできません。
EmbeddedIdを使用すると、HQLでIN句を使用できます。例:FROM Entity WHERE id IN :ids
idはEmbeddedIdですが、IdClassで同じ結果を達成するのは苦痛ですが、FROM Entity WHERE idPartA = :idPartA0 AND idPartB = :idPartB0 .... OR idPartA = :idPartAN AND idPartB = :idPartBN