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

Posted

技术标签:

【中文标题】如何使用 Hibernate 4 手动设置 @Version 字段?【英文标题】:How to manually set the @Version fields with Hibernate 4? 【发布时间】:2012-02-28 02:06:45 【问题描述】:

环境:

我有那个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 的乐观锁定,如果我在浏览器中打开 2 个选项卡来编辑同一个用户,然后更新第一个选项卡上的登录,然后 然后 在第二个选项卡上登录,我应该有一个 OptimisticLockException 不是吗?

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

我错过了什么吗?

EntityManager 产生 @RequestScoped(所以当我尝试合并时,我在两个不同的 EntityManagers 上......)。

我尝试在entityManager.merge(...) (as said here) 之前执行entityManager.lock(user, LockModeType.OPTIMISTIC_FORCE_INCREMENT),但没有帮助。

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

【问题讨论】:

您确定 entityManager 已刷新(或事务已提交)?生成的 SQL 更新查询是什么? 是的,使用 activation of Hibernate SQL dumping 我可以看到 SQL 更新和 JDBC version 字段绑定,这与我在 @987654338 中的 User 中设置的 version 不对应@...我开发了一个FlushInterceptor,它在我的update 函数存在后调用,它执行entityManager.flush() 【参考方案1】:

找到了另一种手动触发乐观锁定的方法。我们可以将缓存的 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();
  

【讨论】:

【参考方案2】:

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

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

JPA 2.0 最终规范,第 3.4.2 节:

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

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

【讨论】:

【参考方案3】:

其实我已经找到了一种方法……但我认为它不是很有效(因为还有 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 值,而不是它自己复制的某处值...

【讨论】:

以上是关于如何使用 Hibernate 4 手动设置 @Version 字段?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JPA 和 Hibernate 设置默认查询超时?

hibernate手动设置的id无效的原因与解决方法

如何设置在 Java JPA/Hibernate 中不是自动生成的 @Id 主键?

Java Spring Hibernate:模型类主键自动递增字段:在字段上设置自动和手动

Hibernate框架学习1

JPA懒加载如何手动加载