同時に(Spring)トランザクションサービスに入る2つの同時スレッドがあります。
Hibernate を使用して、サービス メソッドはいくつかのエンティティをロードして処理し、エンティティを見つけて DB から削除します。擬似コードは次のとおりです。
@Transactional
public MyEntity getAndDelete(String prop) {
List<MyEntity> list = (List<MyEntity>)sessionFactory
.getCurrentSession()
.createCriteria(MyEntity.class)
.add( Restrictions.eq("prop", prop) )
.list();
// process the list, and find one entity
MyEntity entity = findEntity(list);
if (entity != null) {
sessionFactory.getCurrentSession().delete(entity);
}
return entity;
}
2 つのスレッドが同時に同じパラメーターを渡す場合、両方が同じエンティティを「検索」し、両方が を呼び出しますdelete
。org.hibernate.StaleObjectStateException
そのうちの 1 つは、セッションが閉じているときに をスローして失敗します。
例外がスローされずに、両方のスレッドがエンティティを返すことを望みます。これを達成するために、次のように、エンティティを削除する前に(「select ... for update」で)ロックしようとしました:
@Transactional
public MyEntity getAndDelete(String prop) {
List<MyEntity> list = (List<MyEntity>)sessionFactory
.getCurrentSession()
.createCriteria(MyEntity.class)
.add( Restrictions.eq("prop", prop) )
.list();
// process the list, and find one entity
MyEntity entity = findEntity(list);
if (entity != null) {
// reload the entity with "select ...for update"
// to ensure the exception is not thrown
MyEntity locked = (MyEntity)sessionFactory
.getCurrentSession()
.load(MyEntity.class, entity.getId(), new LockOptions(LockMode.PESSIMISTIC_WRITE));
if (locked != null) {
sessionFactory.getCurrentSession().delete(locked);
}
}
return entity;
}
load()
代わりに使用するのget()
は、hibernate API によると、既にセッションにある場合は get がエンティティを返し、load はエンティティを再読み取りする必要があるためです。
2 つのスレッドが同時に上記の方法に入ると、そのうちの 1 つがロック ステージをブロックし、最初のスレッドがトランザクションを閉じると、2 番目のスレッドが呼び出されてorg.hibernate.StaleObjectStateException
. なんで?
ロックされたロードが null を返さないのはなぜですか? どうすればこれを達成できますか?