指定方法@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=0
LocalContainerEntityManagerFactoryBean.jpaProperties
、影響はありません。
エンティティを悲観的にロックするには、ロックモードを_
PESSIMISTIC_READ
_、_PESSIMISTIC_WRITE
_、または_PESSIMISTIC_FORCE_INCREMENT
_に設定します。ペシミスティックロックを取得できなくても、ロックの失敗によってトランザクションがロールバックされない場合は、
LockTimeoutException
がスローされます。永続化プロバイダーがデータベーステーブルのロックを取得するために待機する時間(ミリ秒単位)は、javax.persistence.lock.timeoutプロパティを使用して指定できます。ロックの取得にかかる時間がこのプロパティの値を超えると、
LockTimeoutException
がスローされますが、現在のトランザクションはロールバック対象としてマークされません。 このプロパティが0に設定されている場合、永続性プロバイダーは、すぐにロックを取得できない場合にLockTimeoutException
をスローする必要があります。_
javax.persistence.lock.timeout
_が複数の場所に設定されている場合、値は次の順序で決定されます。
EntityManager
または_Query methods
_のいずれかの引数。- _
@NamedQuery
_アノテーションの設定。- _
Persistence.createEntityManagerFactory
_メソッドへの引数。- _
persistence.xml
_デプロイメント記述子の値。
_@Lock
_は、Spring Data JPAのバージョン1.6の時点でCRUDメソッドでサポートされています(実際、すでに milestone が利用可能です)。詳細はこちら チケット をご覧ください。
そのバージョンでは、次のように宣言するだけです。
_interface WidgetRepository extends Repository<Widget, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
Widget findOne(Long id);
}
_
これにより、バッキングリポジトリプロキシのCRUD実装部分が、構成されたLockModeTypeをEntityManager
のfind(…)
呼び出しに適用します。
Spring Dataの悲観的な_@Lock
_アノテーションは、(指摘したように)クエリにのみ適用されます。トランザクション全体に影響を与える可能性があるとわかっている注釈はありません。悲観的ロックでfindByOnePessimistic
を呼び出すfindByOne
メソッドを作成するか、findByOne
を変更して常に悲観的ロックを取得できます。
独自のソリューションを実装したい場合は、おそらくできます。内部では、_@Lock
_アノテーションはLockModePopulatingMethodIntercceptor
によって処理され、次の処理を行います。
_TransactionSynchronizationManager.bindResource(method, lockMode == null ? NULL : lockMode);
_
_ThreadLocal<LockMode>
_メンバー変数を持つ静的ロックマネージャーを作成し、ThreadLocalでロックモードを設定してbindResourceを呼び出すすべてのリポジトリー内のすべてのメソッドにアスペクトをラップすることができます。これにより、スレッドごとにロックモードを設定できます。次に、メソッドを実行する前にスレッド固有のロックモードを設定し、メソッドの実行後にそれをクリアするアスペクトでメソッドをラップする独自の_@MethodLockMode
_アノテーションを作成できます。
エンティティオブジェクトは、lockメソッドによって明示的にロックできます。
_em.lock(employee, LockModeType.PESSIMISTIC_WRITE);
_
最初の引数はエンティティオブジェクトです。 2番目の引数は、要求されたロックモードです。
明示的なロックにはアクティブなトランザクションが必要なため、ロックが呼び出されたときにアクティブなトランザクションがない場合、TransactionRequiredException
がスローされます。
LockTimeoutException
は、要求された悲観的ロックを許可できない場合にスローされます。
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);
_
@QueryHints
Springデータ:
@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="5000")})
Stock findById(String id)
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);
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);
}
}