tutorial transacciones tipos español transactions java-ee ejb-3.0

transactions - transacciones - EJB3 Propagación de transacción



tipos de ejb (8)

Tengo un frijol sin estado algo así como:

@Stateless public class MyStatelessBean implements MyStatelessLocal, MyStatelessRemote { @PersistenceContext(unitName="myPC") private EntityManager mgr; @TransationAttribute(TransactionAttributeType.SUPPORTED) public void processObjects(List<Object> objs) { // this method just processes the data; no need for a transaction for(Object obj : objs) { this.process(obj); } } @TransationAttribute(TransactionAttributeType.REQUIRES_NEW) public void process(Object obj) { // do some work with obj that must be in the scope of a transaction this.mgr.merge(obj); // ... this.mgr.merge(obj); // ... this.mgr.flush(); } }

El uso típico entonces es que el cliente llamaría a processObjects (...), que en realidad no interactúa con el administrador de entidades. Hace lo que necesita hacer y llama al proceso (...) individualmente para cada objeto a procesar. La duración del proceso (...) es relativamente corta, pero processObjects (...) podría tardar mucho tiempo en recorrer todo. Por lo tanto, no quiero que mantenga una transacción abierta. Necesito las operaciones de proceso individuales (...) para operar dentro de su propia transacción. Esta debería ser una nueva transacción para cada llamada. Por último, me gustaría mantener la opción abierta para que el cliente llame al proceso (...) directamente.

He intentado una serie de tipos de transacciones diferentes: nunca, no soportado, compatible (en processObjects) y requerido, requiere nuevo (en proceso) pero obtengo TransactionRequiredException cada vez que se llama a merge ().

Pude hacerlo funcionar dividiendo los métodos en dos beans diferentes:

@Stateless @TransationAttribute(TransactionAttributeType.NOT_SUPPORTED) public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 { @EJB private MyStatelessBean2 myBean2; public void processObjects(List<Object> objs) { // this method just processes the data; no need for a transaction for(Object obj : objs) { this.myBean2.process(obj); } } } @Stateless public class MyStatelessBean2 implements MyStatelessLocal2, MyStatelessRemote2 { @PersistenceContext(unitName="myPC") private EntityManager mgr; @TransationAttribute(TransactionAttributeType.REQUIRES_NEW) public void process(Object obj) { // do some work with obj that must be in the scope of a transaction this.mgr.merge(obj); // ... this.mgr.merge(obj); // ... this.mgr.flush(); } }

pero todavía tengo curiosidad si es posible lograr esto en una clase. Me parece que el administrador de transacciones solo opera en el nivel de los beans, incluso cuando los métodos individuales reciben anotaciones más específicas. Entonces, si marqué un método para evitar que la transacción comience a llamar a otros métodos dentro de esa misma instancia, ¿tampoco creará una transacción, sin importar cómo estén marcados?

Estoy usando JBoss Application Server 4.2.1.GA, pero las respuestas no específicas son bienvenidas / preferidas.


Creo que el problema es que cada bean está envuelto en un proxy que controla el comportamiento transaccional. Cuando llama desde un bean a otro, va a través del proxy de ese bean y el proxy puede cambiar el comportamiento de la transacción.

Pero cuando un bean llama a un método en sí mismo con un atributo de transacción diferente, la llamada no pasa por el proxy, por lo que el comportamiento no cambia.


Creo que tiene que ver con el @TransationAttribute (TransactionAttributeType.Never) en el método processObjects .

TransactionAttributeType.Never

http://docs.sun.com/app/docs/doc/819-3669/6n5sg7cm3?a=view

Si el cliente se ejecuta dentro de una transacción e invoca el método enterprise bean, el contenedor arroja una RemoteException. Si el cliente no está asociado con una transacción, el contenedor no inicia una nueva transacción antes de ejecutar el método.

Supongo que usted es el cliente del método processObjects desde el código del cliente. Debido a que probablemente su cliente no esté asociado con una transacción, la llamada al método con TransactionAttributeType.Never es feliz en primer lugar. A continuación, llama al método de proceso de processObjects que, aunque tener la anotación TransactionAttributeType.Required no era una llamada de método Bean y la política de transacción no se aplica. Cuando llama a fusión , obtiene la excepción porque aún no está asociado con una transacción.

Intenta usar TransactionAttributeType.Required para ambos métodos de bean para ver si funciona.


En caso de que alguien se encuentre con este día:

para evitar dependencias circulares (permitiendo la auto-referencia por ejemplo) en JBoss use la anotación ''IgnoreDependency'' por ejemplo:

@IgnoreDependency @EJB Myelf myselfRef;


Matt, la pregunta que haces es bastante clásica, creo que la solución de autorreferencia de Herval / Pascal es clara. Hay una solución más general que no se menciona aquí.

Este es un caso para las transacciones de "usuario" de EJB. Como se encuentra en un bean de sesión, puede obtener la transacción del usuario desde el contexto de la sesión. Así es como se verá el código con las transacciones del usuario:

// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1 @Stateless @TransationAttribute(TransactionAttributeType.NOT_SUPPORTED) public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 { @Resource private SessionContext ctx; @EJB private MyStatelessLocal1 myBean2; public void processObjects(List<Object> objs) { // this method just processes the data; no need for a transaction for(Object obj : objs) { this.myBean2.process(obj); } } public void process(Object obj) { UserTransaction tx = ctx.getUserTransaction(); tx.begin(); // do some work with obj that must be in the scope of a transaction this.mgr.merge(obj); // ... this.mgr.merge(obj); // ... this.mgr.flush(); tx.commit(); } }


Matt, por lo que valga, he llegado exactamente a la misma conclusión que tú.

Los atributos TransactionAttributeTypes solo se tienen en cuenta al cruzar los límites de Bean. Cuando se invocan métodos dentro del mismo bean, TransactionAttributeTypes no tiene ningún efecto, sin importar qué Tipos se pongan en los métodos.

Por lo que puedo ver, no hay nada en la especificación de persistencia EJB que especifique cuál debería ser el comportamiento bajo estas circunstancias.

También he experimentado esto en Jboss. También lo intentaré en Glassfish y te haré saber los resultados.


Otra forma de hacerlo es tener ambos métodos en el mismo bean, ¡y tener una referencia de @EJB a sí mismo! Algo como eso:

// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1 @Stateless @TransationAttribute(TransactionAttributeType.NOT_SUPPORTED) public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 { @EJB private MyStatelessLocal1 myBean2; public void processObjects(List<Object> objs) { // this method just processes the data; no need for a transaction for(Object obj : objs) { this.myBean2.process(obj); } } @TransationAttribute(TransactionAttributeType.REQUIRES_NEW) public void process(Object obj) { // do some work with obj that must be in the scope of a transaction this.mgr.merge(obj); // ... this.mgr.merge(obj); // ... this.mgr.flush(); } }

De esta forma, en realidad "obligas" al método process() a acceder a través de la pila ejb de proxies, por lo tanto, tomando @TransactionAttribute en vigencia, y manteniendo solo una clase. ¡Uf!


Tenía estos problemas de dependencia circular que Kevin mencionó. Sin embargo, la anotación propuesta @IgnoreDependency es una anotación específica de jboss y no existe una contrapartida en, por ejemplo, Glassfish.

Como no funciona con la referencia EJB predeterminada, me sentí un poco incómodo con esta solución.

Por lo tanto, le di una oportunidad a la solución de Bluecarbon, comenzando así la transacción interna "a mano".

Además de esto, no veo otra solución que implementar el proceso interno () en otro bean que también es feo porque simplemente queremos alterar nuestro modelo de clase para tales detalles técnicos.


Todavía no lo he probado (estoy a punto de hacerlo), pero una alternativa a la inyección de una autorreferencia a través de la anotación @EJB es el método SessionContext.getBusinessObject() . Esta sería otra forma de evitar la posibilidad de una referencia circular que explota cosas sobre usted, aunque al menos para la inyección de frijoles sin estado parece funcionar.

Estoy trabajando en un gran sistema en el que se emplean ambas técnicas (presumiblemente por diferentes desarrolladores), pero no estoy seguro de cuál es la forma "correcta" de hacerlo.