环境:
我有User
实体:
@Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer userId;
@Version
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "VERSION", length = 19)
private Date version;
@Column(nullable = false, length = 20)
private String login;
// Getters and Setters
}
我有一个列出用户的搜索页面,然后点击用户进行编辑(在网址中显示userId
)。
在编辑表单中,我在服务器上存储该实体的字段,当我保存我的用户时,我这样做:
User user = entityManager.find(User.class, userId)
user.setLogin(form.getLogin());
user.setVersion(form.getVersion());
user.setUserId(form.getUserId());
entityManager.merge(user);
问题:
因此,如果我正确理解使用Hibernate的乐观锁定,如果我在浏览器中打开2个选项卡来编辑同一个用户,然后更新第一个选项卡上的登录,然后然后第二个标签上的登录信息,我应该有一个OptimisticLockException
不应该吗?
实际上,我的应用程序不是这种情况......我已经验证,form.getVersion()
在这两种情况下都返回相同的值,即使在第二次更新中,user.version
已更新通过第一次编辑。
我错过了什么吗?
生成EntityManager
@RequestScoped
(因此,当我尝试合并时,我正处于两个不同的EntityManager
。)。
我试图在entityManager.lock(user, LockModeType.OPTIMISTIC_FORCE_INCREMENT)
(as said here)之前做entityManager.merge(...)
,但它没有帮助。
我正在使用Seam 3和JBoss 7.0.2.Final(使用Hibernate 4)。
答案 0 :(得分:7)
我刚刚对此做了一些研究。
根据JPA规范不允许修改版本字段:
JPA 2.0最终规范,第3.4.2节:
实体可以访问其版本字段或属性的状态,或者导出应用程序用于访问版本的方法,但不得修改版本值。除了4.10节中提到的例外情况,只允许持久性提供程序设置或更新对象中version属性的值。
注意:第4.10节引用了忽略版本属性的批量更新。
答案 1 :(得分:6)
实际上我已经找到了一种方法来做到这一点......但我认为它不是很有效(因为还有1个SELECT请求)。
User user = entityManager.find(User.class, userId)
user.setLogin(form.getLogin());
user.setVersion(form.getVersion());
user.setUserId(form.getUserId());
entityManager.detach(user);
entityManager.merge(user);
使用 entityManager.detach(user)
,hibernate现在使用我设置的version
值而不是自己复制的某个值...
答案 2 :(得分:1)
找到了另一种手动触发乐观锁定的方法。我们可以将缓存的Hibernate版本与实体中的版本进行比较。我使用的是Spring数据JPA,并添加了ff。到存储库中保存实现:
EntityEntry entityEntry = entityManager
.unwrap(SessionImplementor.class)
.getPersistenceContext()
.getEntry(entity);
//checked if a cached entry is present
if (entityEntry != null) {
//Compare cached version with the one in the entity
if (!Objects.equals(entityEntry.getVersion(), classMetadata.getVersion(entity))) {
throw new ObjectOptimisticLockingFailureException();
}
}