java - Un JTA EntityManager no puede usar getTransaction()
persistence ejb-3.0 (3)
¿Cómo tengo el siguiente código en mi aplicación no-ejb? El código funciona.
@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();
}
}
}
}
En una nueva aplicación, estoy usando EJB3 + JSF y me gustaría reutilizar la biblioteca que contiene el código anterior. Mi unidad de peristence para la nueva aplicación se ve así:
<persistence-unit name="myApp" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>MySQLConnection</jta-data-source>
</persistence-unit>
Mi nueva aplicación lanza una excepción cuando llega a esta línea:
EntityTransaction tx = em.getTransaction();
la excepción es:
A JTA EntityManager cannot use getTransaction()
Que es lo suficientemente claro. La pregunta es cómo convertiría mi código para que las transacciones sean administradas por el contenedor. Presumiblemente, mis métodos de frijol deben ser anotados apropiadamente ... La pregunta es ¿cómo?
En el caso más simple, simplemente funciona. Si tiene su EntityManager inyectado en EJB y no usa anotaciones especiales, la transacción se abrirá en el primer método EJB ingresado (esto significa que si EjbA llama a EjbB y que a su vez llama a EjbC, solo se utilizará una transacción en todos los métodos EJB ). Si desea modificar la forma en que se controlan las transacciones, busque @Transaction.
La forma más sencilla de hacer una reversión es lanzar una excepción marcada con @ApplicationException (rollback = true)
Puede que esté equivocado, pero a juzgar por su código, debería leer la diferencia entre EXTENDED y NORMAL EntityManager. Parece que estás usando un em extendido de una manera muy incómoda (sacar el bucle de la transacción te ayudaría a deshacerte de él finalmente).
Edición pequeña: si intenta utilizar UserTransaction, como sugiere la otra publicación, recibirá un error, porque un EntityManager estándar (que probablemente esté usando) utiliza el llamado CMT (Transacciones administradas por contenedor). No lo toques, a menos que entiendas las tres oposiciones básicas (si quieres, puedo elaborar, pero francamente, no lo necesitarás):
- EntityManager administrado por contenedor versus EntityManager administrado por aplicación,
- Transacciones administradas por contenedor versus transacciones administradas por aplicación,
- EntityManager NORMAL y EntityManager EXTENDIDO.
Solo para resumir el código que me funciona en Jboss EAP6 e Hibernate 4.2.18.Final.
Puede ahorrar tiempo para alguien.
persistencia.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>
campo de golf:
Gestores de https://docs.oracle.com/cd/E19798-01/821-1841/bnbra/index.html gestionados por la aplicación https://docs.oracle.com/cd/E19798-01/821-1841/bnbra/index.html
EntityTransaction
se utiliza con el administrador de entidades de tipo recurso local. Si desea usar JTA, entonces tiene que usar la interfaz UserTransaction
.
De la documentación: EntityTransaction : interfaz utilizada para controlar las transacciones en los administradores de entidades de recursos locales. El método EntityManager.getTransaction () devuelve la interfaz EntityTransaction.
Edición: Añadido pseudo código.
@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
}
}