我有 2 个并发线程同时进入(Spring)事务服务。
使用 Hibernate,服务方法加载一些实体,处理这些实体,找到一个并将其从数据库中删除。伪代码如下:
@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;
}
如果两个线程同时传递相同的参数,则两者都将“找到”两个都将调用的相同实体delete
。org.hibernate.StaleObjectStateException
当会话关闭时,其中一个将失败。
我希望两个线程都返回实体,而不会抛出异常。为了实现这一点,我尝试在删除实体之前锁定(使用“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()
因为根据休眠 API,如果已经在会话中,get 将返回实体,而 load 应该重新读取它。
如果两个线程同时进入上述方法,其中一个线程阻塞了一个锁定阶段,当第一个线程关闭事务时,第二个线程被唤醒并抛出一个org.hibernate.StaleObjectStateException
. 为什么?
为什么锁定的负载不只是返回null?我怎么能做到这一点?