Hibernate中的persist()和merge()有什么区别?

Hibernate中的persist()和merge()有什么区别?

persist()可以创build一个UPDATE&INSERT查询,例如:

 SessionFactory sef = cfg.buildSessionFactory(); Session session = sef.openSession(); A a=new A(); session.persist(a); a.setName("Mario"); session.flush(); 

在这种情况下查询将会像这样产生:

 Hibernate: insert into A (NAME, ID) values (?, ?) Hibernate: update A set NAME=? where ID=? 

所以persist()方法可以生成一个Insert和一个Update。

现在用merge()

 SessionFactory sef = cfg.buildSessionFactory(); Session session = sef.openSession(); Singer singer = new Singer(); singer.setName("Luciano Pavarotti"); session.merge(singer); session.flush(); 

这是我在数据库中看到的:

 SINGER_ID SINGER_NAME 1 Ricky Martin 2 Madonna 3 Elvis Presley 4 Luciano Pavarotti 

现在使用merge()更新logging

 SessionFactory sef = cfg.buildSessionFactory(); Session session = sef.openSession(); Singer singer = new Singer(); singer.setId(2); singer.setName("Luciano Pavarotti"); session.merge(singer); session.flush(); 

这是我在数据库中看到的:

 SINGER_ID SINGER_NAME 1 Ricky Martin 2 Luciano Pavarotti 3 Elvis Presley 

JPA规范包含这些操作的语义的非常精确的描述,比在javadoc中更好:

应用于实体X的持久化操作的语义如下:

  • 如果X是一个新的实体,它就变成了pipe理。 实体X将在事务提交之前或之前进入数据库,或作为刷新操作的结果。

  • 如果X是一个先前存在的pipe理实体,则持久操作将忽略它。 但是,如果从X到这些其他实体的关系使用cascade=PERSISTcascade=ALL注释元素值进行注释,或者使用等效XML描述符元素进行了指定,则持久操作将级联到由X引用的实体。

  • 如果X是一个被删除的实体,它将被pipe理。

  • 如果X是分离的对象,则在调用persist操作时可能会引发EntityExistsException或者在刷新或提交时抛出EntityExistsException或其他PersistenceException

  • 对于由X的关系引用的所有实体Y,如果与Y的关系已用级联元素值cascade=PERSISTcascade=ALL进行注释,则将持久化操作应用于Y.


适用于实体X的合并操作的语义如下:

  • 如果X是分离的实体,则将X的状态复制到具有相同标识的预先存在的被pipe理实体X'或创buildX的新被pipe理副本X'。

  • 如果X是新的实体实例,则创build新的托pipe实体实例X',并将X的状态复制到新的托pipe实体实例X'中。

  • 如果X是已删除的实体实例,则合并操作将抛出IllegalArgumentExceptionexception(否则事务提交将失败)。

  • 如果X是托pipe实体,则合并操作会忽略它,但是,如果这些关系已使用级联元素值cascade=MERGEcascade=ALL注释进行注释,则合并操作将级联到由X关系引用的实体。

  • 对于由具有级联元素值cascade=MERGEcascade=ALL X的关系引用的所有实体Y,Y被recursion地合并为Y'。 对于由X引用的所有这样的Y,将X'设置为引用Y'。 (注意,如果X被pipe理,那么X与X'是同一个对象。)

  • 如果X是合并到X'的实体,并且引用了另一个实体Y,那么cascade=MERGEcascade=ALL没有被指定,那么从X'导航同一个关联产生一个被pipe理对象Y'的引用。与Y相同的持久性标识。

这是来自JPA。 以一种非常简单的方式:

persist(entity)应该与全新实体一起使用,将它们添加到数据库中(如果实体已经存在于数据库中,则会出现EntityExistsException throw)。

应该使用合并(实体),如果实体被分离并被改变,则将实体放回持久性上下文。

坚持只应该被称为新的实体,而合并则意味着重新分离的实体。

如果使用分配的生成器,则使用merge而不是persist可能会导致冗余的SQL语句 ,从而影响性能。

此外, 调用托pipe实体的合并也是一个错误,因为托pipe实体是由Hibernate自动pipe理的,并且在刷新持久性上下文时,它们的状态通过脏检查机制与数据库logging同步。