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

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


当前回答

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

(编辑以扩大差异信息)

坚持:

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

走:

找到具有相同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是一个非常高级的编程,你不能假设这将是任何地方的情况。

其他回答

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

另一个观察:

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!!

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

(编辑以扩大差异信息)

坚持:

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

走:

找到具有相同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是一个非常高级的编程,你不能假设这将是任何地方的情况。

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

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

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()替换旧的内容。