我有一个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是一个无状态会话bean,注入了EntityManager。执行时,控制台打印:
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>
没有抛出任何错误。
答案 0 :(得分:3)
原因是Id
可能会插入/定义 - 正如您在第二个示例中所做的那样,但未更改/更新 - 正如您在第一个示例中所尝试的那样。 JPA提供程序尝试反映数据库中的更改并失败。
JPA 2spec§2.4说
应用程序不得更改主键的值。该 如果发生这种情况,行为是不确定的。
答案 1 :(得分: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。
答案 2 :(得分:1)
在persistence.xml中尝试<property name="eclipselink.weaving.internal" value="false"/>
http://blogs.nologin.es/rickyepoderi/index.php?/archives/95-Weaving-Problem-in-EclipseLink.html
答案 3 :(得分:1)
这个答案迟了4年但无论如何。
您可以使用SQL或 JPQL 或标准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 。 &#34; ID&#34;
另一个例子http://www.thoughts-on-java.org/criteria-updatedelete-easy-way-to/