eclipselink
eclipselink copied to clipboard
ClassCastException updating foreignkeys with identity generation
We are seeing a ClassCastException from EclipseLink 4.0.0. Setting <property name="eclipselink.target-database-properties" value="supportsReturnGeneratedKeys=false" /> resolves the issue. It appears to be a bug in the GeneratedKeys support https://github.com/eclipse-ee4j/eclipselink/issues/1480 I added.
Entity:
@Entity
public class StepExecutionEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@ManyToOne
@JoinColumn(name = "FK_ID")
private StepExecutionEntity childEntity;
Test:
EntityManager em = emf.createEntityManager();
try {
StepExecutionEntity e1 = new StepExecutionEntity();
e1.setChildEntity(e1);
em.getTransaction().begin();
em.persist(e1);
em.getTransaction().commit();
Exception:
Caused by: java.lang.ClassCastException: org.eclipse.persistence.queries.SQLCall incompatible with java.lang.Integer
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.updateObject(DatasourceCallQueryMechanism.java:905)
at org.eclipse.persistence.internal.queries.StatementQueryMechanism.updateObject(StatementQueryMechanism.java:462)
at org.eclipse.persistence.internal.queries.CallQueryMechanism.updateForeignKeyFieldAfterInsert(CallQueryMechanism.java:435)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.updateForeignKeyFieldAfterInsert(DatabaseQueryMechanism.java:795)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:471)
at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:86)
at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:97)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:326)
at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:61)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:913)
at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:812)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:109)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:86)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:3008)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1841)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1823)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1773)
at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:248)
at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:147)
at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4335)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1507)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1597)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:287)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1234)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:137)
In this test, EclipseLink is creating an InsertObjectQuery for the new Object. However, EclipseLink also recognizes that a FK update will be needed AND because the FK Object is also new (it's the same object, which is important here) then the code enters this method:
https://github.com/eclipse-ee4j/eclipselink/blob/fd9e4ecbee675d15ac3b0a5081189e4f88cfa0e9/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/queries/CallQueryMechanism.java#L416-L437
So, within the execution of the INSERT, EclipseLink creates a new SQLCall for the UPDATE. The issue is that the Query is STILL for the InsertObjectQuery... that means this is going to fail:
https://github.com/eclipse-ee4j/eclipselink/blob/fd9e4ecbee675d15ac3b0a5081189e4f88cfa0e9/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/queries/CallQueryMechanism.java#L150-L167
The new SQLCall is for an UPDATE query, but this.query still refers to the InsertObjectQuery. So, shouldReturnGeneratedKeys is copied from the InsertObjectQuery to the new UPDATE SQLCall... that isn't allowed and is the reason for the failure