8.7.4 キャッシュ機能を使用するときの注意事項
ここでは,エンティティオブジェクトのキャッシュ機能を使用するときの注意事項を説明します。
- 〈この項の構成〉
(1) クエリでデータを更新または削除した場合の注意
アプリケーション内でJPQLやネイティブクエリを使用して,データを更新したり,削除したりした場合,キャッシュの内容は更新されません。refresh操作などを行って,データベースの内容を取得し直してください。
次に示す操作の場合,キャッシュのデータを読み込むため,データベースへの更新がされません。
-
データをエンティティオブジェクトに読み込む。
キャッシュにもエンティティのデータが登録されます。
-
1.で読み込んだデータを含む削除クエリを実行する。
削除クエリの実行でデータは削除されますが,キャッシュは削除されません。
-
1.と同じデータをエンティティオブジェクトに読み込む。
1.と同じデータのため,キャッシュにあるデータを読み込みます。
-
3.のデータをflushする。
データベースには該当する行がないため,追加または更新処理ができません。
(2) キャッシュを使用する永続化コンテキストが複数ある場合の注意
キャッシュを使用することで,データベースへのアクセス頻度を下げることができます。ただし,一方でキャッシュによるデータのタイムラグが発生して,楽観的ロック例外の発生頻度が高くなるおそれもあります。
キャッシュは永続化コンテキスト単位に存在します。そのため,永続化コンテキストと対で存在するEntityManagerが複数かつ同時期に生成され,同一プライマリキーのエンティティを同時に操作すると,一方でデータを更新しても他方でタイミング良く,更新後のデータを参照できないことがあります。これによって,楽観的ロック例外が発生しやすくなります。
次に,楽観的ロック例外が発生する仕組みと対処方法について説明します。
(a) 楽観的ロック例外が発生する例
ここでは,次の図に示す環境で,永続化コンテキスト単位にキャッシュが存在する場合を例に説明します。
図の状態では,キャッシュとデータベースには不整合はなく,すでにキャッシュにAというデータが格納されているものとします。
-
永続化コンテキスト1でデータをAからBに変更します。
このとき,キャッシュとデータベースの内容が等しいため例外は発生しません。
図8‒20 永続化コンテキスト1でのデータの変更 -
1.の処理が終了後,永続化コンテキスト2でAのデータを変更します。
キャッシュのデータは変更されていないため,データベースのデータとキャッシュに不整合があります。このため,楽観的ロックによる例外が発生します。
図8‒21 永続化コンテキスト2でのデータの変更
このような環境の場合,キャッシュを使用すると,更新されないキャッシュが残ってしまい,楽観的ロックによる例外が発生することがあります。
(b) 対処方法
楽観的ロック例外が発生した場合は,キャッシュ内の該当オブジェクトは削除されます。このため,findメソッドを実行するか,またはrefreshメソッドを実行して関連するすべてのデータを再度データベースから取得してください。これによって,キャッシュのデータとデータベースを同期できます。
(3) キャッシュの登録および更新タイミングに関する注意事項
-
JPQLを使用して悲観的ロックを取得する場合,クエリの実行時に返されるエンティティオブジェクトはキャッシュに登録されません。
-
ネイティブクエリを利用して,@SqlResultSetMappingでエンティティを戻り値とするクエリを実行した場合,戻り値のエンティティオブジェクトはキャッシュに格納されます。
-
エンティティのrefresh操作で,エンティティオブジェクトの読み込み後にほかのスレッドでOptimisticLockExceptionが発生してキャッシュが削除されると,リフレッシュ処理を実行してもキャッシュにエンティティオブジェクトは登録されません。