2

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
Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331
  • This really looks like an EclipseLink bug. I would file them a bug sumbission. – JB Nizet Jul 12 '12 at 10:38
  • 1
    You have marked the ManyToOne with optional=false but are not showing how you are populating the relationship. The problem is likely in the createEmployee method and how it creates the Employee and associates the specialty. Please show this code, and turn EclipseLink logging to finest to help debug the issue. Looking at the create method, I believe you are likely passing in '1' when you should be looking up the Specialty using find or getReference and passing it in instead? Entities should not point to non-managed or detached entities or it corrupts the EntityManager context. – Chris Jul 12 '12 at 15:29
  • Yeap, I had forgotten to use EntityManager::find to obtain the Specialty instance so the Employee entity graph contained a non-managed entity. Still I don't see why: (a) one can observe different Hibernate / EclipseLink behavior (b) why EclipseLink's error message was so cryptic and why (c) it was the second commit() that failed and not the first one. – Marcus Junius Brutus Jul 12 '12 at 19:53

3 Answers3

2

EclipseLink does not allow you to update the PK (Some DBMS allow this if this PK does not has any FK)

there are 2 work around:

  1. Try to delete the old record then insert the new one.
  2. Use surrogate PK instead of the business PK (you still can add unique constrain on the business PK fields), and in this case the dummy surrogate key will not be changed and here is a comparison between both keys
Community
  • 1
  • 1
Samy Omar
  • 800
  • 14
  • 29
0

It may be:

@Entity                                                  
public class Specialty {                                 
@Id                                                  
@Basic(optional = false)                             
@NotNull
@GeneratedValue(strategy = GenerationType.AUTO)                                         
private int id;
... }
Stas Yak
  • 109
  • 1
  • 3
0

try <property name="eclipselink.weaving.internal" value="false"/> as per http://blogs.nologin.es/rickyepoderi/index.php?/archives/95-Weaving-Problem-in-EclipseLink.html.

vnysmnn
  • 31
  • 5