JPA EntityManager。为什么使用 persist() 而不是 merge()?

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

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

解决办法

无论哪种方式都会将一个实体添加到PersistenceContext中,区别在于你之后如何处理这个实体。

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

Merge为你的实体创建一个新的实例,从提供的实体中复制状态,并使新的副本得到管理。你传递进来的实例将不会被管理(你所做的任何改变都不会成为事务的一部分--除非你再次调用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。

评论(12)

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

评论(2)

我在我的实体上得到了lazyLoading异常,因为我试图访问一个处于会话中的lazy loaded集合。

我所做的是在一个单独的请求中,从会话中获取实体,然后在我的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!!
评论(0)