web-dev-qa-db-ja.com

Spring Data JPAクエリで@lockタイムアウトを指定するにはどうすればよいですか?

指定方法@Lockクエリのタイムアウト?私はOracle 11gを使用しています。'select id from table where id = ?1 for update wait 5'

私はこのようにメソッドを定義しました:

@Lock(LockModeType.PESSIMISTIC_WRITE)
Stock findById(String id);

永久にロックしているようです。 javax.persistence.lock.timeout=0LocalContainerEntityManagerFactoryBean.jpaProperties、影響はありません。

17
user2501759

エンティティを悲観的にロックするには、ロックモードを_PESSIMISTIC_READ_、_PESSIMISTIC_WRITE_、または_PESSIMISTIC_FORCE_INCREMENT_に設定します。

ペシミスティックロックを取得できなくても、ロックの失敗によってトランザクションがロールバックされない場合は、LockTimeoutExceptionがスローされます。

悲観的ロックタイムアウト

永続化プロバイダーがデータベーステーブルのロックを取得するために待機する時間(ミリ秒単位)は、javax.persistence.lock.timeoutプロパティを使用して指定できます。ロックの取得にかかる時間がこのプロパティの値を超えると、LockTimeoutExceptionがスローされますが、現在のトランザクションはロールバック対象としてマークされません。 このプロパティが0に設定されている場合、永続性プロバイダーは、すぐにロックを取得できない場合にLockTimeoutExceptionをスローする必要があります。

_javax.persistence.lock.timeout_が複数の場所に設定されている場合、値は次の順序で決定されます。

  1. EntityManagerまたは_Query methods_のいずれかの引数。
  2. _@NamedQuery_アノテーションの設定。
  3. _Persistence.createEntityManagerFactory_メソッドへの引数。
  4. _persistence.xml_デプロイメント記述子の値。

Spring Data 1.6以降の場合

_@Lock_は、Spring Data JPAのバージョン1.6の時点でCRUDメソッドでサポートされています(実際、すでに milestone が利用可能です)。詳細はこちら チケット をご覧ください。

そのバージョンでは、次のように宣言するだけです。

_interface WidgetRepository extends Repository<Widget, Long> {

  @Lock(LockModeType.PESSIMISTIC_WRITE)
  Widget findOne(Long id);
}
_

これにより、バッキングリポジトリプロキシのCRUD実装部分が、構成されたLockModeTypeをEntityManagerfind(…)呼び出しに適用します。

一方、

Spring Data 1.6の以前のバージョンの場合

Spring Dataの悲観的な_@Lock_アノテーションは、(指摘したように)クエリにのみ適用されます。トランザクション全体に影響を与える可能性があるとわかっている注釈はありません。悲観的ロックでfindByOnePessimisticを呼び出すfindByOneメソッドを作成するか、findByOneを変更して常に悲観的ロックを取得できます。

独自のソリューションを実装したい場合は、おそらくできます。内部では、_@Lock_アノテーションはLockModePopulatingMethodIntercceptorによって処理され、次の処理を行います。

_TransactionSynchronizationManager.bindResource(method, lockMode == null ? NULL : lockMode);
_

_ThreadLocal<LockMode>_メンバー変数を持つ静的ロックマネージャーを作成し、ThreadLocalでロックモードを設定してbindResourceを呼び出すすべてのリポジトリー内のすべてのメソッドにアスペクトをラップすることができます。これにより、スレッドごとにロックモードを設定できます。次に、メソッドを実行する前にスレッド固有のロックモードを設定し、メソッドの実行後にそれをクリアするアスペクトでメソッドをラップする独自の_@MethodLockMode_アノテーションを作成できます。

リソースリンク:

  1. Spring Data JPAでエンティティを検索するときにLockModeType.PESSIMISTIC_WRITEを有効にする方法
  2. Spring Data JPAにカスタムメソッドを追加する方法
  3. PostgresでのSpring Data Pessimistic Lockタイムアウト
  4. JPAクエリAPI

悲観的ロックタイムアウトのさまざまな例

悲観的ロックの設定

エンティティオブジェクトは、lockメソッドによって明示的にロックできます。

_em.lock(employee, LockModeType.PESSIMISTIC_WRITE);
_

最初の引数はエンティティオブジェクトです。 2番目の引数は、要求されたロックモードです。

明示的なロックにはアクティブなトランザクションが必要なため、ロックが呼び出されたときにアクティブなトランザクションがない場合、TransactionRequiredExceptionがスローされます。

LockTimeoutExceptionは、要求された悲観的ロックを許可できない場合にスローされます。

  • 別のユーザー(別のEntityManagerインスタンスによって表される)が現在そのデータベースオブジェクトで_PESSIMISTIC_READ_ロックを保持している場合、_PESSIMISTIC_WRITE_ロック要求は失敗します。
  • 別のユーザーが現在そのデータベースオブジェクトで_PESSIMISTIC_WRITE_ロックまたは_PESSIMISTIC_WRITE_ロックを保持している場合、_PESSIMISTIC_READ_ロック要求は失敗します。

クエリヒントの設定(スコープ)

クエリヒントは、次のスコープで設定できます(グローバルからローカル):

永続性ユニット全体-_persistence.xml_プロパティを使用:

_<properties>
   <property name="javax.persistence.query.timeout" value="3000"/>
</properties>
_

EntityManagerFactoryの場合-createEntityManagerFacotoryメソッドを使用:

_Map<String,Object> properties = new HashMap();
properties.put("javax.persistence.query.timeout", 4000);
EntityManagerFactory emf =
  Persistence.createEntityManagerFactory("pu", properties);
_

EntityManagerの場合-createEntityManagerメソッドを使用:

_Map<String,Object> properties = new HashMap();
properties.put("javax.persistence.query.timeout", 5000);
EntityManager em = emf.createEntityManager(properties);
_

または、setPropertyメソッドを使用します。

_em.setProperty("javax.persistence.query.timeout", 6000);
_

_named query_定義の場合-hints要素を使用:

_@NamedQuery(name="Country.findAll", query="SELECT c FROM Country c",
    hints={@QueryHint(name="javax.persistence.query.timeout", value="7000")})
_

特定のクエリ実行の場合-setHintメソッドを使用(クエリ実行前):

_query.setHint("javax.persistence.query.timeout", 8000);
_

リソースリンク:

  1. JPAでのロック
  2. 悲観的ロックタイムアウト
17
SkyWalker

@QueryHints Springデータ:

@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="5000")})
Stock findById(String id)
10

Spring Data 1.6以降では、Spring Data jpaによって提供される@Lockアノテーションを使用できます。

また、@ QueryHintsを使用してロックタイムアウトを設定することもできます。元々、デフォルトのCRUDメソッドではクエリヒントアノテーションがサポートされていませんでしたが、1.6M1の修正後に利用可能になりました。 https://jira.spring.io/browse/DATAJPA-17

以下は、排他ロックであるPESSIMISTIC_WRITEモードタイプのペシミスティックロックの例です。

@Lock(LockModeType.PESSIMISTIC_WRITE) 
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value   ="5000")}) 
Customer findByCustomerId(Long customerId);
1
arpit garg

javax.persistence.lock.timeoutは、以下のように提供された場合にも機能しません

@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout",value = "15000")})

しかし、私はうまくいった他のものを試しました。 @RepositoryとCrudRepositoryを使用する代わりに、エンティティマネージャーを使用してhbernateを構成しています。 createQueryをロックおよびロックタイムアウトの設定と一緒に使用しました。そして、この構成は期待どおりに機能しています。私は2つのトランザクションを並列で実行していて、DBのまったく同じ行をロックしようとしています。最初のトランザクションはWRITEロックを取得でき、ロックを解放する前に約10秒間ロックを保持します。一方、2番目のトランザクションは同じ行のロックを取得しようとしますが、javax.persistence.lock.timeoutが15秒に設定されているため、ロックが解放されるのを待ってから、独自のロックを取得します。したがって、フローをシリアル化します。

@Component
public class Repository {

    @PersistenceContext
    private EntityManager em;

    public Optional<Cache> getById(int id){
        List<Cache> list = em.createQuery("select c from Cache c where c.id = ?1")
                            .setParameter(1, id)
                            .setHint("javax.persistence.lock.timeout", 15000)
                            .setLockMode(LockModeType.PESSIMISTIC_WRITE)
                            .getResultList();


        return Optional.ofNullable(list.get(0));
    }

    public void save(Cache cache) {
        cache = em.find(Cache.class, cache.getId());
        em.merge(cache);
    }
}
0
goyalshub1509