Tengo problemas para realizar una transacción dentro de mi método @Transactional:
methodA() {
methodB()
}
@Transactional
methodB() {
...
em.persist();
...
em.flush();
log("OK");
}
Cuando llamo a methodB () desde methodA (), el método pasa correctamente y puedo ver "OK" en mis registros. Pero luego consigo
Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly 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:521)
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.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at methodA()...
- El contexto del método B falta por completo en la excepción, ¿cuál está bien, supongo?
- ¿Algo dentro del método B () marcó la transacción como solo reversión? ¿Cómo puedo averiguarlo? ¿Existe, por ejemplo, una forma de comprobar algo como
getCurrentTransaction().isRollbackOnly()?? Así podría recorrer el método y encontrar la causa.

Respuestas:
Cuando marca su método como
@Transactional, la aparición de cualquier excepción dentro de su método marcará el TX circundante como solo retroceso (incluso si los detecta). Puede usar otros atributos de la@Transactionalanotación para evitar que retroceda como:fuente
noRollbackFor=Exception.class, pero parece que no tiene ningún efecto, ¿funciona para excepciones heredadas?methodCen su primera publicación). AmbosmethodBymethodCusan el mismo TX y siempre@Transactionalse usa la anotación más específica , por lo que cuandomethodCarroja la excepción, el TX circundante se marcará como solo retroceso. También puede utilizar diferentes marcadores de propagación para evitar esto.EmptyResultDataAccessExceptionexcepción en una transacción de solo lectura y obtuve el mismo error. Cambiando mi anotación para@Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)solucionar el problema.@Transactionalcontenedor de proxy, es decir, no detectadas . Vea otra respuesta de Vojtěch para conocer la historia completa. Puede haber@Transactionalmétodos anidados que pueden marcar su transacción solo como reversión.Finalmente entendí el problema:
Lo que sucede es que aunque
methodBtiene la anotación correcta,methodCno la tiene. Cuando se lanza la excepción, la segunda@Transactionalmarca la primera transacción como Revertir solo de todos modos.fuente
propagation=requires_newentonces methodB no se revertirá?methodCdebe estar en un bean / servicio de Spring diferente o se debe acceder de alguna manera a través del proxy de Spring. De lo contrario, Spring no tendrá la posibilidad de conocer su excepción. La única excepción que pasa por la@Transactionalanotación puede marcar la transacción como solo de reversión.Para obtener rápidamente la excepción causante sin la necesidad de volver a codificar o reconstruir , establezca un punto de interrupción en
y subir en la pila, generalmente a algún Interceptor. Allí puede leer la excepción causante de algún bloque de captura.
fuente
org.hibernate.jpa.internal.TransactionImplorg.hibernate.engine.transaction.internal.TransactionImply el método essetRollbackOnly.Luché con esta excepción mientras ejecutaba mi aplicación.
Finalmente, el problema estaba en la consulta sql . quiero decir que la consulta es incorrecta.
por favor verifique su consulta. Esta es mi sugerencia
fuente
Busque excepciones lanzadas y atrapadas en las
...secciones de su código. Las excepciones de aplicaciones en tiempo de ejecución y reversión provocan una reversión cuando se eliminan de un método comercial, incluso si se detectan en otro lugar.Puede utilizar el contexto para averiguar si la transacción está marcada para reversión.
fuente
SessionContextuna clase estándar en primavera? Me parece que es más bien EJB3 y no está contenido en mi aplicación Spring.TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()disponible.Encontré una buena explicación con soluciones: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/
1) elimine @Transacional del método anidado si realmente no requiere control de transacciones. Entonces, incluso tiene una excepción, simplemente surge y no afecta las transacciones.
O:
2) si el método anidado necesita control de transacciones, hágalo como REQUIRE_NEW para la política de propagación de esa manera, incluso si arroja una excepción y está marcado como solo reversión, la persona que llama no se verá afectada.
fuente
deshabilite el transactionmanager en su Bean.xml
comente estas líneas y verá la excepción que causa la reversión;)
fuente
aplicar el siguiente código en productRepository
@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);mientras esté en la prueba junit, aplique el código siguiente
está funcionando bien para mi código
fuente