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()...
  1. le contexte de methodB est complètement manquant dans l'exception-ce qui est correct je suppose?
  2. 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.
64
demandé sur leonardoborges 2013-10-10 21:18:35

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)
67
répondu Ean V 2013-10-11 06:03:14

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.

50
répondu Vojtěch 2013-10-11 06:56:35

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.

33
répondu FelixJongleur42 2016-05-14 19:49:19

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

7
répondu Kumaresan Perumal 2016-10-18 05:34:43

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();
5
répondu Mareen 2013-10-10 20:07:25

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 ;)

1
répondu rémy 2015-07-05 07:39:46

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é.

1
répondu aquajach 2018-07-23 05:59:42