正确使用Session.persist()

时间:2015-05-18 15:38:56

标签: java hibernate orm

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

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

举个例子,假设我有一个实体关系 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'

Hibernate documentation声明如下。

  

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

INSERT确实是在事务块结束时刷新会话时执行的,但它仍然使用来自的对象的属性值进行参数化,因为它是 { {1}}被召唤。

我知道这个示例所说明的特定问题可以通过一些简单的更改来修复,但我更有兴趣尝试理解其假设的如何工作。

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

1 个答案:

答案 0 :(得分:1)

是的,这是Session.persist()合同的一部分。根据{{​​3}},这是执行SQL的顺序:

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

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

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

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

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

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

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

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