如何正确锁定和重新加载实体
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() 之前明确锁定它以上是关于如何正确锁定和重新加载实体的主要内容,如果未能解决你的问题,请参考以下文章
调用解除方法后如何正确重新加载 UIViewController?