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

其他回答

场景X:

表:spititter(1),表:Spittles(许多)(Spittles是与FK:spitter_id关系的所有者)

这个场景的结果是节省:喷壶和两个喷壶就像被同一个喷壶拥有一样。

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

场景Y:

这将拯救喷壶,将拯救2喷壶,但他们不会引用同一喷壶!

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

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

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

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

Merge不会更新传递的实体,除非该实体被管理。即使实体ID被设置为一个现有的DB记录,一个新的记录将在数据库中创建。

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)。例如,插入带有新相册或已有相册引用的新照片。

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

(编辑以扩大差异信息)

坚持:

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

走:

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