JPA EntityManager:为什么在merge()上使用persist()?

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

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


无论哪种方式都会将实体添加到PersistenceContext中,不同之处在于您之后对实体所做的操作。

Persist需要一个实体实例,将其添加到上下文中并使该实例得到管理(即将对实体的未来更新进行跟踪)。

合并创建实体的新实例,从提供的实体复制状态,并管理新副本。 您传入的实例将不会被管理(您所做的任何更改都不会成为交易的一部分 - 除非您再次调用合并)。

也许一个代码示例会有所帮助。

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。


坚持和合并是出于两个不同的目的(它们根本不是替代品)。

(编辑扩大差异信息)

坚持:

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

  • 找到具有相同ID的附加对象并更新它。
  • 如果存在,则更新并返回已连接的对象。
  • 如果不存在,则将新的寄存器插入数据库。
  • 坚持()效率:

  • 将一个新的寄存器插入数据库比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);
    }
    

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

    合并()为一个实体的id是这样的:

    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()可以像使用ON DUPLICATE KEY UPDATE选项对INSERT的调用一样有效,persist(),JPA是一个非常高级的编程,并且您不能认为这将会到处是这种情况。


    如果您使用的是分配的生成器,则使用合并而不是持久可能会导致冗余SQL语句,从而影响性能。

    此外,调用托管实体的合并也是一个错误,因为托管实体由Hibernate自动管理,并且在刷新持久性上下文时,它们的状态通过脏检查机制与数据库记录同步。

    为了理解所有这些如何工作,您应该首先知道Hibernate将开发人员的思维从SQL语句转换为实体状态转换。

    一旦实体由Hibernate主动管理,所有更改将自动传播到数据库。

    Hibernate监视当前连接的实体。 但是对于一个实体进行管理,它必须处于正确的实体状态。

    首先,我们必须定义所有的实体状态:

  • 新(瞬态)

    新创建的对象尚未与Hibernate Session (又名Persistence Context )相关联,并且未映射到任何数据库表行被认为处于新建(瞬态)状态。

    为了持久化,我们需要显式调用EntityManager#persist方法或使用传递持久性机制。

  • 持久性(管理)

    持久实体已与数据库表格行相关联,并由当前正在运行的持久性上下文进行管理。 对此类实体所做的任何更改都将被检测到并传播到数据库(在会话刷新期间)。 借助Hibernate,我们不再需要执行INSERT / UPDATE / DELETE语句。 Hibernate采用事务性后写式工作风格,并且在当前Session刷新时间的最后一个负责时刻同步更改。

  • 超脱

    一旦当前正在运行的持久性上下文关闭,所有先前管理的实体就会分离。 将不再跟踪连续更改,并且不会发生自动数据库同步。

    要将分离的实体关联到活动的Hibernate会话,可以选择以下选项之一:

  • 重新连接

    Hibernate(但不是JPA 2.1)支持通过Session#更新方法重新连接。 Hibernate Session只能将一个Entity对象关联到给定的数据库行。 这是因为持久化上下文用作内存中缓存(第一级缓存),并且只有一个值(实体)与给定键(实体类型和数据库标识符)关联。 只有当没有其他JVM对象(匹配相同的数据库行)已经与当前的Hibernate Session关联时才能重新附加实体。

  • 合并

  • 合并将将分离的实体状态(源)复制到托管实体实例(目标)。 如果合并实体在当前会话中没有等价物,则将从数据库中提取一个合并实体。 即使在合并操作之后,分离的对象实例仍将保持分离状态。

  • 删除

    尽管JPA要求仅允许删除托管实体,但Hibernate也可以删除分离的实体(但只能通过Session#delete方法调用)。 已删除的实体仅安排删除,而实际的数据库DELETE语句将在会话刷新时执行。

  • 为了更好地理解JPA状态转换,可以看到下图:

    在这里输入图像描述

    或者如果您使用Hibernate特定的API:

    在这里输入图像描述

    链接地址: http://www.djcxy.com/p/37001.html

    上一篇: JPA EntityManager: Why use persist() over merge()?

    下一篇: Hibernate generating SQL queries when accessing associated entity's id