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

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


当前回答

关于merge的更多细节将帮助你使用merge over persist:

Returning a managed instance other than the original entity is a critical part of the merge process. If an entity instance with the same identifier already exists in the persistence context, the provider will overwrite its state with the state of the entity that is being merged, but the managed version that existed already must be returned to the client so that it can be used. If the provider did not update the Employee instance in the persistence context, any references to that instance will become inconsistent with the new state being merged in. When merge() is invoked on a new entity, it behaves similarly to the persist() operation. It adds the entity to the persistence context, but instead of adding the original entity instance, it creates a new copy and manages that instance instead. The copy that is created by the merge() operation is persisted as if the persist() method were invoked on it. In the presence of relationships, the merge() operation will attempt to update the managed entity to point to managed versions of the entities referenced by the detached entity. If the entity has a relationship to an object that has no persistent identity, the outcome of the merge operation is undefined. Some providers might allow the managed copy to point to the non-persistent object, whereas others might throw an exception immediately. The merge() operation can be optionally cascaded in these cases to prevent an exception from occurring. We will cover cascading of the merge() operation later in this section. If an entity being merged points to a removed entity, an IllegalArgumentException exception will be thrown. Lazy-loading relationships are a special case in the merge operation. If a lazy-loading relationship was not triggered on an entity before it became detached, that relationship will be ignored when the entity is merged. If the relationship was triggered while managed and then set to null while the entity was detached, the managed version of the entity will likewise have the relationship cleared during the merge."

以上所有信息都摘自Mike Keith和Merrick Schnicariol的“Pro JPA 2掌握Java™Persistence API”。第六章。分段分离与合并。这本书实际上是作者专门介绍JPA的第二本书。这本新书比前一本有许多新信息。我非常推荐那些将认真参与JPA的人阅读这本书。我很抱歉匿名发布了我的第一个答案。

其他回答

您可能来这里寻求关于何时使用持久化和何时使用合并的建议。我认为这取决于具体情况:您需要创建新记录的可能性有多大,以及检索持久数据的难度有多大。

让我们假设您可以使用自然键/标识符。

Data needs to be persisted, but once in a while a record exists and an update is called for. In this case you could try a persist and if it throws an EntityExistsException, you look it up and combine the data: try { entityManager.persist(entity) } catch(EntityExistsException exception) { /* retrieve and merge */ } Persisted data needs to be updated, but once in a while there is no record for the data yet. In this case you look it up, and do a persist if the entity is missing: entity = entityManager.find(key); if (entity == null) { entityManager.persist(entity); } else { /* merge */ }

如果你没有自然的键/标识符,你将很难弄清楚实体是否存在,或者如何查找它。

合并也可以用两种方式处理:

如果更改通常很小,请将其应用到托管实体。 如果更改很常见,则从持久化实体以及未更改的数据复制ID。然后调用EntityManager::merge()替换旧的内容。

通过回答,有一些关于“Cascade”和id生成的细节缺失。看到问题

此外,值得一提的是,您可以为合并和持久化使用单独的Cascade注释:Cascade。合并和级联。PERSIST将根据使用的方法进行处理。

规格是你的朋友;)

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

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

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

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

这两种方法都会将实体添加到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语句(但我不确定)。