如何使用Hibernate 4手动设置@Version字段?

时间:2012-02-06 15:49:18

标签: hibernate jpa-2.0 optimistic-locking jboss7.x

环境:

我有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)。

3 个答案:

答案 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();
  }
}