users@glassfish.java.net

Cannot modify entity and collection within entity in same transaction

From: <glassfish_at_javadesktop.org>
Date: Thu, 08 Nov 2007 17:10:07 PST

I've run across this a few times now, and it is extremely frustrating. Each time I've had to modify the code to perform in some less efficient way to avoid an error. Here's what I try to do:

EntityManager em is looked up via JNDI.
UserTransaction tx is looked up via JNDI.

Thing and SubThing are entities with correct annotations.

thingId is the primary key of the Thing entity.

tx.begin();
Thing t = em.find(Thing.class,thingId);
t.changeField("new value");
t.getSubThings().add(new SubThing(t));
tx.commit();

But on commit, here's the exception:

javax.transaction.RollbackException: Transaction marked for rollback.

No useful information, no reason WHY it's marked for rollback. An alternative way to achieve that useless error message would be:

tx.begin();
Thing t = em.find(Thing.class,thingId);
t.changeField("new value");
em.persist(new SubThing(t);
tx.commit();

What I gather is that for some reason it doesn't like it when the following conditions happen to Entity e within a transaction:

1) e has a field which is modified
2) e has a collection of other entities which gains a new entity (perhaps when one is modified too?)
3) the entity of which there is a collection within e has a reference to e

It's totally fine if you only change the field, or only add the entity to the collection.

Another question, are there known differences when using JPA with hibernate as the provider within Glassfish?

All the tutorials I've read, and the Manning Java Persistence with Hibernate book use the following to get a reference to an EntityManager:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("nameOfPersistenceUnit");
EntityManager em = emf.createEntityManager();

But from the very beginning of my new project (using Hibernate as a JPA provider in Glassfish) that threw an exception every time I tried it. They also all get a hold of a transaction with:

EntityTransaction tx = em.getTransaction();
tx.begin();

But every time I've tried that, I get an exception saying:

java.lang.IllegalStateException: A JTA EntityManager cannot use getTransaction()
        at org.hibernate.ejb.AbstractEntityManagerImpl.getTransaction(AbstractEntityManagerImpl.java:317)
        at com.sun.enterprise.util.EntityManagerWrapper.getTransaction(EntityManagerWrapper.java:565)
        at com.stag.servlet.AdminServlet.doPost(AdminServlet.java:156)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:738)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
        at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:411)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:290)
        at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:271)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:202)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:632)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:577)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:94)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:206)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:632)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:577)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:571)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1080)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:150)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:632)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:577)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:571)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1080)
        at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:270)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:637)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.doProcess(DefaultProcessorTask.java:568)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:813)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:339)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:261)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:212)
        at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:265)
        at com.sun.enterprise.web.connector.grizzly.ssl.SSLWorkerThread.run(SSLWorkerThread.java:106)

The only way I have ever been able to get a hold of an EntityManager or UserTransaction is via:

InitialContext ctx = new InitialContext();
UserTrasaction tx = (UserTransaction) ctx.lookup("UserTransaction");
EntityManager em = (EntityManager) ctx.lookup("java:comp/env/persistence/EntityManager");

I've also been able to get a transaction via container injection in a Servlet, thought I don't use that to get an EntityManager because I've read articles that that is potentially dangerous since servlets can be shared (see http://weblogs.java.net/blog/ss141213/archive/2005/12/dont_use_persis_1.html).

Also, does anybody know the difference between a UserTransaction and an EntityTransaction? Is there a functional difference?
[Message sent by forum member 'rwillie6' (rwillie6)]

http://forums.java.net/jive/thread.jspa?messageID=244663