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

Posted

技术标签:

【中文标题】如何正确锁定和重新加载实体【英文标题】: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),但我不确定这里的正确性。

感谢您的帮助!

【问题讨论】:

【参考方案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() 之前明确锁定它

以上是关于如何正确锁定和重新加载实体的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 IGListKit 强制重新加载所有部分?

如何在 Dart 中以正确的方式重定向和重新加载?

如何正确删除小部件并更新/重新加载小部件

调用解除方法后如何正确重新加载 UIViewController?

如何使用代码优先实体框架在 ASP.Net MVC3 中重新加载多对多导航属性

tableView.reloadData() 如何正确重新加载数据?