正确使用Session.persist()
我试图理解Session.persist()
的语义,以及实体管理器对未保存的瞬态实例的确切作用。 我想要实现的是只是向会话添加一个新的临时实例,并让Hibernate在刷新会话时执行INSERT
。
我发现如果一个新实例被持久化然后在同一个会话中被修改,那么实体管理器将生成INSERT
和UPDATE
语句,这些语句可能导致约束违规。
举个例子,假设我有一个带有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的顺序:
- 按插入顺序执行插入
- 更新
- 删除集合元素
- 插入集合元素
- 按照执行顺序删除
此命令是官方Hibernate API的一部分,应用程序在操作实体图时依赖它。
将Session.persist()
之后发生的更改立即置于INSERT
语句中会破坏此合同,并在某些用例中导致问题。
假设我们有一个User
实体,并且两个用户可能以某种方式相互关联。 然后我们可以在一个事务中插入两个:
persist(user1); persist(user2); user1.setPartner(user2); user2.setPartner(user1);
如果所有内容都存储在INSERT
语句中,那么在持久化user1
时我们会遇到外键约束违规。
通常,通过确保只有传递给persist
的状态最终在INSERT
,Hibernate为我们提供了更大的灵活性来满足底层数据库约束。
我不知道任何可以更改此行为的配置。 当然,正如您所提到的,您可以重构代码,以便在设置所有值之后调用persist
,前提是不会违反DB约束。