Hibernate (EntityManager) is consistently throwing a NullPointerException for me when I persist and subsequently remove an entity on the "many" side of a one-to-many relationship outside the scope of a transaction. (The NPE happens while committing the next transaction.) The same operations complete successfully if performed in the scope of a transaction. I'm pretty sure JPA allows the sequence of operations that Hibernate fails on (but I am prepared to be proven wrong on that). The complete JUnit test case is too long for the forum (available on request), but here's a bit more detail:
There are two entities, Parent and Child. Property-style access is used. Each entity has an ID of type Long:
Code:
@Id
@GeneratedValue
public Long getId() {
return id;
}
Parent also has a one-to-many relationship with child:
Code:
@OneToMany(mappedBy = "parent", cascade = ALL)
public Collection<Child> getChildren() {
return children;
}
and Child has a many-to-one with Parent:
Code:
@ManyToOne(optional = false)
public Parent getParent() {
return parent;
}
The steps performed are:
1) create and persist a new Parent (and commit)
Code:
EntityManager manager = ...;
Parent parent = new Parent();
manager.getTransaction().begin();
manager.persist(parent);
manager.getTransaction().commit();
2) create a new Child of the parent and persist it:
Code:
Child child = new Child();
child.setParent(parent);
parent.getChildren().add(child);
manager.persist(child); // no active transaction here
3) remove the new child:
Code:
manager.remove(child); // still no transaction
parent.getChildren().remove(child);
4) Perform an entity transaction:
Code:
manager.getTransaction().begin();
// no additional work needed here
manager.getTransaction().commit();
The result is a NullPointerException:
javax.persistence.RollbackException: Error while commiting the transaction
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71)
at demo.PersistRemoveTest.testPersistRemoveBeforeTx(PersistRemoveTest.java:187)
at demo.PersistRemoveTest.runTest(PersistRemoveTest.java:151)
at junit.framework.TestCase.runBare(TestCase.java:130)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:120)
at junit.framework.TestSuite.runTest(TestSuite.java:230)
at junit.framework.TestSuite.run(TestSuite.java:225)
at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.lang.NullPointerException
at org.hibernate.action.EntityAction.getId(EntityAction.java:86)
at org.hibernate.action.EntityDeleteAction.postDelete(EntityDeleteAction.java:142)
at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:117)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:172)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54)
... 15 more
I have tried a number of variations on this and found the following:
a) There is only a problem when the entities involved have a mapped relationship. Everything works as expected when there is no relationship.
b) There is no problem if the persist() and remove() of the child are performed while an entity transaction is active. (That is, at the point marked "no additional work needed here" above, instead of earlier.)
c) It makes no difference whether EntityManager.persist() or EntityManager.merge() is used to obtain persistent Parent and / or Child entities.
d) This behavior is seen both in Hibernate Core 3.2.6.GA / Hibernate EntityManager 3.3.2.GA, and in Hibernate Core 3.3.1.GA / Hibernate EntityManager 3.4.0.GA.
Additionally:
DBMS: MySQL 5.0.51
Java: Sun Java SE 5.0
OS: WinXP SP3
This sure looks like a bug to me; am I missing something? Can anyone suggest a work-around?
Thanks.