8.12.6 エンティティでのプライマリキーの指定
エンティティでは,プライマリキーをクラス階層の中で必ず指定してください。プライマリキーを指定する場合には,次に示すルールに従ってください。
-
単体の(複合型でない)プライマリキーは,永続化フィールドまたは永続化プロパティに@Idを指定するか,O/Rマッピングファイルで指定します。これによって,エンティティのフィールドとの対応づけをしてください。
-
複合型のプライマリキーの場合,@EmbeddedIdとして単一のフィールドでプライマリキークラスを指定するか,または@IdClassと@Idによって,フィールドのセットとして複合プライマリキーを指定します。
-
複合型のプライマリキーの場合,プライマリキークラスと呼ばれるプライマリキーを含むクラスを作成します。
これらの条件に従っていない場合は,アプリケーション開始時に例外が発生します。
また,アプリケーションでエンティティのプライマリキーの値を変更しないでください。アプリケーションでプライマリキーの値を変更した場合,実行時に例外が発生します。
- 〈この項の構成〉
(1) プライマリキーの型
単体または複合型のプライマリキーは,次に示す型のどれかにしてください。
-
Javaのプリミティブ型
-
プリミティブをラッパした型
-
java.lang.String
-
java.util.Date
-
java.sql.Date
なお,近似値型(例えば浮動小数点数型)をプライマリキーとして指定すると,CJPAプロバイダの場合,丸め誤差が発生したり,equalsメソッドの結果に信頼性がないという問題が発生したりします。このため,CJPAプロバイダではプライマリキーに近似値型を使用した場合の動作は保証しません。java.util.Dateがプライマリキーとしてフィールド/プロパティで使用される場合,temporal type属性はDATE型として指定する必要があります。
(2) 複合型のプライマリキー
エンティティでは複合型のプライマリキーを扱うことができます。エンティティで複合型のプライマリキーを指定するには,埋め込み型クラスを利用する方法と@IdClassを利用する方法の2とおりがあります。それぞれの方法を説明します。
(a) 埋め込み型クラスを利用する方法
埋め込み型クラスを利用するには,@Embeddableを付与したクラスを作成し,そのクラスのフィールドとして複合型のプライマリキーを定義します。エンティティクラスでは,@Embeddableを付与したクラスの型のフィールドを定義して,@EmbeddedIdをアノテートします。エンティティクラスの例と埋め込み型クラスの例を次に示します。
-
エンティティクラスの例
@Entity public class Employee { private EmployeePK employeePK; public Employee(){ } @EmbeddedId public EmployeePK getEmployeePK(){ retrun this.employeePK; } public void setEmployeePK(EmployeePK employeePK){ this.employeePK = employeePK; } ・・・ }
-
埋め込み型クラスの例
@Embeddable public class EmployeePK { private String name; private int employeeId; public EmployeePK(){ } public boolean equals(Object obj){ ・・・ } public int hashCode(){ ・・・ } ・・・ }
埋め込み型クラスについては「(3) 埋め込み型クラス」を参照してください。なお,アノテーションの代わりにO/Rマッピングファイルを利用することもできます。
(b) @IdClassを利用する方法
@IdClassを利用するには,エンティティクラスでプライマリキーに対応する複数のインスタンス変数を定義して@Idを付与します。また,@IdClassを使用してプライマリキークラスを指定します。プライマリキークラスでは,エンティティで定義したプライマリキーと同じ名前と型を持つフィールドまたはプロパティを定義します。エンティティクラスの例とプライマリキークラスの例を次に示します。
-
エンティティクラスの例
@Entity @IdClass(EmployeePK.class) public class Employee { private String name; private int employeeId; public Employee(){ } @Id public String getName(){ retrun this.name; } public void setName(String name){ this.name = name; } @Id public int getEmployeeId(){ retrun this.employeeId; } public void setName(int employeeId){ this.employeeId = employeeId; } ・・・ }
-
プライマリキークラスの例
public class EmployeePK implements Serializable { private String name; private int employeeId; public EmployeePK(){ } public boolean equals(Object obj){ ・・・ } public int hashCode(){ ・・・ } ・・・ }
埋め込み型クラスの場合と同様に,アノテーションの代わりにO/Rマッピングファイルを利用することもできます。なお,プライマリキークラスのアクセスタイプは,プライマリキーに対応するエンティティクラスのアクセスタイプで決定します。
複合型のプライマリキーを扱うためには,埋め込み型クラスまたは@IdClassのどちらかを使用します。ただし,次に示すルールに従ってください。
-
プライマリキークラスは,publicで引数のないコンストラクタを持たなければならなりません。
-
永続化プロパティを使用する場合,プライマリキークラスのプロパティはpublicまたはprotectedにしてください。
-
プライマリキークラスは,直列化可能にしてください。
-
プライマリキークラスではequalsとhashCodeメソッドを定義します。マップされているデータベース上で主キーが等しい場合にはequalsでtrueを返し,hashCodeの値を等しくする必要があります。
-
複合プライマリキーは,埋め込みクラスとしてマップされているか,エンティティクラスの複数のフィールド/プロパティをマップされている必要があります。
-
プライマリキークラスがエンティティクラスの複合フィールド/プロパティにマップされる場合,プライマリキークラスのプライマリキーのフィールド/プロパティの名前と,エンティティクラスの名前を一致させてください。また,型も同じにしてください。
CJPAプロバイダでは,これらの条件を満たさない場合動作は保証しません。また,条件を満たさない場合はアプリケーションの開始時に例外が発生することもあります。
(3) 埋め込み型クラス
永続化対象の幾つかのフィールドをまとめたクラスを用意すると,エンティティのフィールドとして保持できます。このようなクラスを埋め込み型クラスといいます。
埋め込み型クラスは,エンティティに埋め込まれてエンティティと同じデータベースのテーブルにマップされます。このため,エンティティとは異なり,プライマリキーを持ちません。
ユーザが埋め込み型クラスを利用する場合,埋め込むクラスには@Embeddableを設定します。また,埋め込まれる側のエンティティクラスでは,埋め込み先のフィールド,プロパティに@Embeddedを指定します。なお,アノテーションの代わりにO/Rマッピングファイルで同様に定義することもできます。
埋め込み型クラスは複合型のプライマリキーを定義するために利用することもできます。この場合,埋め込まれるエンティティクラスでは,@Embeddedの代わりに@EmbeddedIdを設定します。
埋め込み型クラスでは,次に示す作成要件を必ず守ってください。
-
埋め込み型クラスは,必ず@Embeddableで定義するか,O/Rマッピングファイルの<embeddable>タグで定義してください。
-
enumやインタフェースを埋め込み型クラスとしないでください。
-
埋め込み型クラスを含むエンティティクラスをdetachedオブジェクトとして値渡ししている場合,Serializableインタフェースを実装してください。
-
埋め込み型クラスおよび埋め込み型クラスの永続化インスタンス変数と,すべてのメソッドではfinalにしないでください。
-
引数なしのコンストラクタを持つ必要があります。
-
引数なしコンストラクタは,publicかprotectedで宣言してください。
-
埋め込み型クラスのインスタンス変数は,private,protected,またはpackageから参照できなければなりません。
-
埋め込み型クラスの永続化インスタンス変数は,クライアントから直接アクセスされないようにしてください。エンティティのアクセサメソッド(getter/setterメソッド)や,ほかのビジネスメソッドでアクセスしないでください。
CJPAプロバイダでは,1.および2.の条件を満たさない場合には例外が発生して,アプリケーションの開始に失敗します。また,3.から8.についても例外が発生するおそれがありますが,例外が発生しない場合でも動作は保証しません。
埋め込み型クラスのアクセスタイプは,埋め込まれた側のエンティティクラスのアクセスタイプで決定します。
埋め込み型クラスを利用する場合,埋め込みの階層は一つにしてください。また,複数のエンティティから埋め込み型クラスのオブジェクトを共有することはできません。これらの条件を満たさない場合,CJPAプロバイダでは動作を保証しません。