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。
坚持和合并是出于两个不同的目的(它们根本不是替代品)。
(编辑扩大差异信息)
坚持:
合并:
坚持()效率:
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