Liebes Lieblingsforum,
ich arbeite mich gerade in JPA (2.0) ein. Abbilden möchte ich Artikel (Item) mit diversen Attributen. Manche Artikel haben eine komplexe Herstellungsanleitung (Recipe).
Mein Schema würde ich ungefähr so gestalten:
Wenn ich das in JPA mappe, dann sieht das vereinfacht so aus (setter und getter mit dazu denken):
Ich spiele mit EclipseLink 2.0.1.v20100213-r6600 gegen eine Derby-DB 10.5.3.0 (in einem GlassFish v3).
Jetzt setze ich einen Unique Index (alternativ ein Unique Constraint; verhält sich gleich) auf [c]Recipe.parentItem[/c]. Fall 1 und Fall 2 verhalten sich genauso. Fall 3 aber führt zu einem sehr hässlichen Fehler:
Ohne Unique Index sieht das Log so aus:
Ich mutmaße daher, es handelt sich hier um zwei Fehler. 1. EclipseLink müsste eigentlich zuerst DELETE und danach INSERT ausführen. 2. Die DerbyDB verhält sich seltsam und zeigt eine NullPointerException, anstatt ordnungsgemäß über eine Constraint-Violation zu schimpfen. Hab ich da recht? Kennt das jemand? Mache ich beim Mapping etwas falsch? Ist das ein Bug im EclipseLink? Oder in der DerbyDB? Oder im JDBC-Treiber? Hab ich [c]@OneToOne.orphanRemove[/c] falsch verstanden? Hilfe!
Ich danke schonmal für Hinweise…
Ebenius
ich arbeite mich gerade in JPA (2.0) ein. Abbilden möchte ich Artikel (Item) mit diversen Attributen. Manche Artikel haben eine komplexe Herstellungsanleitung (Recipe).
Mein Schema würde ich ungefähr so gestalten:
Code:
Item Recipe
=================== ===================
id : NUMBER, PK id : NUMBER, PK
// and more parentItem : NUMBER, (*** erstmal ohne UNIQUE INDEX ***)
// and more
Wenn ich das in JPA mappe, dann sieht das vereinfacht so aus (setter und getter mit dazu denken):
Java:
@Entity
class Item {
@Id Long id;
@OneToOne(orphanRemoval = true, cascade = ALL, mappedBy = "parentItem")
Recipe recipe;
}
Java:
@Entity
class Recipe {
@Id Long id;
@OneToOne(optional = false) Item parentItem;
}
- Lade ich nun eine Instanz von [c]Item[/c] die kein Recipe hat, füge ein Recipe hinzu und mache dann das Item wieder persistent, wird das Recipe in der Datenbank korrekt angelegt.
- Lade ich eine Instanz von [c]Item[/c] die ein Recipe hat, setze das Recipe auf [c]null[/c] und mache dann das Item persistent, wird das Recipe korrekt aus der Datenbank gelöscht.
- Lade ich eine Instanz von [c]Item[/c] die ein Recipe hat, ersetze das Recipe durch eine neue Instanz und mache dann das Item persistent, wird das alte Recipe gelöscht und das neue hinzugefügt.
Jetzt setze ich einen Unique Index (alternativ ein Unique Constraint; verhält sich gleich) auf [c]Recipe.parentItem[/c]. Fall 1 und Fall 2 verhalten sich genauso. Fall 3 aber führt zu einem sehr hässlichen Fehler:
Code:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: DERBY SQL error: SQLCODE: -1, SQLSTATE: XJ001, SQLERRMC: java.lang.NullPointerException^T^TXJ001.U
Error Code: -1
Call: INSERT INTO RECIPE (parentItem) VALUES (?)
bind => [2]
Query: InsertObjectQuery([Entity Recipe] id=null, parentItem=4711)
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:333)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.processExceptionForCommError(DatabaseAccessor.java:1422)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:799)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:867)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:587)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:530)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeCall(AbstractSession.java:914)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:205)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:191)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:334)
at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:162)
at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:177)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:461)
at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80)
at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:286)
at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:675)
at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:589)
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:2857)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1225)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1207)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1167)
at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:197)
at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsForClassWithChangeSet(CommitManager.java:164)
at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:116)
at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:3260)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1403)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:547)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkImpl.java:1549)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.writeChanges(RepeatableWriteUnitOfWork.java:360)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:696)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.flush(EntityManagerWrapper.java:407)
...
Caused by: java.sql.SQLException: DERBY SQL error: SQLCODE: -1, SQLSTATE: XJ001, SQLERRMC: java.lang.NullPointerException^T^TXJ001.U
at org.apache.derby.client.am.SQLExceptionFactory40.getSQLException(Unknown Source)
at org.apache.derby.client.am.SqlException.getSQLException(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.executeUpdate(Unknown Source)
at com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:108)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:792)
... 121 more
Caused by: org.apache.derby.client.am.SqlException: DERBY SQL error: SQLCODE: -1, SQLSTATE: XJ001, SQLERRMC: java.lang.NullPointerException^T^TXJ001.U
at org.apache.derby.client.am.Statement.completeExecute(Unknown Source)
at org.apache.derby.client.net.NetStatementReply.parseEXCSQLSTTreply(Unknown Source)
at org.apache.derby.client.net.NetStatementReply.readExecute(Unknown Source)
at org.apache.derby.client.net.StatementReply.readExecute(Unknown Source)
at org.apache.derby.client.net.NetPreparedStatement.readExecute_(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.readExecute(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.flowExecute(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.executeUpdateX(Unknown Source)
... 124 more
Ohne Unique Index sieht das Log so aus:
Code:
INSERT INTO RECIPE (parentItem) VALUES (?)
bind => [2]
DELETE FROM RECIPE WHERE (id = ?)
bind => [1]
Ich danke schonmal für Hinweise…
Ebenius
Zuletzt bearbeitet: