如何在Hibernate 4中手动设置@Version字段?

11

环境:

我有一个名为 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
}

我有一个搜索页面,列出了用户列表,然后我点击一个用户以编辑它(在URL中提供了userId)。

在编辑表单中,我将该实体的字段存储在服务器上,当我保存我的用户时,我这样做:

User user = entityManager.find(User.class, userId)
user.setLogin(form.getLogin());
user.setVersion(form.getVersion());
user.setUserId(form.getUserId());
entityManager.merge(user);

问题:

如果我正确地理解了使用 Hibernate 的乐观锁,那么如果我在浏览器中打开两个选项卡以编辑同一个用户,然后在第一个选项卡上更新登录,然后在第二个选项卡上更新登录,我应该会遇到 OptimisticLockException ,对吗?

实际上,在我的应用程序中并不是这样的...... 我验证过,在这两种情况下,form.getVersion() 返回相同的值,即使第二次更新中,user.version 已经被第一个编辑更新了。

我错过了什么吗?

EntityManager 是通过 @RequestScoped 生成的(因此在尝试合并时我使用了两个不同的 EntityManager)。

我尝试在 entityManager.merge(...) 之前执行 entityManager.lock(user, LockModeType.OPTIMISTIC_FORCE_INCREMENT)如此说道),但这没有帮助。

我正在使用 Seam 3 和 JBoss 7.0.2.Final(它使用 Hibernate 4)。


你确定entityManager已经flushed(或者事务已经提交)了吗?生成的SQL更新查询是什么? - JB Nizet
是的,通过激活Hibernate SQL转储,我可以看到SQL更新和JDBC version字段绑定,但它与我在User中设置的version不对应,在WHERE id =?AND version =?中。我开发了一个FlushInterceptor,在我的update函数存在后调用它,并执行entityManager.flush() - Anthony O.
3个回答

7

我刚刚对此进行了更多的研究。

根据JPA规范,不允许修改版本字段

JPA 2.0 Final Spec,第3.4.2节:

实体可以访问其版本字段或属性的状态,或导出用于应用程序访问版本的方法,但不能修改版本值。除第4.10节中提到的例外情况外,只有持久性提供者被允许设置或更新对象中版本属性的值

注意:第4.10节是指忽略版本属性的批量更新。


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 值而非它自己在其他地方复制的值.


1
发现了另一种手动触发乐观锁的方法。我们可以将缓存的 Hibernate 版本与实体中的版本进行比较。我正在使用 Spring Data JPA,并在存储库保存实现中添加了以下内容:
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();
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接