问题
I have a JPA program where EclipseLink is the Persistence provider. When I merge an user entity, change its ID, and try to merge the same user instance again, an error is thrown. I rewrite my code to illustrate my problem in the simplest way.
User user = userManager.find(1);
userManager.merge(user);
System.out.println("User is managed? "+userManager.contains(user);
user.setId(2);
userManager.merge(user);
The above code is not in a transaction context. userManager is a stateless session bean with an EntityManager injected. When executed, the console prints:
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.
The exception occurs at the second merge() invocation.
If I create a new user, sets its ID and merge it, it works:
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);
So what is the difference between the first scenario and second one? According to the JPA specification, as long as the entity is in detached state, the merge should succeed, right? (Assuming the entity with ID=2 exists)
Why the EclipseLink provider seems to be bothered by the fact that the user entity has been merged before?
Update: It seems to be an bug of EclipseLink. I have replaced the persistence provider from EclipseLink to Hibernate:
I change
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
to
<provider>org.hibernate.ejb.HibernatePersistence</provider>
No error has been thrown.
回答1:
The reason is that the Id
may be inserted/defined - as you do in your second example, but not changed/updated - as you try in your first example. The JPA provider tries to reflect the change in the database and fails.
JPA 2 spec §2.4 says
The application must not change the value of the primary key. The behavior is undefined if this occurs.
回答2:
It seems to be an bug of EclipseLink. I have changed the persistence provider from EclipseLink to Hibernate:
from
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
to
<provider>org.hibernate.ejb.HibernatePersistence</provider>
No error has been thrown.
The version of EclipseLink is 2.3.2. (which is shipped with the latest Glassfish application server 3.1.2).
The version of hibernate is, as of now the latest, 4.1.7.
回答3:
Try <property name="eclipselink.weaving.internal" value="false"/>
in persistence.xml as per
http://blogs.nologin.es/rickyepoderi/index.php?/archives/95-Weaving-Problem-in-EclipseLink.html
回答4:
This answer is 4 years late but anyway.
You can update it by executing regular update queries using SQL or JPQL or Criteria API. I find the last one is the best.
Here is a code example that can do the trick. I have tried it in a similar situation and it works fine with 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();
Instead of User_.id you can pass the name of the field as a String e.g. "id".
Another example http://www.thoughts-on-java.org/criteria-updatedelete-easy-way-to/
来源:https://stackoverflow.com/questions/12364051/merge-an-entity-change-its-id-merge-again-cause-mapped-to-a-primary-key-colu