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

发布于 2025-01-03 03:08:16 字数 1576 浏览 8 评论 0原文

环境:

我有一个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 在 URL 中)。

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

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()在两种情况下都返回相同的值,即使在第二次更新中,用户.version 已由第一次编辑更新。

我错过了什么吗?

EntityManager 是由 @RequestScoped 生成的(因此,当我尝试合并时,我处于两个不同的 EntityManager 上...)。

我尝试在 entityManager.merge(...) (如此处所述),但没有帮助。

我将 Seam 3 与 JBoss 7.0.2.Final(使用 Hibernate 4)一起使用。

Environment:

I have that User entity :

@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
}

I have a search page which lists users, then I click on a user to edit it (giving its userId in the URL).

In the edit form, I store on the server the fields of that entity and when I save my User I do this :

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

Question:

So if I correctly understood optimistic locking with Hibernate, if I open 2 tabs in my browser to edit the same user, then update the login on the first tab, and then the login on the second tab, I should have an OptimisticLockException shouldn't I ?

Actually, this is not the case on my application... I verified, the form.getVersion() return the same value in both case, even if that in the second update, the user.version has been updated by the first edit.

Am I missing something ?

The EntityManager is produced @RequestScoped (so I'm on two different EntityManagers when I try to merge...).

I've tried to do a entityManager.lock(user, LockModeType.OPTIMISTIC_FORCE_INCREMENT) before entityManager.merge(...) (as said here), but it didn't help.

I'm using Seam 3 with JBoss 7.0.2.Final (which uses Hibernate 4).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

北音执念 2025-01-10 03:08:16

我刚刚对此做了一些更多的研究。

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

JPA 2.0 Final Spec,Section 3.4.2:

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

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

I just did some more research on this.

It is not allowed to modify the version field according to the JPA spec:

JPA 2.0 Final Spec, Section 3.4.2:

An entity may access the state of its version field or property or export a method for use by the application to access the version, but must not modify the version value. With the exception noted in section 4.10, only the persistence provider is permitted to set or update the value of the version attribute in the object.

Note: Section 4.10 refers to batch updates which ignore the version attribute.

慕巷 2025-01-10 03:08:16

实际上我已经找到了一种方法来做到这一点...但我认为它并不是真正有效(因为还有 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 值,而不是它自己复制到某处的值...

Actually I've found a way to do that... but I think it's not really efficient (because there is 1 more SELECT request).

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);

With entityManager.detach(user), hibernate now uses my setted version value instead of its own copied somewhere value...

别把无礼当个性 2025-01-10 03:08:16

找到了另一种手动触发乐观锁定的方法。我们可以将缓存的 Hibernate 版本与实体中的版本进行比较。我正在使用 spring data 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();
  }
}

Found another way of triggering optimistic locking manually. We can compare the cached Hibernate version with the version in the entity. I'm using spring data JPA, and added the ff. to the repository save implementation:

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();
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文