【问题标题】:How to lock and reload an entity correctly如何正确锁定和重新加载实体
【发布时间】:2011-10-05 05:40:10
【问题描述】:

在我的 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),但我不确定这里的正确性。

感谢您的帮助!

【问题讨论】:

    标签: java mysql hibernate spring jakarta-ee


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      为什么要让“LockMode.UPGRADE”和乐观锁定同时存在?似乎是有争议的事情。

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

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

      【讨论】:

      • 我正在使用支持“SELECT ... FOR UPDATE”的 MySQL InnoDB,所以我在这里看不到问题。此外,我也在使用乐观锁定,以便在进行并发修改时获得“通知”。
      • 对不起,错过了您对孩子有问题的一点(粗体:))。我已经更新了答案...
      • 在我的代码中,我总是锁定实体,即使我只想更新实体而不是子项。所以也应该有获得的锁。实际上,我主要担心的是,我不能仅使用刷新来处理最新的实体/子项。
      • 我很困惑,抱歉。我相信您正在使用最新版本的实体,但是孩子有问题。我相信问题的出现是因为没有为他们定义未锁定的孩子(org.hibernate.annotations.CascadeType.LOCK)。是这样还是我错过了什么?如果您不锁定儿童,则无法保证您使用的是最新版本的儿童。另一方面,主实体必须是最新的,因为您在调用 refresh() 之前明确锁定它
      猜你喜欢
      • 2020-12-25
      • 2011-06-04
      • 1970-01-01
      • 2014-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多