I have a very simple console-based (not application-server-deployed) JPA program which I test against both EclipseLink and Hibernate providers. All the code is JPA so I figured I'd be able to switch providers without a problem. However the program succeeds when using Hibernate as a provider yet fails in EclipseLink.
The key segment is this (I am behind firewall so I can't see the formatting due to "SO requires JavaScript from another domain..." so apologies if not properly formatted - I'll do the formatting from home):
em.getTransaction().begin();
Employee emp = service.createEmployee(id, "John Doe", 45000,1);
em.getTransaction().commit();
System.out.println("Persisted "+emp);
em.getTransaction().begin();
em.getTransaction().commit();
I used to do some stuff inside the second empty transaction, but in narrowing down the problem I noticed that it fails even if I just begin() and commit() immediately without doing anything. The intriguing part is that EclipseLink (see stack-trace at bottom of post) seems to complain about the id attribute of entity Specialty being updated although it is not.
The Employee entity has a foreign key to a Specialty entity (each Employee has a Specialty), i.e. a Many-To-One relationship from the Employee:
@Entity
public class Employee {
@Id
@Basic(optional = false)
@NotNull
private int id;
private String name;
private long salary;
@JoinColumn(name = "spc_id", referencedColumnName = "id")
@ManyToOne(optional = false)
private Specialty spcId;
...}
and the Specialty class is defined as:
@Entity
public class Specialty {
@Id
@Basic(optional = false)
@NotNull
private int id;
... }
When using Hibernate as JPA provider the code executes smoothly. When using EclipseLink it fails with the stack trace found at the bottom of this post.
UPDATE: Upon further experimentation I discovered that the following code works with both providers:
em.getTransaction().begin();
Employee emp = service.createEmployee(id, "John Doe", 45000, 1);
em.getTransaction().commit();
System.out.println("Persisted " + emp);
EntityManager em2 = emf.createEntityManager();
em2.getTransaction().begin();
em2.getTransaction().commit();
em2.close();
That still doesn't explain why EclipseLink fails when trying to begin() / commit() a transaction on the same entity manager object (em - see code at beginning of post), and moreover why it fails with such a cryptic message (complaining about the primary key of class Specialty being updated).
STACK TRACE:
[java] [EL Info]: 2012-07-12 11:21:09.453--ServerSession(23846608)--EclipseLink, version: Eclipse Persistence Services - 2.4.0.v20120608-r11652
[java] [EL Info]: connection: 2012-07-12 11:21:09.59--ServerSession(23846608)--file:/home/mperdikeas/playground/jpa/01-employee-job/prj/use/build/employeetest.jar_EmployeeService login successful
[java] Exception in thread "main" javax.persistence.RollbackException: Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.ValidationException
[java] Exception Description: The attribute [id] of class [mp.Specialty] is mapped to a primary key column in the database. Updates are not allowed.
[java] at org.eclipse.persistence.internal.Persisted mp.Employee@ea70f2
[java] [EL Warning]: 2012-07-12 11:21:09.735--UnitOfWork(32118258)--Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.ValidationException
[java] Exception Description: The attribute [id] of class [mp.Specialty] is mapped to a primary key column in the database. Updates are not allowed.
[java] [EL Info]: connection: 2012-07-12 11:21:09.738--ServerSession(23846608)--file:/home/mperdikeas/playground/jpa/01-employee-job/prj/use/buildjpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)
[java] at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)
[java] at mp.EmployeeTest.main(EmployeeTest.java:29)
[java] Caused by: Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.ValidationException
[java] Exception Description: The attribute [id] of class [mp.Specialty] is mapped to a primary key column in the database. Updates are not allowed.
[java] at org.eclipse.persistence.exceptions.ValidationException.primaryKeyUpdateDisallowed(ValidationException.java:2458)
[java] at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.writeFromObjectIntoRowWithChangeRecord(AbstractDirectMapping.java:1214)
[java] at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildRowForUpdateWithChangeSet(ObjectBuilder.java:1378)
[java] at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.updateObjectForWriteWithChangeSet(DatabaseQueryMechanism.java:1003)
[java] at org.eclipse.persistence.queries.UpdateObjectQuery.executeCommitWithChangeSet(UpdateObjectQuery.java:84)
[java] at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:286)
[java] at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
[java] at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:852)
[java] at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:751)
[java] at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
[java] at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
[java] at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2875)
[java] at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1602)
[java] at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1584)
[java] at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1535)
[java] at org.eclipse.persistence.internal.sessions.CommitManager.commitChangedObjectsForClassWithChangeSet(CommitManager.java:265)
[java] at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:128)
[java] at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:3914)
[java] at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1419)
[java] at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:634)
[java] at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1509)
[java] at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:266)
[java] at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1147)
[java] at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:84)
[java] ... 2 more
[java] /employeetest.jar_EmployeeService logout successful