web-dev-qa-db-fra.com

Transaction marquée en tant que restauration uniquement: comment 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");
}

Lorsque 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 manque complètement dans l'exception - ce qui est correct je suppose?
  2. Quelque chose dans la méthode B () a marqué la transaction en tant que restauration seulement? Comment puis-je le trouver? Existe-t-il, par exemple, un moyen de vérifier quelque chose comme getCurrentTransaction().isRollbackOnly()? - comme ceci, je pourrais parcourir la méthode et en trouver la cause.
81
Vojtěch

J'ai finalement compris le problème:

methodA() {
    methodB()
}

@Transactional(noRollbackFor = Exception.class)
methodB() {
    ...
    try {
        methodC()
    } catch (...) {...}
    log("OK");
}

@Transactional
methodC() {
    throw new ...();
}

Qu'est-ce qui se passe, c'est que même si le methodB a la bonne annotation, le methodC ne l'a pas. Lorsque l'exception est levée, le second @Transactional marque la première transaction en tant que restauration uniquement.

59
Vojtěch

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 une restauration uniquement (même si vous les interceptez). Vous pouvez utiliser d'autres attributs de l'annotation @Transactional pour l'empêcher de revenir en arrière, comme ceci:

@Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)
89
Ean V

Pour récupérer rapidement l’exception responsable sans avoir besoin de re-coder 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 certain Interceptor. Là, vous pouvez lire l'exception causante de certains bloc catch.

38
FelixJongleur42

J'ai eu du mal avec cette exception lors de l'exécution de mon application.

Enfin le problème était lié à la requête SQL . Je veux dire que la requête est fausse.

veuillez vérifier votre requête. C'est ma suggestion

10
Kumaresan Perumal

Recherchez les exceptions levées et interceptées dans les sections ... de votre code. Les exceptions d'application à l'exécution et à l'annulation entraînent une annulation lorsqu'elles sont supprimées d'une méthode métier, même si elles sont interceptées ailleurs.

Vous pouvez utiliser le contexte pour savoir si la transaction est marquée pour une annulation.

@Resource
private SessionContext context;

context.getRollbackOnly();
5
Mareen

désactiver 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 à l'origine de la restauration;)

1
rémy

J'ai trouvé une bonne explication aux 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 si elle a une exception, elle ne fait que bouillonner et n’affecte pas les transactions.

OU:

2) si la méthode imbriquée a besoin du contrôle de la transaction, définissez-la comme REQUIRE_NEW pour la stratégie de propagation de cette manière, même si une exception est levée et marquée comme annulation uniquement, l'appelant ne sera pas affecté.

1
aquajach

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

pendant que le test junit s'applique sous le code

@Test
public void updateData()
{
  int i=productRepository.updateMyData("Iphone",102);

  System.out.println("successfully updated ... ");
  assertTrue(i!=0);

}

ça fonctionne bien pour mon code

0
Asif Raza

veuillez vérifier votre @ Transactional importez-le.

import javax.transaction.Transactional;
0
SHIVA