Manejo de transacciones en hibernate/spring

Buenas tardes a todos, acudo a ustedes porque tengo un problema que he estado buscando pero no encuentro por donde esta la solución.

Estoy manejando una aplicación con el patrón DAO y tengo la siguiente clase en la capa de negocio:

 

Y esta es mi clase de la capa de acceso a datos:
 

Mi primer duda es qué sucede si hay una excepción en la línea  , se hace rollback?, ya que el @Transactional lo tiene el método que manda a llamar el método que tiene ésta línea...

Otra duda es, ¿Dónde debo poner el @Transactional, en la capa de acceso a datos (DAO) o en la capa de negocio (la que manda a llamar a los DAO) ?

Y por último: ¿Puedo poner @Transactional en la capa DAO y también en la capa de negocio ?

Les agradezco infinitamente pudieran ayudarme a encontrar cómo solucionar esto porque por una extraña razón mi aplicación me está lanzando errores que dicen que no se puede hacer rollback porque la conexión ha sido cerrada y supongo que primero debo entender bien cómo se manejan las transacciones. Gracias por su tiempo

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

@Transactional

Lee la documentación de la transaccionalidad en Spring. Si usas configuración de transacciones basada en anotaciones, necesitas tener un único PlatformTransactionManager definido en tu applicationContext, y entonces los beans que tengan @Transactional serán envueltos en proxies que ejecutan los métodos anotados dentro de una transacción. Si se arroja una RuntimeException, se da rollback. Si se arroja una excepción declarada en el método (que NO sea RuntimeException por supuesto), NO se da rollback (a menos que configures en la anotación que para esa excepción sí se haga rollback).

Puedes poner   donde te dé la gana. Generalmente se pone en la capa DAO, pero si por algo la necesitas en la capa de negocio también la puedes poner ahí, y puedes indicarle si quieres que sea una transacción anidada o que use la existente en caso de que ya haya una abierta (PROPAGATION_REQUIRED, el comportamiento default).

Es muy importante que consideres lo de los proxies. Considera el siguiente escenario:

 

Cuando invocas el   de  , ahí se invoca el   de  , el cual se ejecutará dentro de una transacción, porque la referencia que   tiene a   realmente es al proxy (de eso se encarga Spring en el ApplicationContext).

Pero cuando invocas  , eso se invoca en el proxy, pero dentro de   se invoca directamente  , no dentro del proxy sino ya directamente el método en el objeto envuelto en el proxy. Y como   no está anotado como transaccional, no se genera una transacción, por lo tanto si truena la segunda operación, se arroja excepción pero no hay rollback y los datos insertados por la primera operación se quedan, y tendrás una bonita inconsistencia de datos.

Imagen de AlexSnake

Interesante.

Valla que tema tan interesante, supongo que la configuración que se requiere @ezamudio en el applicationContext para las transacciones es esta:

 

Pero que pasaria si tienes un metodo que requiere guardar varios datos, supiendo asi:

 

Y que por algun motivo truene la 2da linea, se guardan el primer dato o le da rollback?

Menuda explicación ezamudio,

Menuda explicación ezamudio, muchas gracias

Mi applicationContext lo tengo definido así para los proxies, es correcto ?

 

Imagen de ezamudio

tx:annotation-driven

Es correcto, con el tx:annotation-driven es que se procesan los beans que tengan la anotación @Transactional y se envuelven en proxies que van a iniciar una transacción con el transactionManager configurado y le van a dar commit/rollback al final del método.

AlexSnake: El ResourcelessTransactionManager no lo conozco, por lo que veo debe ser de Spring Batch, que no lo he manejado. Pero la configuración es como puso hobo_75: defines un TransactionManager (puede ser el de DataSource, o el de Hibernate, o de JTA, etc) y luego pones el tx:annotation-driven para que use ese TransactionManager.

Y en el caso de tu método saveData(), lo que va a pasar es que se inicia una transacción, se ejecuta la primera línea, luego la segunda, y cuando "truena" (que significa, arroja una excepción en tiempo de ejecución, o sea una subclase de RuntimeException), eso lo cacha el proxy y le da rollback a la transacción, y re-arroja la excepción. Pero si no truena esa línea ni la que sigue, entonces al final se le da commit a la transacción.

Imagen de AlexSnake

Tks ezamudio

Gracias por contestar mi pregunta ezamudio y al igual que hobo_75 tambien tenia esa duda, revisare la documentación de la transaccionalidad en Spring como bien mencionas.
Saludos.

Muchas gracias por la

Muchas gracias por la explicación, te agradezco ezamudio

Transactional en dos aplicaciones distintas

Hola ezamudio, gracias por la valiosa información.

Quisiera consultarte algo.

Tengo una aplicación web donde tengo ya tengo configurado el Transactional de spring, también tengo otra aplicación que es un web service donde también aplico el Transactional de spring. Cuando ejecuto individualmente cada proyecto el transactiona de cada uno funciona perfectamente. Pero cuando uso las dos aplicaciones al mismo tiempo me ocurre una lectura sucia. Ambas aplicaciones lee una tabla donde tengo un valor numerico y ambas aplicaciones pueden modificar este valor.

La pregunta es, ¿Cómo puedo hacer para que funcione el transactional sobre un registro cuando interactúan dos aplicaciones distintas?
y de antemano te agradezco por tu ayuda.

Imagen de ezamudio

FOR UPDATE

Tienes que ponerle FOR UPDATE al SELECT que lee el registro que ambas modifican. Cuando haces SELECT...FOR UPDATE dentro de una transacción, se bloquean los registros devueltos hasta que la transacción termina.

FOR UPDATE

Hola ezamudio, muchas gracias por tu pronta respuesta.

Por favor, me podrías dar algún ejemplo o guía de como hacer esto, es decir lo debería hacer con los metadatas de spring o tal vez en los DAO con hibernate ?? yo uso los criteria de hibernate, ¿debería hacerlo con los criteria de hibernate ?

y Gracias nuevamente ezamudio

Imagen de ezamudio

lock

Si usas Hibernate entonces debes de bloquearlos en tu sesión con el método lock.

Imagen de chuyzartos

me has salvado ezamudio jeje

estuve batallando un buen rato porque justamente tenia este problema que no me hacia rollback,puesto que lo hacia dentro de mi propio bean, pero hice la prueba creando otro bean y llamando mi metodo @transaccional y "buala" , GRACIAS eres un crack , saludos