JPA EntityManager:为什么在merge()上使用persist()?
JPA EntityManager:为什么在merge()上使用persist()?,jpa,merge,entitymanager,persist,java-persistence-api,Jpa,Merge,Entitymanager,Persist,Java Persistence Api,EntityManager.merge()可以插入新对象并更新现有对象 为什么要使用persist()(它只能创建新对象)?任何一种方法都会将一个实体添加到PersistenceContext中,区别在于之后如何处理该实体 Persist获取一个实体实例,将其添加到上下文中,并对该实例进行管理(即跟踪实体的未来更新) Merge返回状态合并到的托管实例。它确实返回PersistenceContext中存在的内容或创建实体的新实例。在任何情况下,它都将从提供的实体复制状态,并返回托管副本。您传入的
EntityManager.merge()
可以插入新对象并更新现有对象
为什么要使用
persist()
(它只能创建新对象)?任何一种方法都会将一个实体添加到PersistenceContext中,区别在于之后如何处理该实体
Persist获取一个实体实例,将其添加到上下文中,并对该实例进行管理(即跟踪实体的未来更新)
Merge返回状态合并到的托管实例。它确实返回PersistenceContext中存在的内容或创建实体的新实例。在任何情况下,它都将从提供的实体复制状态,并返回托管副本。您传入的实例将不会被管理(您所做的任何更改都不会成为事务的一部分,除非您再次调用merge)。尽管您可以使用返回的实例(托管实例)
也许一个代码示例会有所帮助
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。我的实体上出现懒散加载异常,因为我试图访问会话中的懒散加载集合 我要做的是在一个单独的请求中,从会话中检索实体,然后尝试访问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!!
我注意到,当我使用
em.merge
时,对于每个INSERT
,我都会得到一个SELECT
语句,即使JPA没有为我生成字段——主键字段是我自己设置的UUID。我切换到em.persist(myEntityObject)
,然后得到了INSERT
语句。JPA规范对persist()
做了如下说明
如果X是分离的对象,则在持久化时可能会抛出EntityExistsException
调用操作,或者在刷新或提交时抛出EntityExistSexException
或另一个持久异常
因此,当对象不应该是分离对象时,使用persist()
是合适的。您可能更喜欢让代码抛出PersistenceException
,这样它会很快失败
虽然,persist()
可能会为对象设置@GeneratedValue
@Id
merge()
但是必须已经生成了一个具有@Id
的对象。有关merge的更多详细信息,这将帮助您使用merge over persist:
返回原始实体以外的托管实例是合并的关键部分
过程如果持久性上下文中已存在具有相同标识符的实体实例,则
提供程序将用正在合并的实体的状态覆盖其状态,但托管
必须将已存在的版本返回给客户端,以便使用。如果提供者没有
在持久性上下文中更新Employee实例,对该实例的任何引用都将成为
与正在合并的新状态不一致
对新实体调用merge()时,其行为类似于persist()操作。它补充道
将实体添加到持久性上下文中,但不是添加原始实体实例,而是创建新的实体实例
而是复制并管理该实例。由merge()操作创建的副本将被持久化
就好像对它调用了persist()方法一样
存在关系时,merge()操作将尝试更新托管实体
指向分离实体引用的实体的托管版本。如果该实体有
与没有持久标识的对象的关系,合并操作的结果是
未定义。某些提供程序可能允许托管副本指向非持久对象,
而其他人可能会立即抛出异常。merge()操作可以是可选的
在这些情况下级联以防止发生异常。我们将讨论merge()的级联
操作将在本节后面介绍。如果要合并的实体指向删除的实体,则
将引发IllegalArgumentException异常
延迟加载关系是合并操作中的一种特殊情况。如果延迟加载
关系在实体分离之前未在其上触发,该关系将
合并实体时忽略。如果在管理时触发关系,然后在分离实体时将其设置为null,则该实体的管理版本同样会在合并过程中清除该关系。”
以上所有信息均取自“精通Java的Pro JPA 2”™ 持久性API由迈克·基思和梅里克·施尼卡里奥创作。第六章。分段分离合并。这本书实际上是第二本由作者撰写的关于JPA的书。这本新书比前一本有许多新信息。我真的建议那些将认真参与JPA的人读这本书。很抱歉,我无意中发布了我的第一个答案。场景X:
表:Spitter(一个),表: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!!
Spitter Spitter=新Spitter();
Spitle spittle3=新的Spittle();
spitter.setUsername(“乔治”);
设置密码(“test1234”);
setSpittle(“我爱Java2”);
痰盂3.固定式痰盂(痰盂);
dao.addSpittle(spittle3);// 波斯
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!!
{
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);
}
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;
}
//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);
//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);