複数トランザクションからDBMSの同一行に対して同時に検索処理から更新処理を実行すると、トランザクション内でデータの一貫性が保たれずにデッドロックなどのエラーが発生する場合があります。
この対策として、Optimistic Locking機能とPessimistic Locking機能が選択できます。
アイソレーションレベルにREAD_COMMITTEDを使用している場合、JPAのOptimistic Locking機能を指定することを推奨します。Optimistic Locking機能は、テーブルの更新時に他トランザクションでレコードに更新がなかったか確認し、更新された場合にはエラーを返却するため、データの一貫性を保ちます。本機能は、行をロックする期間が短いため、同一レコードの更新頻度が少ない高負荷な環境で有効です。
アイソレーションレベルにREAD_COMMITTEDより高いレベル(REPEATABLE_READ、SERIALIZABLE)を使用する場合、Pessimistic Locking機能を指定することを推奨します。Pessimistic Locking機能はデッドロックエラーを防ぐことができる場合があるため、同一のレコードの更新頻度が高い場合に効果的です。
Interstage永続性プロバイダが公開しているPessimistic Locking機能は、レコードの検索時にSELECT FOR UPDATE命令を発行しています。SELECT FOR UPDATE文の発行によりDBMSは更新対象の行に排他ロックをかけ、トランザクションが終わるまで他トランザクションによる更新をブロックします。
Optimistic Locking機能の定義方法
Optimistic Locking機能の定義方法については、JPAの規約を参照してください。
Pessimistic Locking機能の定義方法
Pessimistic Locking機能はJPQLクエリのヒントに定義します。
クエリヒント名 | 値(太字:省略値) | 説明 |
---|---|---|
toplink.pessimistic-lock | Lock | クエリの実行にSELECT FOR UPDATE命令を使用します。 |
LockNoWait | クエリの実行にSELECT FOR UPDATE NOWAIT命令を使用します。 | |
NoLock | クエリの実行にSELECTを使用します。 |
値は、大文字と小文字を区別しません。
SELECT FOR UPDATE NOWAIT命令では、排他ロックをかけようとしている行が、他トランザクションによってすでにロックされている場合、ただちにエラーを返却します。そのため、アプリケーションは排他ロックの解放までの不確定な期間、アプリケーションが停止することがなくなります。
クエリヒントは、deployment descriptor (orm.xml)のquery-hintタグ、アノテーション(@QueryHint)、またはQuery.setHintメソッドで指定します。
以下にアノテーションでPessimistic Locking機能を指定する方法を示します。
@NamedQuery(name="Order.findBigOrders", query="SELECT o FROM Order o WHERE o.quantity > :minimum", hints={@QueryHint(name="toplink.pessimistic-lock", value="Lock")})
以下にQuery.setHintメソッドを使用し、Pessimistic Locking機能を指定する方法を示します。上記の例との違いは、この指定方法ではこの実行にしか本機能が利用されませんが、上記の指定方法ではOrder.findBigOrdersクエリを使用しているすべての箇所で本機能が利用されることです。
Collection bigOrders = em.createNamedQuery("Order.findBigOrders") .setParameter("minimum", "1000") .setHint("toplink.pessimistic-lock", "Lock") .getResultList();
注意
行のロック機能に関する注意事項
SELECT FOR UPDATE (NOWAIT)命令は、DBMSの機能です。使用時の注意事項については、DBMSのマニュアルを参照してください。
行のロック機能は、JPQLクエリの機能です。findメソッド、リレーションシップに対するgetアクセッサメソッドなどでは使用できません。
行のロック機能を使用してもDBMSの仕様によりデッドロックが発生する場合があります。この場合には、デッドロックが発生した処理の再実行を試みてください。
データベースが提供するその他のロック機能を使用する場合、NamedQueryアノテーションで定義したJava Persistence Query Language(JPQL)クエリをネイティブSQLのクエリに上書きし、データベースのロック機能を使用するSQLクエリを定義することを検討してください。