transaction example enabletransactionmanagement java spring hibernate transactions rollback

java - example - spring transaction propagation



Comportamiento extraño con @Transactional(propagation=Propagation.REQUIRES_NEW) (1)

Las transacciones Spring, de forma predeterminada, funcionan envolviendo el bean Spring con un proxy que maneja la transacción y las excepciones. Cuando llama a method2() desde method1() , está omitiendo completamente este proxy, por lo que no puede iniciar una nueva transacción, y efectivamente está llamando a method2() desde la misma transacción que la que abrió la llamada method1() .

Por el contrario, cuando llama a un método de otro bean inyectado desde method1() , de hecho está llamando a un método en un proxy transaccional. Por lo tanto, si este método extraño está marcado con REQUIRES_NEW, el proxy inicia una nueva transacción y usted puede detectar la excepción en el method1() y reanudar la transacción externa.

Esto se describe en la documentación .

Aquí está mi problema:

Estoy ejecutando un lote en una aplicación Java EE / Spring / Hibernate. Este lote llama a un method1 . Este método llama a un method2 que puede lanzar UserException (una clase que extiende RuntimeException ). Así es como se ve:

@Transactional public class BatchService implements IBatchService { @Transactional(propagation=Propagation.REQUIRES_NEW) public User method2(User user) { // Processing, which can throw a RuntimeException } public void method1() { // ... try { this.method2(user); } catch (UserException e) { // ... } // ... } }

La excepción se detecta cuando la ejecución continúa, pero al final del method1 cuando se cierra la transacción, se lanza una excepción RollbackException.

Aquí está la traza de la pila:

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:476) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy128.method1(Unknown Source) at batch.BatchController.method1(BatchController.java:202)

Cuando method2 no está lanzando esta excepción, funciona bien.

Lo que he intentado:

  • Configuración de @Transactional(noRollbackFor={UserException.class})) en method1
  • Prueba y atrapa en method2

Pero no cambió nada.

Como la excepción se produce en una transacción diferente en la que se produjo una reversión, no entiendo por qué no funciona. Eché un vistazo a esto: Jpa transaction javax.persistence.RollbackException: Transacción marcada como rollbackOnly pero realmente no me ayudó.

Estaré muy agradecido si alguien me pudiera dar una pista.

Actualizar

Lo he hecho funcionar configurando propagation=Propagation.REQUIRES_NEW en el método llamado por method2 (que en realidad es el que está enviando la excepción). Este método se define en una clase muy similar a mi BatchService . Así que no veo por qué funciona en este nivel y no en method2 .

  • He establecido method2 como público, ya que la anotación @Transactional no se tiene en cuenta si el método es privado como se dice en la documentación:

La anotación @Transactional puede colocarse antes de una definición de interfaz, un método en una interfaz, una definición de clase o un método público en una clase.

  • También intenté usar Exception lugar de RuntimeException (ya que es más apropiado) pero tampoco cambió nada.

Incluso si está funcionando, la pregunta permanece abierta ya que tiene un comportamiento extraño y me gustaría entender por qué no está actuando como debería.