正确使用Session.persist()

我试图理解Session.persist()的语义,以及实体管理器对未保存的瞬态实例的确切作用。 我想要实现的是只是向会话添加一个新的临时实例,并让Hibernate在刷新会话时执行INSERT

我发现如果一个新实例被持久化然后在同一个会话中被修改,那么实体管理器将生成INSERTUPDATE语句,这些语句可能导致约束违规。

举个例子,假设我有一个带有NOT NULL的实体关系Foo和以下服务方法。

 @Transactional void persistFoo(String bar) { Foo foo = new Foo(); session.persist(foo); foo.setBar(bar); } 

尽管我们为bar提供了一个值,但执行此代码将违反数据库中的NULL约束。

 BatchUpdateException: Cannot insert the value NULL into column 'bar' 

Hibernate文档说明如下。

persist()使瞬态实例持久化。 但是,它不保证标识符值将立即分配给持久性实例,分配可能在刷新时发生…

INSERT确实在事务块结束时刷新会话时执行,但它仍然使用来自对象的属性值进行参数化, 就像调用persist()时一样。

我知道这个例子说明的特定问题可以通过一些简单的修改来解决,但我更想知道它应该如何工作。

我的问题是,这种行为只是Session.persist()合同的一部分还是可以改变? 如果是这样,我如何告诉会话推迟生成的INSERT语句的收集参数,直到实际执行它为止?

是的,这是Session.persist()合同的一部分。 根据Hibernate文档 ,这是执行SQL的顺序:

  1. 按插入顺序执行插入
  2. 更新
  3. 删除集合元素
  4. 插入集合元素
  5. 按照执行顺序删除

此命令是官方Hibernate API的一部分,应用程序在操作实体图时依赖它。

Session.persist()之后发生的更改立即置于INSERT语句中会破坏此合同,并在某些用例中导致问题。

假设我们有一个User实体,并且两个用户可能以某种方式相互关联。 然后我们可以在一个事务中插入两个:

 persist(user1); persist(user2); user1.setPartner(user2); user2.setPartner(user1); 

如果所有内容都存储在INSERT语句中,那么在持久化user1时我们会遇到外键约束违规。

通常,通过确保只有传递给persist的状态最终在INSERT ,Hibernate为我们提供了更大的灵活性来满足底层数据库约束。

我不知道任何可以更改此行为的配置。 当然,正如您所提到的,您可以重构代码,以便在设置所有值之后调用persist ,前提是不会违反DB约束。