JPA 实体管理器合并,对象中没有任何更改

JPA Entity Manager Merge with no changes in the object

我正在尝试使用 Entity Manager 的合并方法在数据库中的对象没有更改时更新对象。假设该行具有以下属性:

id             DECIMAL(11) NOT NULL,
name           CHAR(20) NOT NULL, 
lastModified   TIMESTAMP NOT NULL FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP

假设我在数据库中有一个对象(我们称它为一个人),它具有以下属性:

{id: 1, name: "example", lastModified: 2019-05-22 16:24:54.771}

所以 lastModified 属性是数据库在每次行发生变化时更新的东西。

现在,当我首先获取对象,然后将人名更新为 "example"(与数据库中已有的值相同),然后使用合并更新它时,lastModified属性似乎没有在数据库中更新。即

Person person = em.find(Person.class, 1)
person.setName("example")
em.merge(person)

经过以上操作,lastModified还是2019-05-2216:24:54.771(和之前一样)。现在,如果我更改名称,即

Person person = em.find(Person.class, 1)
person.setName("new name")
em.merge(person)

所以现在 lastModified 也会更新。

这方面有什么帮助吗?有人可以解释一下为什么当没有对对象进行任何更改时该行不更新吗? :) 谢谢!

当您使用 merge 时,Hibernate 首先 运行 在您尝试合并的元组中使用 Select 语句。执行此 select 以获取元组的最新状态,然后 运行 数据库中的更新,以及元组中已更改的数据。

正如您在示例中所说,您没有更改任何数据,因为您正在尝试将名称更新为与之前相同的名称,因此休眠不会 运行 对数据库。

您可以参考 Hibernate 脏检查机制了解更多信息。

如果您真的想在没有数据更改的情况下更新此列,则必须在应用层执行此操作,方法是在保存 Person 对象时手动设置:

person.setLastModified(LocalDateTime.now())

只是对马塞洛回答的补充:

当 "merging" 数据在持久上下文之外时,您应该只使用 merge()。例如,当您从未链接到事务作用域的 REST API 接收到一个 Person 对象时,您希望将它连接到上下文,然后将其保存在 EJB 中。

在您的代码中,删除 em.merge(person) 语句。 Hibernate/JPA 将在您通过代理使用 set() 方法时检测更改,并会在后台生成 SQL 命令。

除了 Marcelo 的回答之外,您还可以使用锁强制更新实体。使用以下内容:

em.lock(person, LockModeType.OPTIMISTIC_FORCE_INCREMENT)