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@Transactional
anotación para evitar que retroceda como:fuente
noRollbackFor=Exception.class
, pero parece que no tiene ningún efecto, ¿funciona para excepciones heredadas?methodC
en su primera publicación). AmbosmethodB
ymethodC
usan el mismo TX y siempre@Transactional
se usa la anotación más específica , por lo que cuandomethodC
arroja la excepción, el TX circundante se marcará como solo retroceso. También puede utilizar diferentes marcadores de propagación para evitar esto.EmptyResultDataAccessException
excepció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.@Transactional
contenedor de proxy, es decir, no detectadas . Vea otra respuesta de Vojtěch para conocer la historia completa. Puede haber@Transactional
métodos anidados que pueden marcar su transacción solo como reversión.Finalmente entendí el problema:
Lo que sucede es que aunque
methodB
tiene la anotación correcta,methodC
no la tiene. Cuando se lanza la excepción, la segunda@Transactional
marca la primera transacción como Revertir solo de todos modos.fuente
propagation=requires_new
entonces methodB no se revertirá?methodC
debe 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@Transactional
anotació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.TransactionImpl
org.hibernate.engine.transaction.internal.TransactionImpl
y 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
SessionContext
una 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