Je rencontre des problèmes pour valider une transaction dans ma méthode @Transactional:
methodA() {
methodB()
}
@Transactional
methodB() {
...
em.persist();
...
em.flush();
log("OK");
}
Quand j'appelle methodB () depuis 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 absent de l'exception - ce qui est bien je suppose?
- Quelque chose dans la méthodeB () a marqué la transaction comme une annulation uniquement? Comment puis-je le découvrir? Existe-t-il, par exemple, un moyen de vérifier quelque chose comme
getCurrentTransaction().isRollbackOnly()?
- comme ça, je pourrais parcourir la méthode et trouver la cause.
Réponses:
Lorsque vous marquez votre méthode comme
@Transactional
, l'occurrence d'une exception à l'intérieur de votre méthode marquera le TX environnant comme un retour arrière uniquement (même si vous les attrapez). Vous pouvez utiliser d'autres attributs d'@Transactional
annotation pour l'empêcher de revenir en arrière comme:la source
noRollbackFor=Exception.class
, mais cela semble n'avoir aucun effet - est-ce que cela fonctionne pour les exceptions héritées?methodC
dans votre premier message). Les deuxmethodB
etmethodC
utilisent le même TX et l'@Transactional
annotation la plus spécifique est toujours utilisée, donc lorsquemethodC
l'exception est lancée, le TX environnant sera marqué comme rollback uniquement. Vous pouvez également utiliser différents marqueurs de propagation pour éviter cela.EmptyResultDataAccessException
exception sur une transaction en lecture seule et j'ai eu la même erreur. Changer mon annotation pour@Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)
résoudre le problème.@Transactional
wrapper proxy, c'est-à-dire non interceptées . Voir l'autre réponse de Vojtěch pour l'histoire complète. Il peut y avoir des@Transactional
méthodes imbriquées qui peuvent marquer uniquement l'annulation de votre transaction.J'ai enfin compris le problème:
Ce qui se passe, c'est que même si le
methodB
a la bonne annotation, lemethodC
ne le fait pas. Lorsque l'exception est levée, la seconde@Transactional
marque la première transaction comme Rollback uniquement de toute façon.la source
propagation=requires_new
alors methodB ne reviendra pas?methodC
doit être dans un bean / service Spring différent ou accessible via le proxy Spring. Sinon, Spring n'aura aucune possibilité de connaître votre exception. Seule une exception qui passe par l'@Transactional
annotation peut marquer la transaction comme annulation uniquement.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
et montez dans la pile, généralement vers un intercepteur. Là, vous pouvez lire l'exception à l'origine de certains blocs catch.
la source
org.hibernate.jpa.internal.TransactionImpl
org.hibernate.engine.transaction.internal.TransactionImpl
et la méthode estsetRollbackOnly
.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 fausse.
veuillez vérifier votre requête. C'est ma suggestion
la source
Recherchez les exceptions levées et interceptées dans les
...
sections de votre code. Les exceptions d'application d'exécution et de restauration provoquent une restauration 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 une annulation.
la source
SessionContext
classe standard est-elle au printemps? Il me semble que c'est plutôt EJB3 et il n'est pas contenu dans mon Spring Application.TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()
disponible.J'ai trouvé une bonne explication avec des solutions: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/
1) supprimez @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, cela ne fait que bouillonner et n'affecte pas les choses transactionnelles.
OU:
2) si la méthode imbriquée nécessite un contrôle de transaction, définissez-la comme REQUIRE_NEW pour la politique de propagation de cette façon, même si elle lance une exception et est marquée uniquement comme une annulation, l'appelant ne sera pas affecté.
la source
désactiver le gestionnaire de transaction dans votre Bean.xml
commentez ces lignes, et vous verrez l'exception provoquant l'annulation;)
la source
appliquer le code ci-dessous dans productRepository
@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);
tandis que dans le test junit, appliquer ci-dessous le code
cela fonctionne bien pour mon code
la source