我试图理解Session.persist()
的语义,以及实体管理器对未保存的瞬态实例的确切理解。我想要实现的只是在会话中添加一个新的瞬态实例,并在刷新会话时让Hibernate执行INSERT
。
我发现如果新实例被持久化然后在同一会话中修改,实体管理器将生成INSERT
和UPDATE
语句,这可能导致约束违规。
举个例子,假设我有一个实体关系 Foo ,其中NOT NULL
列 bar 以及以下服务方法。
@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'
persist()使瞬态实例持久化。但是,它不保证标识符值将立即分配给持久性实例,分配可能在刷新时发生...
INSERT
确实是在事务块结束时刷新会话时执行的,但它仍然使用来自的对象的属性值进行参数化,因为它是 { {1}}被召唤。
我知道这个示例所说明的特定问题可以通过一些简单的更改来修复,但我更有兴趣尝试理解其假设的如何工作。
我的问题是,这种行为只是persist()
合同的一部分还是可以改变?如果是这样,我如何告诉会话推迟生成的Session.persist()
语句的收集参数,直到实际执行它为止?
答案 0 :(得分:1)
是的,这是Session.persist()
合同的一部分。根据{{3}},这是执行SQL的顺序:
- 按照执行顺序插入
- 更新
- 删除集合元素
- 插入集合元素
- 按照执行顺序删除
醇>
此命令是官方Hibernate API的一部分,应用程序在操作实体图时依赖它。
将Session.persist()
之后发生的更改立即发送到INSERT
语句会破坏此合同,并在某些用例中导致问题。
假设我们有一个User
实体,并且两个用户可能以某种方式相互关联。然后我们可以在一个事务中插入两个:
persist(user1);
persist(user2);
user1.setPartner(user2);
user2.setPartner(user1);
如果所有内容都存储在INSERT
语句中,那么在持久化user1
时我们会遇到外键约束违规。
通常,通过确保只传递给persist
的状态最终在INSERT
中,Hibernate为我们提供了更大的灵活性来满足基础数据库约束。
我不知道任何可以更改此行为的配置。当然,正如您所提到的,您可以重构代码,以便在设置所有值之后调用persist
,前提是不会违反DB约束。