8.4.5 データベースとの同期
トランザクションのコミットまたはflushメソッドの実行時に,エンティティの状態がデータベースに書き込まれます。一方,明示的にrefreshを呼び出さないかぎり,メモリにロードされたエンティティの状態のリフレッシュは実行されません。
ここでは,データベースへのエンティティ情報の書き込みと,データベースからのエンティティ情報の読み込みについて説明します。
(1) データベースへのエンティティ情報の書き込み
flush操作またはトランザクションのコミットでのエンティティの状態遷移について説明します。次の表ではエンティティAの状態ごとに状態遷移の結果を示しています。
エンティティAの状態 |
状態遷移の結果 |
---|---|
new |
flush操作は無視されます。 |
managed |
データベースと同期されます。 |
removed |
エンティティAはデータベースから削除されます。 |
detached |
flush操作は無視されます。 |
また,managed状態のエンティティAがエンティティBに対してリレーションシップを持っている場合,flush処理の延長で次の表に示す条件に従って,persist操作がカスケードされます。
エンティティBへのリレーションシップのcascade属性の指定 |
エンティティBの状態 |
結果 |
---|---|---|
PERSISTまたはALLが指定されている |
− |
persist操作がエンティティBにカスケードされます。 |
PERSISTまたはALLが指定されていない |
new |
|
managed |
データベースと同期化されます。 |
|
removed |
|
|
detached |
|
なお,トランザクションの外でflushメソッドを呼び出すと,TransactionRequiredExceptionが発生します。
- ポイント
-
リレーションシップとデータベースに対する永続化の関連
-
双方向のリレーションシップを持つmanaged状態のエンティティは,リレーションシップの所有者側で保持される参照を基に永続化されます。アプリケーション開発者は,変更が発生したときに所有者側と被所有者側でそれぞれ,メモリ上の状態に矛盾がないようにエンティティを保持するようアプリケーションを作成してください。
-
単方向のOneToOne,OneToManyの場合,エンティティで定義しているリレーションシップの関係とデータベースのテーブル間の関係が合っていることは,開発者の責任で保証してください。
-
(2) データベースからのエンティティ情報の読み込み
EntityManagerのrefreshメソッドを呼び出すと,それまでに行われたエンティティの変更は破棄され,データベースの内容でエンティティの状態を上書きします。このとき,データベース上に対応する行が存在しない場合はEntityNotFoundExceptionが発生します。
エンティティがmanaged状態以外の場合に,EntityManagerのrefreshメソッドを呼び出すとIllegalArgumentExceptionが発生します。
(a) データベースからのエンティティ情報を読み込むタイミング
refreshメソッドもしくはfindメソッドの実行時,またはクエリの発行時に,データベースからエンティティが読み込まれます。このときに,関連するエンティティもあわせて読み込むことができます。これをフェッチ戦略といいます。フェッチ戦略は各リレーションシップのfetch属性で指定します。fetch属性には次の2種類のどちらかを指定します。
-
データベースからエンティティの情報を読み込むときに,関連するフィールドやエンティティの情報を読み込みます。
-
フィールドまたは関連先に初めてアクセスしたときにデータベースからの読み込みが実行されます。これを,Lazyローディングといいます。
FetchType.EAGERを指定するとデータベースからエンティティの情報を読み込むたびに,関連するフィールドやエンティティの情報を読み込みます。このため,不要な関連先のエンティティの取得を回避したい場合は,FetchType.LAZYを指定してください。
CJPAプロバイダでのfetch属性のサポート範囲を次の表に示します。
リレーションシップのアノテーション |
サポート範囲 |
---|---|
@ManyToMany |
デフォルトはFetchType.LAZYです。 |
@OneToMany |
デフォルトはFetchType.LAZYです。 |
@OneToOne |
デフォルトはFetchType.EAGERです。なお,LAZYを指定したときには,(b)を参照してください。 |
@ManyToOne |
デフォルトではFetchType.EAGERです。なお,LAZYを指定したときには,(b)を参照してください。 |
@Basic |
fetch属性は無視されます。デフォルトのFetchType.EAGERが常に適用されます。 |
(b) @OneToOneおよび@ManyToOneでのLAZYフェッチ
@OneToOneおよび@ManyToOneのリレーションシップに対してフェッチ戦略にLAZYを指定した場合,エンティティクラスのローディング時に,LAZYを指定したフィールドのgetterメソッドにバイナリコードを埋め込みます。
getterメソッドが呼び出されると,埋め込んだバイナリコードの処理を通して,データベースから関連先のエンティティを取得します。getterメソッドがバイナリの埋め込み対象になるため,リレーションの対象となるフィールドにアクセスするためのgetterメソッドを設定しておく必要があります。また,そのgetterメソッドに@OneToOneまたは@ManyToOneを設定しておく必要があります。
- 注意事項
-
getterメソッドでは,リレーションシップのtargetEntityの型から,関連先のエンティティの型を決定します。targetEntityに指定するクラスの型は,フィールド・プロパティの型にキャストできる必要があります。