EntityManager.merge()可以插入新对象并更新现有对象。
为什么要使用persist()(它只能创建新对象)?
EntityManager.merge()可以插入新对象并更新现有对象。
为什么要使用persist()(它只能创建新对象)?
当前回答
JPA is indisputably a great simplification in the domain of enterprise applications built on the Java platform. As a developer who had to cope up with the intricacies of the old entity beans in J2EE I see the inclusion of JPA among the Java EE specifications as a big leap forward. However, while delving deeper into the JPA details I find things that are not so easy. In this article I deal with comparison of the EntityManager’s merge and persist methods whose overlapping behavior may cause confusion not only to a newbie. Furthermore I propose a generalization that sees both methods as special cases of a more general method combine.
持久化实体
与merge方法相比,persist方法非常简单直观。使用persist方法最常见的场景可以总结如下:
一个新创建的实体类实例被传递给持久化方法。该方法返回后,将管理实体并计划将其插入到数据库中。它可能发生在事务提交时或提交之前,也可能发生在调用flush方法时。如果实体通过标记为PERSIST级联策略的关系引用另一个实体,则此过程也应用于它。”
规范中有更多的细节,然而,记住它们并不重要,因为这些细节只涵盖了或多或少的奇异情况。
合并实体
In comparison to persist, the description of the merge's behavior is not so simple. There is no main scenario, as it is in the case of persist, and a programmer must remember all scenarios in order to write a correct code. It seems to me that the JPA designers wanted to have some method whose primary concern would be handling detached entities (as the opposite to the persist method that deals with newly created entities primarily.) The merge method's major task is to transfer the state from an unmanaged entity (passed as the argument) to its managed counterpart within the persistence context. This task, however, divides further into several scenarios which worsen the intelligibility of the overall method's behavior.
我没有从JPA规范中重复段落,而是准备了一个流程图,概要地描述了合并方法的行为:
什么时候使用持久化,什么时候使用归并?
坚持
您希望该方法总是创建一个新实体,而从不更新实体。否则,该方法将因违反主键惟一性而引发异常。 批处理流程,以有状态的方式处理实体(参见网关模式)。 性能优化
走
您希望该方法插入或更新数据库中的实体。 您希望以无状态的方式处理实体(服务中的数据传输对象) 您希望插入一个新实体,该实体可能对另一个实体有引用,但可能尚未创建(关系必须标记为MERGE)。例如,插入带有新相册或已有相册引用的新照片。
其他回答
Merge不会更新传递的实体,除非该实体被管理。即使实体ID被设置为一个现有的DB记录,一个新的记录将在数据库中创建。
关于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的人阅读这本书。我很抱歉匿名发布了我的第一个答案。
另一个观察:
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 可能在同花顺或提交时抛出。)
persist(entity)应该与全新的实体一起使用,将它们添加到DB(如果实体已经存在于DB中,则会抛出EntityExistsException)。
应该使用Merge(实体),如果实体被分离并被更改,则将实体放回持久性上下文中。
可能持久是生成INSERT sql语句和合并UPDATE sql语句(但我不确定)。
我在我的实体上得到了lazyLoading异常,因为我试图访问一个会话中的惰性加载集合。
我要做的是在一个单独的请求中,从会话检索实体,然后尝试访问我的jsp页面中的一个集合,这是有问题的。
为了缓解这个问题,我在控制器中更新了相同的实体,并将其传递给我的jsp,尽管我想象当我在会话中重新保存时,它也可以通过SessionScope访问,而不会抛出一个LazyLoadingException,这是示例2的修改:
以下是我行之有效的方法:
// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"
//access e from jsp and it will work dandy!!