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的人阅读这本书。我很抱歉匿名发布了我的第一个答案。

其他回答

我注意到,当我使用em.merge时,每个INSERT都有一个SELECT语句,即使JPA没有为我生成字段——主键字段是我自己设置的UUID。我切换到em.persist(myEntityObject),然后只得到INSERT语句。

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

持久化和合并是为了两个不同的目的(它们根本不是替代方案)。

(编辑以扩大差异信息)

坚持:

向数据库中插入一个新的寄存器 将对象附加到实体管理器。

走:

找到具有相同id的附加对象并更新它。 如果存在,则更新并返回已附加的对象。 如果不存在,则将新寄存器插入数据库。

persist()效率:

在向数据库插入新寄存器时,它可能比merge()更有效。 它不会复制原始对象。

persist()的语义:

它确保您是在插入而不是错误地更新。

例子:

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

这种方法对于实体管理器中的任何寄存器只存在一个附加对象。

对于带id的实体,Merge()是这样的:

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

虽然如果连接到MySQL merge()可以像persist()一样有效,使用一个调用INSERT和ON DUPLICATE KEY UPDATE选项,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 可能在同花顺或提交时抛出。)

我在我的实体上得到了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!!