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

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


当前回答

persist(entity)应该与全新的实体一起使用,将它们添加到DB(如果实体已经存在于DB中,则会抛出EntityExistsException)。

应该使用Merge(实体),如果实体被分离并被更改,则将实体放回持久性上下文中。

可能持久是生成INSERT sql语句和合并UPDATE sql语句(但我不确定)。

其他回答

这两种方法都会将实体添加到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。

我在我的实体上得到了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!!

persist(entity)应该与全新的实体一起使用,将它们添加到DB(如果实体已经存在于DB中,则会抛出EntityExistsException)。

应该使用Merge(实体),如果实体被分离并被更改,则将实体放回持久性上下文中。

可能持久是生成INSERT sql语句和合并UPDATE sql语句(但我不确定)。

通过回答,有一些关于“Cascade”和id生成的细节缺失。看到问题

此外,值得一提的是,您可以为合并和持久化使用单独的Cascade注释:Cascade。合并和级联。PERSIST将根据使用的方法进行处理。

规格是你的朋友;)

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