根本的なメカニズムが異なるため、最初に楽観的ロックと悲観的ロックを区別します。
オプティミスティックロックはJPAによって完全に制御され、DBテーブルに追加のバージョン列のみが必要です。リレーショナルデータの保存に使用される基盤となるDBエンジンから完全に独立しています。
一方、ペシミスティックロックは、基盤となるデータベースによって提供されるロックメカニズムを使用して、テーブル内の既存のレコードをロックします。 JPAは、これらのロックをトリガーする方法を知る必要があり、一部のデータベースはそれらをサポートしていないか、部分的にしかサポートしていません。
次に、ロックタイプのリストに移動します。
-
LockModeType.Optimistic
- エンティティがバージョンフィールドを指定する場合、これがデフォルトです。バージョン列のないエンティティの場合、このタイプのロックの使用は、JPA実装で機能することが保証されていません。 ObjectDBで述べられているように、このモードは通常無視されます。私の意見では、ロックが最終的にOPTIMISTICであっても、ロックモードを動的に計算し、それをさらに渡すことができるようにするためにのみ存在します。可能性の高いユースケースではありませんが、デフォルト値を参照するオプションを提供することは常に優れたAPI設計です。
-
例:
`LockModeType lockMode = resolveLockMode(); A a = em.find(A.class, 1, lockMode);`
-
LockModeType.OPTIMISTIC_FORCE_INCREMENT
- これはめったに使用されないオプションです。ただし、別のエンティティによるこのエンティティの参照をロックする場合は、合理的である可能性があります。つまり、エンティティが変更されていなくても、エンティティの操作をロックしたいのですが、他のエンティティはこのエンティティに関連して変更される可能性があります。
- 例:エンティティBookとShelfがあります。 BookをShelfに追加することは可能ですが、bookにはそのシェルフへの参照がありません。本を棚に移動するアクションをロックして、このトランザクションが終了する前に本が(別のトランザクションのために)別の棚に置かれないようにすることは合理的です。このアクションをロックするには、本がまだ棚にある必要がないため、現在の本棚エンティティをロックするだけでは不十分です。また、トランザクションごとに異なる可能性があるため、すべてのターゲット本棚をロックすることは意味がありません。理にかなっている唯一のことは、たとえ私たちの場合は変更されなくても(本棚への参照を保持していない場合でも)、本のエンティティ自体をロックすることです。
-
LockModeType.PESSIMISTIC_READ
- このモードは
LockModeType.PESSIMISTIC_WRITE
に似ています 、ただし1つの点で異なります。あるトランザクションによって同じエンティティに書き込みロックが設定されるまで、エンティティの読み取りをブロックしないでください。また、LockModeType.PESSIMISTIC_READ
を使用して他のトランザクションをロックすることもできます 。 WRITEロックとREADロックの違いは、ここ(ObjectDB)とここ(OpenJPA)で詳しく説明されています。エンティティがすでに別のトランザクションによってロックされている場合、そのエンティティをロックしようとすると例外がスローされます。この動作は、例外をスローしてトランザクションをロールバックする前に、ロックが解放されるのをしばらく待つように変更できます。これを行うには、javax.persistence.lock.timeout
を指定します 例外をスローする前に待機するミリ秒数のヒント。 Java EEチュートリアルで説明されているように、これを複数のレベルで行うには複数の方法があります。
-
LockModeType.PESSIMISTIC_WRITE
- これは
LockModeType.PESSIMISTIC_READ
のより強力なバージョンです 。WRITE
の場合 ロックが設定されている場合、データベースを使用したJPAは、READ
のように書き込むだけでなく、他のトランザクションがエンティティを読み取ることを防ぎます。 ロックします。 - 基盤となるDBと連携してJPAプロバイダーでこれを実装する方法は規定されていません。 Oracleの場合、Oracleは
READ
に近いものを提供していません。 ロック。SELECT...FOR UPDATE
むしろWRITE
ロック。これは、休止状態のバグであるか、カスタムの「よりソフトな」READ
を実装する代わりに決定しただけの場合があります。 ロック、「より難しい」WRITE
代わりにロックが使用されます。これはほとんど一貫性を損なうものではありませんが、READ
のすべてのルールを保持するわけではありません ロック。READ
を使用していくつかの簡単なテストを実行できます より多くのトランザクションがREAD
を取得できるかどうかを確認するために、ロックと長時間実行されるトランザクション 同じエンティティをロックします。これは可能であるはずですが、WRITE
では不可能です。 ロック。
- LockModeType.PESSIMISTIC_FORCE_INCREMENT
- これは、めったに使用されないもう1つのロックモードです。ただし、これは
PESSIMISTIC
を組み合わせる必要があるオプションです。 およびOPTIMISTIC
メカニズム。プレーンなPESSIMISTIC_WRITE
を使用する 次のシナリオでは失敗します:- トランザクションAは楽観的ロックを使用し、エンティティEを読み取ります
- トランザクションBはエンティティEの書き込みロックを取得します
- トランザクションBはEのロックをコミットして解放します
- トランザクションAはEを更新してコミットします
- ステップ4で、バージョン列がトランザクションBによってインクリメントされない場合、AがBの変更を上書きすることを妨げるものは何もありません。ロックモード
LockModeType.PESSIMISTIC_FORCE_INCREMENT
トランザクションBにバージョン番号の更新を強制し、トランザクションAをOptimisticLockException
で失敗させます 、Bが悲観的なロックを使用していたとしても。
- LockModeType.NONE
- これは、エンティティがバージョンフィールドを提供しない場合のデフォルトです。これは、ロックが有効になっていないことを意味します。競合はベストエフォートベースで解決され、検出されません。これは、トランザクションの外部で許可される唯一のロックモードです