10

我有一个 JPA 程序,其中 EclipseLink 是持久性提供程序。当我合并用户实体、更改其 ID 并尝试再次合并相同的用户实例时,会引发错误。我重写我的代码以最简单的方式说明我的问题。

User user = userManager.find(1);
userManager.merge(user);
System.out.println("User is managed? "+userManager.contains(user);
user.setId(2);
userManager.merge(user);

上面的代码不在事务上下文中。userManager 是一个注入了 EntityManager 的无状态会话 bean。执行时,控制台打印:

User is managed? false

Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.1.3.v20110304-r9073): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [id] of class [demo.model.User] is mapped to a primary key column in the database. Updates are not allowed.

异常发生在第二次 merge() 调用。

如果我创建一个新用户,设置它的 ID 并合并它,它可以工作:

User user = userManager.find(1);
userManager.merge(user);
System.out.println("User is managed? "+userManager.contains(user);
User newUser = new User();
newUser.setId(2);
userManager.merge(newUser);

那么第一种情况和第二种情况有什么区别呢?根据 JPA 规范,只要实体处于分离状态,合并就应该成功,对吧?(假设存在 ID=2 的实体)

为什么 EclipseLink 提供者似乎对用户实体之前已合并的事实感到困扰?

更新:这似乎是 EclipseLink 的一个错误。我已将持久性提供程序从 EclipseLink 替换为 Hibernate:

我改变

<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

<provider>org.hibernate.ejb.HibernatePersistence</provider>    

没有抛出任何错误。

4

4 回答 4

4

原因是Id可以插入/定义 - 正如您在第二个示例中所做的那样,但没有更改/更新 - 正如您在第一个示例中尝试的那样。JPA 提供程序尝试在数据库中反映更改并失败。

JPA 2 规范 §2.4 说

应用程序不得更改主键的值。如果发生这种情况,则行为未定义。

于 2012-09-11T07:51:12.090 回答
3

这似乎是 EclipseLink 的一个错误。我已将持久性提供程序从 EclipseLink 更改为 Hibernate:

<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

<provider>org.hibernate.ejb.HibernatePersistence</provider>  

没有抛出任何错误。

EclipseLink 的版本是 2.3.2。(随最新的 Glassfish 应用程序服务器 3.1.2 一起提供)。

截至目前,hibernate 的最新版本是 4.1.7。

于 2012-09-13T05:26:52.517 回答
2

<property name="eclipselink.weaving.internal" value="false"/>根据 http://blogs.nologin.es/rickyepoderi/index.php?/archives/95-Weaving-Problem-in-EclipseLink.html在 persistence.xml 中尝试

于 2015-08-17T17:26:04.803 回答
1

这个答案晚了4年,但无论如何。

您可以通过使用 SQL 或JPQLCriteria API执行定期更新查询来更新它。我觉得最后一个是最好的。

这是一个可以解决问题的代码示例。我在类似的情况下尝试过它,它适用于 EclipseLink。

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaUpdate<User> cu = cb.createCriteriaUpdate(User.class);
Root<User> c = cu.from(User.class);
cu.set(User_.id, newId).where(    cb.equal(c.get(User_.id), oldId)    );
em.createQuery(cu).executeUpdate();

而不是User_.id您可以将字段名称作为字符串传递,例如"id"

另一个例子http://www.thoughts-on-java.org/criteria-updatedelete-easy-way-to/

于 2016-09-24T20:40:22.817 回答