Estou tendo problemas para confirmar uma transação no meu método @Transactional:
methodA() {
methodB()
}
@Transactional
methodB() {
...
em.persist();
...
em.flush();
log("OK");
}
Quando chamo methodB () de methodA (), o método foi aprovado com sucesso e posso ver "OK" em meus logs. Mas então eu 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()...
- O contexto do método B está completamente ausente na exceção - o que está certo, suponho?
- Algo dentro do methodB () marcou a transação apenas como rollback? Como posso descobrir? Existe, por exemplo, uma maneira de verificar algo como
getCurrentTransaction().isRollbackOnly()?
- eu poderia percorrer o método e encontrar a causa.
Respostas:
Quando você marca seu método como
@Transactional
, a ocorrência de qualquer exceção dentro de seu método marcará o TX circundante apenas como reversão (mesmo se você capturá-los). Você pode usar outros atributos de@Transactional
anotação para evitar que ela volte, como:fonte
noRollbackFor=Exception.class
, mas parece não ter surtido efeito - funciona para exceções herdadas?methodC
na sua primeira postagem). AmbosmethodB
emethodC
usam o mesmo TX e sempre a@Transactional
anotação mais específica é usada, portanto, quandomethodC
lançar a exceção, o TX adjacente será marcado como somente rollback. Você também pode usar marcadores de propagação diferentes para evitar isso.EmptyResultDataAccessException
exceção em uma transação somente leitura e recebi o mesmo erro. Alterar minha anotação para@Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)
corrigir o problema.@Transactional
proxy wrapper, ou seja, não capturadas . Veja outra resposta da Vojtěch para a história completa. Pode haver@Transactional
métodos aninhados que podem marcar sua transação apenas como rollback.Eu finalmente entendi o problema:
O que acontece é que, embora
methodB
tenha a anotação correta,methodC
não tem. Quando a exceção é lançada, o segundo@Transactional
marca a primeira transação apenas como Rollback.fonte
propagation=requires_new
, o método B não reverterá?methodC
deve estar em um bean / serviço diferente do Spring ou de alguma forma acessado por meio do proxy Spring. Caso contrário, a Spring não terá possibilidade de saber sobre sua exceção. Apenas a exceção que passa pela@Transactional
anotação pode marcar a transação como somente rollback.Para buscar rapidamente a exceção causadora sem a necessidade de recodificar ou reconstruir , defina um ponto de interrupção em
e sobe na pilha, geralmente para algum Interceptor. Lá você pode ler a exceção causadora de algum bloco catch.
fonte
org.hibernate.jpa.internal.TransactionImpl
org.hibernate.engine.transaction.internal.TransactionImpl
e o método ésetRollbackOnly
.Lutei com essa exceção ao executar meu aplicativo.
Finalmente o problema estava na consulta sql . quero dizer que a consulta está errada.
por favor, verifique sua consulta. Esta é minha sugestão
fonte
Procure exceções sendo lançadas e capturadas no
...
seções de seu código. As exceções de tempo de execução e de aplicativo de reversão causam reversão quando descartadas de um método de negócios, mesmo se capturadas em algum outro lugar.Você pode usar o contexto para descobrir se a transação está marcada para reversão.
fonte
SessionContext
uma aula padrão na primavera? Parece-me que é bastante EJB3 e não está contido em meu aplicativo Spring.TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()
disponível.Encontrou uma boa explicação com as soluções: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/
1) remova o @Transacional do método aninhado se ele realmente não exigir o controle da transação. Portanto, mesmo com exceção, ele simplesmente surge e não afeta as transações.
OU:
2) se o método aninhado precisar de controle de transação, torne-o REQUIRE_NEW para a política de propagação dessa forma, mesmo que lance uma exceção e seja marcado apenas como rollback, o chamador não será afetado.
fonte
desative o gerenciador de transações em seu Bean.xml
comente essas linhas e você verá a exceção que causa a reversão;)
fonte
aplique o código abaixo em productRepository
@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);
enquanto no teste junit, aplique o código abaixo
está funcionando bem para o meu código
fonte