EntityManager.merge()可以插入新对象并更新现有对象。

为什么要使用persist()(它只能创建新对象)?


当前回答

另一个观察:

merge()只关心自动生成的id(在IDENTITY和SEQUENCE上测试),当表中已经存在这样一个id的记录时。在这种情况下,merge()将尝试更新记录。 然而,如果一个id不存在或者不匹配任何现有的记录,merge()将完全忽略它,并要求db分配一个新的。这有时是许多bug的来源。不要使用merge()强制新记录的id。

另一方面,Persist()甚至不允许将id传递给它。它会立即失效。在我的例子中,它是:

原因:org.hibernate.PersistentObjectException:分离实体 传递到持久化

Hibernate-jpa javadoc有一个提示:

抛出:javax.persistence.EntityExistsException——如果实体 已经存在。(如果实体已经存在,则 当持久化操作时,可能会抛出EntityExistsException 或者EntityExistsException或另一个PersistenceException 可能在同花顺或提交时抛出。)

其他回答

merge和persist之间还有一些区别(我将再次列举已经在这里发布的区别):

D1。Merge不会使传递的实体受管理,而是返回另一个受管理的实例。在另一边持久化将使传递的实体被管理:

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

D2。如果你删除了一个实体,然后决定持久化该实体,你只能使用persist()来做,因为merge会抛出一个IllegalArgumentException。

D3。如果您决定手动处理您的id(例如通过使用uuid),则合并 operation将触发后续的SELECT查询,以查找具有该ID的存在实体,而persist可能不需要这些查询。

D4。有些情况下,您只是不相信调用您的代码的代码,为了确保没有数据被更新,而是被插入,您必须使用持久化。

这两种方法都会将实体添加到PersistenceContext中,区别在于之后如何处理实体。

Persist获取一个实体实例,将其添加到上下文中,并使该实例被管理(即将跟踪实体的未来更新)。

Merge返回与状态合并的托管实例。它确实返回存在于PersistenceContext中的内容或创建实体的新实例。在任何情况下,它都会从提供的实体复制状态,并返回一个托管副本。传入的实例不会被管理(你所做的任何更改都不会成为事务的一部分——除非你再次调用merge)。不过您可以使用返回的实例(托管实例)。

也许一个代码示例会有所帮助。

MyEntity e = new MyEntity();

// scenario 1
// tran starts
em.persist(e); 
e.setSomeField(someValue); 
// tran ends, and the row for someField is updated in the database

// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue); 
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)
      
// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue); 
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)

场景1和3大致相同,但在某些情况下,您可能希望使用场景2。

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

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

可能持久是生成INSERT sql语句和合并UPDATE sql语句(但我不确定)。

我发现Hibernate文档中的解释很有启发性,因为它们包含了一个用例:

The usage and semantics of merge() seems to be confusing for new users. Firstly, as long as you are not trying to use object state loaded in one entity manager in another new entity manager, you should not need to use merge() at all. Some whole applications will never use this method. Usually merge() is used in the following scenario: The application loads an object in the first entity manager the object is passed up to the presentation layer some modifications are made to the object the object is passed back down to the business logic layer the application persists these modifications by calling merge() in a second entity manager Here is the exact semantic of merge(): if there is a managed instance with the same identifier currently associated with the persistence context, copy the state of the given object onto the managed instance if there is no managed instance currently associated with the persistence context, try to load it from the database, or create a new managed instance the managed instance is returned the given instance does not become associated with the persistence context, it remains detached and is usually discarded

来自:http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html

JPA规范说明了关于persist()的以下内容。

如果X是一个分离对象,则在持久化时可能会抛出EntityExistsException 操作被调用,或者EntityExistsException或另一个PersistenceException可能在刷新或提交时抛出。

因此,当对象不应该是分离对象时,使用persist()是合适的。您可能更喜欢让代码抛出PersistenceException,以便快速失败。

尽管规范不清楚,persist()可能会为对象设置@GeneratedValue @Id。但是merge()必须有一个已经生成的@Id对象。