在我的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)
,但我不确定这里的正确性。
感谢您的帮助!
答案 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”。