1. 概要
このクイックチュートリアルでは、カスタムクエリメソッドおよび事前定義されたリポジトリCRUDメソッドに対してSpringDataJPAでトランザクションロックを有効にする方法について説明します。
また、さまざまなロックタイプとトランザクションロックタイムアウトの設定についても説明します。
2. ロックタイプ
JPAには、ペシミスティックロックとオプティミスティックロックの2つの主要なロックタイプが定義されています。
2.1. 悲観的なロック
トランザクションでペシミスティックロックを使用してエンティティにアクセスすると、すぐにロックされます。 トランザクションは、トランザクションをコミットまたはロールバックすることによってロックを解放します。
2.2. 楽観的なロック
オプティミスティックロックでは、トランザクションはエンティティをすぐにはロックしません。代わりに、トランザクションは通常、バージョン番号が割り当てられたエンティティの状態を保存します。
別のトランザクションでエンティティの状態を更新しようとすると、トランザクションは更新中に保存されたバージョン番号を既存のバージョン番号と比較します。
この時点で、バージョン番号が異なる場合は、エンティティを変更できないことを意味します。 アクティブなトランザクションがある場合、そのトランザクションはロールバックされ、基礎となるJPA実装はOptimisticLockExceptionをスローします。
バージョン番号のアプローチとは別に、現在の開発コンテキストに最も適したアプローチに応じて、タイムスタンプ、ハッシュ値の計算、シリアル化されたチェックサムなどの他のアプローチを使用できます。
3. クエリメソッドでのトランザクションロックの有効化
エンティティのロックを取得するには、必要なロックモードタイプを渡すことで、ターゲットクエリメソッドにLockアノテーションを付けることができます。.
ロックモードタイプは、エンティティのロック中に指定される列挙値です。 次に、指定されたロックモードがデータベースに伝播され、対応するロックがエンティティオブジェクトに適用されます。
Spring Data JPAリポジトリのカスタムクエリメソッドのロックを指定するには、メソッドに @Lock アノテーションを付け、必要なロックモードタイプを指定します。
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
@Query("SELECT c FROM Customer c WHERE c.orgId = ?1")
public List<Customer> fetchCustomersByOrgId(Long orgId);
findAllやfindById(id)などの事前定義されたリポジトリメソッドにロックを適用するには、リポジトリ内でメソッドを宣言し、Lockでメソッドにアノテーションを付ける必要があります。 ]注釈:
@Lock(LockModeType.PESSIMISTIC_READ)
public Optional<Customer> findById(Long customerId);
ロックが明示的に有効になっていて、アクティブなトランザクションがない場合、基礎となるJPA実装はTransactionRequiredExceptionをスローします。
ロックを付与できず、ロックの競合によってトランザクションのロールバックが発生しない場合、JPAはLockTimeoutExceptionをスローします。 ただし、アクティブなトランザクションにロールバックのマークは付けられません。
4. トランザクションロックタイムアウトの設定
Pessimistic Lockingを使用する場合、データベースはエンティティをすぐにロックしようとします。 基盤となるJPA実装は、ロックをすぐに取得できない場合、LockTimeoutExceptionをスローします。 このような例外を回避するために、ロックタイムアウト値を指定できます。
Spring Data JPAでは、 QueryHints アノテーションを使用して、クエリメソッドに QueryHint を配置することにより、ロックタイムアウトを指定できます。
@Lock(LockModeType.PESSIMISTIC_READ)
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "3000")})
public Optional<Customer> findById(Long customerId);
さまざまなスコープでのロックタイムアウトヒントの設定の詳細については、このObjectDBの記事を参照してください。
5. 結論
このチュートリアルでは、さまざまなタイプのトランザクションロックモードについて学習しました。 SpringDataJPAでトランザクションロックを有効にする方法を学びました。 また、ロックタイムアウトの設定についても説明しました。
適切な場所に適切なトランザクションロックを適用すると、大量の同時使用アプリケーションでデータの整合性を維持するのに役立ちます。
トランザクションがACIDルールに厳密に従う必要がある場合は、ペシミスティックロックを使用する必要があります。 複数の同時読み取りを許可する必要がある場合、およびアプリケーションコンテキスト内で結果整合性が許容される場合は、OptimisticLockingを適用する必要があります。
もちろん、PessimisticLockingとOptimisticLockingの両方のサンプルコードは、Githubのにあります。