Transaction marquée comme rollback uniquement: Comment puis-je trouver la cause
J'ai des problèmes avec la validation d'une transaction dans ma méthode @ Transactional:
methodA() {
methodB()
}
@Transactional
methodB() {
...
em.persist();
...
em.flush();
log("OK");
}
Quand j'appelle methodB () à partir de methodA (), la méthode passe avec succès et je peux voir " OK " dans mes journaux. Mais alors je reçois
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()...
- le contexte de methodB est complètement manquant dans l'exception-ce qui est correct je suppose?
- quelque chose dans la methodB () a marqué la transaction comme rollback seulement? Comment puis-je la trouver? Y a t il par exemple un moyen de vérifier quelque chose comme
getCurrentTransaction().isRollbackOnly()?
- comme ça, je pourrais passer par la méthode et trouver la cause.
7 réponses
Lorsque vous marquez votre méthode comme @Transactional
, l'occurrence de toute exception à l'intérieur de votre méthode marquera le TX environnant comme roll-back seulement (même si vous les attrapez). Vous pouvez utiliser d'autres attributs de l'annotation @Transactional
pour l'empêcher de revenir en arrière comme:
@Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)
J'ai finalement compris le problème:
methodA() {
methodB()
}
@Transactional(noRollbackFor = Exception.class)
methodB() {
...
try {
methodC()
} catch (...) {...}
log("OK");
}
@Transactional
methodC() {
throw new ...();
}
Ce qui se passe, c'est que même si le methodB
a la bonne annotation, le methodC
ne le fait pas. Lorsque l'exception est levée, le second @Transactional
marque la première transaction comme Restauration seulement de toute façon.
Pour récupérer rapidement l'exception à l'origine sans avoir besoin de recoder ou de reconstruire , Définissez un point d'arrêt sur
org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or
org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3
Et monter dans la pile, généralement à un intercepteur. Là, vous pouvez lire l'exception causante de certains blocs de capture.
J'ai eu du mal avec cette exception lors de l'exécution de mon application.
Enfin le problème était sur la requête sql. je veux dire que la requête est erronée.
Veuillez vérifier votre requête. C'est ma suggestion
Recherchez les exceptions lancées et capturées dans les sections ...
de votre code. Les exceptions d'application Runtime et rollbacking provoquent une annulation lorsqu'elles sont rejetées d'une méthode métier, même si elles sont interceptées à un autre endroit.
Vous pouvez utiliser le contexte pour savoir si la transaction est marquée pour la restauration.
@Resource
private SessionContext context;
context.getRollbackOnly();
Désactivez le transactionmanager dans votre Bean.xml
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
Commentez ces lignes, et vous verrez l'exception provoquant la restauration ;)
Trouvé une bonne explication avec des solutions: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/
1) supprimez le @ Transacional de la méthode imbriquée si elle ne nécessite pas vraiment de contrôle de transaction. Donc, même s'il a une exception, il fait juste des bulles et n'affecte pas les choses transactionnelles.
Ou:
2) si la méthode imbriquée a besoin d'un contrôle de transaction, faites-LA comme REQUIRE_NEW pour la stratégie de propagation de cette façon, même si les lancers exception et marqué comme restauration seulement, l'appelant ne sera pas affecté.