Hibernateにおける悲観的ロックの理解


ソフトウェアシステムの同時制御は一般的にロック(楽観ロックと悲観ロックの2種類がある)によって実現され、楽観ロックは事後救済措置であり、プログラムの論理制御版を通じて本来実現されているが、悲観ロックは事前の予防措置であり、データベースのロックメカニズムを利用して実現されている.
楽観ロックはシステムの性能を向上させることができるが、衝突したアクセスに対して事後の救済を行い、ユーザーの入力データ量が少ない場合に適用するのが適切であるが、企業ERPでは、ユーザーとシステムのインタラクションが大量のデータに関連してページフォームに入力され、後で提出に失敗した後にユーザーに再入力を提示するのは現実的ではないため、事前制御が必要であり、悲観ロックを採用しなければならない.
複数のクライアントが同一のペンデータを読み取ったり、同時に1つのペンデータを更新したりする可能性がある場合、同一のデータが修正されて混乱を招くことを防止する最も簡単な手段は、読み取り時にデータをロックし、他のクライアントが同一のペンデータを更新できない読み取り動作である.
データベースに依存する典型的な悲観的なロック呼び出し:

select * from account where name=”John” for update

このsql文は、accountテーブル内の検索条件(name="Erica")に一致するすべてのレコードをロックします.このトランザクションがコミットされる前に(トランザクションがコミットされるとトランザクション中のロックが解放されます)、これらのレコードは変更できません.
Hibernateの悲観的なロックは、データベースベースのロックメカニズムでも実現されています.
次のコードは、クエリー・レコードのロックを実現します.

String hqlStr ="from TUser as user where user.name='John'";
Query query = session.createQuery(hqlStr);
query.setLockMode("user",LockMode.UPGRADE); //   
List userList = query.list();
//     ,    

query.setLockMode
クエリー文では、特定の別名に対応するレコードがロックされます(TUserクラスに別名「user」が指定されています).つまり、返されたすべてのuserレコードがロックされます.
実行中のHibernateによって生成されたSQL文を観察します.

select tuser0_.id as id, tuser0_.name as name, tuser0_.group_id
as group_id, tuser0_.user_type as user_type, tuser0_.sex as sex
from t_user tuser0_ where (tuser0_.name='John' ) for update

ここでHibernateは,データベースのfor update句を用いて悲観的なロック機構を実現した.

Hibernate       :
LockMode.NONE :     。
LockMode.WRITE : Hibernate   Insert   Update           。
LockMode.READ : Hibernate              。

以上の3つのロックメカニズムは一般にHibernate内部で使用され,例えばHibernateはUpdate中にオブジェクトが外部に修正されないことを保証するためにsaveメソッド実装において自動的にターゲットオブジェクトにWRITEロックを加える.
LockMode.UPGRADE:データベースのfor update句を使用してロックします.
LockMode. UPGRADE_NOWAIT:Oracleの特定のインプリメンテーションで、Oracleのfor update nowait句を使用してロックを実行します.
上記の2つのロックメカニズムは、アプリケーション層で一般的に使用されています.ロックは、一般的に以下の方法で実現されます.

Criteria.setLockMode
Query.setLockMode
Session.lock

ただし、クエリが開始される前に(つまりHiberateがSQLを生成する前に)ロックを設定してこそ、データベースのロックメカニズムによってロック処理が行われます.そうしないと、for update句を含まないSelectSQLによってデータがロードされ、データベースのロックとは言えません.