Hibernate 删除实体时避免StaleObjectStateException
我有两个并发线程,它们同时进入(Spring)事务服务 使用Hibernate,服务方法加载一些实体,处理这些实体,找到一个实体并将其从数据库中删除。伪代码如下所示:Hibernate 删除实体时避免StaleObjectStateException,hibernate,transactional,pessimistic-locking,staleobjectstate,Hibernate,Transactional,Pessimistic Locking,Staleobjectstate,我有两个并发线程,它们同时进入(Spring)事务服务 使用Hibernate,服务方法加载一些实体,处理这些实体,找到一个实体并将其从数据库中删除。伪代码如下所示: @Transactional public MyEntity getAndDelete(String prop) { List<MyEntity> list = (List<MyEntity>)sessionFactory .getCurrentSession() .
@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;
}
@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应该重新读取它
如果两个线程同时进入上面的方法,其中一个线程会阻止锁定阶段,当第一个线程关闭事务时,第二个线程被唤醒,抛出org.hibernate.StaleObjectStateException
。为什么?
为什么锁定的加载不只是返回null?我怎样才能做到这一点呢?我花了一些时间研究这个问题,终于明白了会发生什么 悲观写锁尝试“锁定”会话中已加载的实体,但不会从数据库中重新读取对象。调试调用时,我看到
entity==locked
返回true
(用Java术语)。两个变量都指向同一个实例
要强制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) {
// Remove the entity from the session.
sessionFactory.getCurrentSession().evict(entity);
// reload the entity with "select ...for update"
MyEntity locked = (MyEntity)sessionFactory
.getCurrentSession()
.get(MyEntity.class, entity.getId(), new LockOptions(LockMode.PESSIMISTIC_WRITE));
if (locked != null) {
sessionFactory.getCurrentSession().delete(locked);
}
}
return entity;
}
@Transactional
公共MyEntity getAndDelete(字符串属性){
列表=(列表)会话工厂
.getCurrentSession()
.createCriteria(MyEntity.class)
.添加(限制条件。等式(“道具”,道具))
.list();
//处理列表,并找到一个实体
MyEntity实体=findEntity(列表);
如果(实体!=null){
//从会话中删除实体。
sessionFactory.getCurrentSession().Recit(实体);
//使用“选择…进行更新”重新加载实体
MyEntity locked=(MyEntity)会话工厂
.getCurrentSession()
.get(MyEntity.class,entity.getId(),new LockOptions(LockMode.悲观_-WRITE));
如果(已锁定!=null){
sessionFactory.getCurrentSession().delete(已锁定);
}
}
返回实体;
}
悲观的WRITE mut可以与
get
一起使用,而不是load
,因为否则会抛出org.hibernate.ObjectNotFoundException
。我花了一些时间研究这个问题,最终明白了会发生什么
悲观写锁尝试“锁定”会话中已加载的实体,但不会从数据库中重新读取对象。调试调用时,我看到entity==locked
返回true
(用Java术语)。两个变量都指向同一个实例
要强制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) {
// Remove the entity from the session.
sessionFactory.getCurrentSession().evict(entity);
// reload the entity with "select ...for update"
MyEntity locked = (MyEntity)sessionFactory
.getCurrentSession()
.get(MyEntity.class, entity.getId(), new LockOptions(LockMode.PESSIMISTIC_WRITE));
if (locked != null) {
sessionFactory.getCurrentSession().delete(locked);
}
}
return entity;
}
@Transactional
公共MyEntity getAndDelete(字符串属性){
列表=(列表)会话工厂
.getCurrentSession()
.createCriteria(MyEntity.class)
.添加(限制条件。等式(“道具”,道具))
.list();
//处理列表,并找到一个实体
MyEntity实体=findEntity(列表);
如果(实体!=null){
//从会话中删除实体。
sessionFactory.getCurrentSession().Recit(实体);
//使用“选择…进行更新”重新加载实体
MyEntity locked=(MyEntity)会话工厂
.getCurrentSession()
.get(MyEntity.class,entity.getId(),new LockOptions(LockMode.悲观_-WRITE));
如果(已锁定!=null){
sessionFactory.getCurrentSession().delete(已锁定);
}
}
返回实体;
}
悲观的写入mut不能与
get
一起使用,而不能与load
一起使用,因为否则会抛出org.hibernate.ObjectNotFoundException
。会话get现在已被弃用。您知道如何在没有它的情况下重新加载吗?会话get现在已被弃用。你知道如何在没有它的情况下重新加载吗?