如何正确锁定和重新加载实体

时间:2011-07-14 10:41:06

标签: java mysql hibernate spring java-ee

在我的Web应用程序中,我有几个可能同时访问相同数据的线程,为什么我决定使用Hibernate实现乐观(版本控制)和悲观锁定。

目前我使用以下模式来锁定实体并对其执行写操作(使用Springs事务管理器和事务划分与@Transactional):

@Transactional
public void doSomething(entity) {
    session.lock(entity, LockMode.UPGRADE);
    session.refresh(entity);

    // I change the entity itself as well as entites in a relationship.
    entity.setBar(...);
    for(Child childEntity : entity.getChildren()) {
        childEntity.setFoo(...);
    }
}

然而,当@Transactional正在刷新时,有时我得到StaleObjectException,告诉我ChildEntity已被同时修改,现在版本错误。

我想我没有正确刷新entity及其子级所以我正在使用陈旧数据。有人可以指出如何实现这一目标吗?我的一些想法包括清除持久性上下文(会话)或再次调用session.lock(entity, LockMode.READ),但我不确定这里的正确性。

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

您可能想看看这个Hibernate-Issue:LockMode.Upgrade doesn't refresh entity values

简而言之:如果给定的实体已经预加载,则Hibernat在成功锁定后不会执行选择。收到锁后,您需要为自己调用实体刷新。

答案 1 :(得分:0)

为什么要将“LockMode.UPGRADE”和乐观锁定一起生活?似乎有争议的事情。

Hibernate永远不会锁定内存中的对象,并始终使用数据库的锁定机制。此外,“如果数据库不支持请求的锁定模式,Hibernate使用适当的备用模式而不是抛出异常。这可以确保应用程序是可移植的。”这意味着,如果您的数据库不支持SELECT ... FOR UPDATE,很可能,您将获得这些例外。

另一个可能的原因是您没有为儿童使用“org.hibernate.annotations.CascadeType.LOCK”。