A JTA EntityManager cannot use getTransaction()
Asked Answered
C

4

25

How can I use the following code in my non-ejb application. The code works.

@Override
public void saveItems(Collection<T> items) {
    synchronized (em) {
        EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            for (T item : items) {
                saveItem_((Class<T>) null, item);
            }
            tx.commit();
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }
    }
}

In a new application I'm using EJB3 + JSF and would like to re-use the library containing the code above. My peristence unit for the new application looks like this:

  <persistence-unit name="myApp" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>MySQLConnection</jta-data-source>
  </persistence-unit>

My new application throw an exception when it hits this line:

    EntityTransaction tx = em.getTransaction();

the exception is:

A JTA EntityManager cannot use getTransaction()

Which is clear enough. The question is how would I convert my code to have the transactions managed by the container. Presumably my bean methods need to be annotated appropriately... The question is how?

Chantal answered 9/6, 2012 at 21:29 Comment(0)
H
18

EntityTransaction is used with entity manager of type resource local. If you want to use JTA, then have to use UserTransaction interface.

From Documentation : EntityTransaction - Interface used to control transactions on resource-local entity managers. The EntityManager.getTransaction() method returns the EntityTransaction interface.


Edit: Added pseudo code.

@Resource
private SessionContext sessionContext;

void execute(){

UserTransaction userTxn = sessionContext.getUserTransaction();

try{

 userTxn.begin();
 /**
  *  do-something
  */
 userTxn.commit();

  } catch(Throwable e){
   userTxn.rollback(); //-- Include this in try-catch 
  }
}   
Heres answered 10/6, 2012 at 9:22 Comment(6)
so what is the solution here?Helium
@Helium Why downvoted & haven't I cleared in my post to use UserTransaction interface.Heres
@downvoter If you can't explain & understand, don't downvote without reason.Heres
You should provide a full solution.Antonyantonym
@Emerald214 Relevant code added for clarity, hope this clears the solution provided.Heres
@downvoter Please care to explain. If you think something is missing, add comment, I will try to add few more details. Else you can add your own solution, don't downvote without reason/comment.Heres
T
5

In the simplest case - it just works. If you have your EntityManager injected into EJB and use no special annotations, the transaction will open in the first EJB method entered (this means that if EjbA calls EjbB and that in turn calls EjbC, only one transaction will be used across all the EJB methods). If you want to modify how transactions are controlled, look up @Transaction.

The simplest way to do a rollback is to throw an exception marked with @ApplicationException(rollback=true)

I may be wrong, but judging from your code you should read up on the difference between EXTENDED and NORMAL EntityManager. It looks like you are using an extended em in a very awkward way (moving the loop out of transaction would help you get rid of finally).

Small edit: if you try to use UserTransaction, as the other post suggests, you will get an error, because a standard EntityManager (that you are probably using) uses the so called CMT (Container Managed Transactions). Don't touch it, unless you understand the three basic opositions (if you want, I can elaborate, but frankly, you will NOT need it):

  • container managed EntityManager versus application managed EntityManager,
  • container managed transactions versus application managed transactions,
  • NORMAL EntityManager and EXTENDED EntityManager.
Thurber answered 10/6, 2012 at 8:10 Comment(0)
F
2

just to summarize the code that works for me on Jboss EAP6 and Hibernate 4.2.18.Final.

May save time for someone.

persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
         version="2.0">
<persistence-unit name="myApp" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/MySQLConnection</jta-data-source>
<properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
        <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
        <!--
        <property name="hibernate.show_sql" value="true" />
        -->
    </properties>
</persistence-unit>

java

 import javax.annotation.Resource;
 import javax.persistence.EntityManager;
 import javax.transaction.UserTransaction;

public class MyClass {
@PersistenceContext(unitName = "myApp")
protected EntityManager em;
@Resource
UserTransaction utx;

public void execute(..) throws Exception {
    try {
        utx.begin();
        em.remove(..);
        em.merge(..);
        em.persist(..);
        utx.commit();
    } catch (Exception ex) {
        try {
            utx.rollback();
        } catch (Exception re) {
            throw new RollbackFailureException("An error occurred attempting to roll back the transaction.", re);
        }
        throw ex;
    }
}

}

pom.xml

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>${hibernate.version}</version>
        <scope>provided</scope>
    </dependency>

links:

Application-Managed Entity Managers https://docs.oracle.com/cd/E19798-01/821-1841/bnbra/index.html

How does the UserTransaction and the EntityManager interact?

Faceless answered 28/12, 2018 at 23:0 Comment(0)
P
2

"May save time for someone."

It sure saved me time! I wish I'd found your solution days ago. I've been struggling with how to handle bean-managed transactions in the context of a JTA persistence unit. Most of our use is only one JPA DML call within a bean method. The problem was that after performing a single DML operation, a subsequent call (within the same bean method) to a stored procedure would fail, complaining about not being able to start another transaction within the running transaction. The documentation is thorough but ponderous.

This was key: @Resource UserTransaction utx;

Thank you!

Piggish answered 20/3, 2021 at 7:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.