-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 
Author Message
 Post subject: NPE when relationship is modified outside a TX
PostPosted: Sat Jan 03, 2009 9:19 pm 
Newbie

Joined: Sun Jul 20, 2008 5:48 pm
Posts: 2
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.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 05, 2009 6:42 am 
Newbie

Joined: Tue Nov 11, 2008 5:36 am
Posts: 5
Location: Stockholm, Sweden
Is there any particular reason why you don't want to run this in a transaction?


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jan 10, 2009 1:39 am 
Newbie

Joined: Sun Jul 20, 2008 5:48 pm
Posts: 2
Isomet wrote:
Is there any particular reason why you don't want to run this in a transaction?

Any database update is, of course, run in a transaction. Per JPA, any manipulation of managed entities performed outside the scope of a transaction gets committed as part of the next transaction on the EntityManager that owns the entities. Hibernate EntityManager gets that right.

The application in which this behavior was discovered has a Swing GUI with a model based directly on JPA entity objects. User manipulation of the GUI results directly in modification of the entities. The entity changes are only recorded in the database, however, when the user clicks a "Save" button. I guess constantly holding a transaction open for the duration of the user's GUI session would work around the problem, but that's poor form and not very good for concurrency. I suppose I could also work around it by detaching the GUI model entities, but then much more code is required to update entity relationships correctly.

The approach I'm trying to use is analogous to what you might see in a web application using transaction-per-request with an application-managed JTA persistence context.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.