首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >删除实体时避免StaleObjectStateException

删除实体时避免StaleObjectStateException
EN

Stack Overflow用户
提问于 2011-10-19 19:11:45
回答 1查看 2.7K关注 0票数 1

我有两个并发线程,它们同时进入一个(Spring)事务服务。

使用Hibernate,服务方法加载一些实体,处理这些实体,找到一个实体并将其从数据库中删除。伪码如下:

代码语言:javascript
复制
@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"),如下所示:

代码语言:javascript
复制
@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已经在会话中,get将返回实体,而load应该重新读取它。

如果两个线程同时进入上述方法,其中一个线程会阻塞锁定阶段,当第一个线程关闭事务时,第二个线程会被唤醒,抛出一个org.hibernate.StaleObjectStateException。为什么?

为什么锁定的加载不直接返回null?我怎样才能做到这一点呢?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-10-21 02:30:26

我花了一些时间调查这个问题,我终于明白了发生了什么。

PESSIMISTIC_WRITE锁试图“锁定”已加载到会话中的实体,它不会从数据库中重新读取对象。在调试调用时,我看到entity == locked返回了true (用Java语言表示)。两个变量都指向同一个实例。

要强制hibernate重新加载实体,必须先将其从会话中删除。

下面的代码实现了这一点:

代码语言:javascript
复制
@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;
}

PESSIMISTIC_WRITE必须与get一起使用,而不能与load一起使用,否则将抛出org.hibernate.ObjectNotFoundException

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7820420

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档