ロールバックのみとしてマークされたトランザクション:原因を見つける方法


93

@Transactionalメソッド内でトランザクションをコミットすることに問題があります。

methodA() {
    methodB()
}

@Transactional
methodB() {
    ...
    em.persist();
    ...
    em.flush();
    log("OK");
}

methodA()からmethodB()を呼び出すと、メソッドが正常に渡され、ログに「OK」と表示されます。しかし、私は得る

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. 例外ではmethodBのコンテキストが完全に欠落しています-どちらが大丈夫でしょうか?
  2. methodB()内の何かがトランザクションをロールバックのみとしてマークしましたか?どうすれば見つけられますか?たとえば、次のようなものをチェックする方法はありますかgetCurrentTransaction().isRollbackOnly()?-このようにして、メソッドをステップ実行して原因を見つけることができます。



興味深いことに、データベーステーブルが存在しない場合、このエラーが表示されることがあります。
Ng Sek Long

回答:


101

メソッドをとしてマークすると、メソッド@Transactional内で例外が発生すると、周囲のTXがロールバックのみとしてマークされます(キャッチしても)。@Transactionalアノテーションの他の属性を使用して、次のようなロールバックを防ぐことができます。

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

6
さて、私はを使用しようとしnoRollbackFor=Exception.classましたが、効果がないようです–継承された例外に対しては機能しますか?
Vojtěch

6
はい、そうです。あなた自身の答えを見て、それは正しいです(あなたはmethodCあなたの最初の投稿で提供していませんでした)。両方が同じTX methodBmethodC使用し、常に最も具体的な@Transactionalアノテーションが使用されるためmethodC、例外をスローすると、周囲のTXはロールバックのみとしてマークされます。これを防ぐために、さまざまな伝播マーカーを使用することもできます。
Ean V 2013

@Ean メソッド内の例外は、周囲のTXをロールバックのみとしてマークします。これはreadOnlyトランザクションにも適用されますか?
Marko Vranjkovic 2014年

1
@lolotron @Ean読み取り専用トランザクションに実際に適用されることを確認できます。私のメソッドはEmptyResultDataAccessException読み取り専用トランザクションで例外をスローし、同じエラーが発生しました。@Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)問題を修正するために注釈を変更します。
cbmeeks

5
この答えは間違っています。Springは@Transactional、プロキシラッパーを通過する例外、つまりuncaughtのみを認識します。詳細については、ヴォイチュフの他の回答を参照してください。@Transactionalトランザクションのロールバックのみをマークできるネストされたメソッドがある可能性があります。
Yaroslav Stavnichiy 2017年

68

私は最終的に問題を理解しました:

methodA() {
    methodB()
}

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

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

何が起きるかというと、methodBには適切な注釈methodCがありますが、そうではありません。例外がスローされると、2番目@Transactionalは最初のトランザクションをとにかくロールバックとしてのみマークします。


5
トランザクションのステータスはスレッドローカル変数に格納されます。springがmethodCをインターセプトしてフラグをrollbackに設定すると、トランザクションはすでにロールバックのマークが付けられています。例外の任意のさらなる抑制がないのに役立ちます、最終的な問題が発生したコミットするとき、エラーが発生しますので
住んでいる

@Vojtěch仮に、仮にmethodCmethodBを持っているpropagation=requires_new場合、methodBはロールバックしませんか?
deFreitas

4
methodC別のSpring Bean /サービスにあるか、Springプロキシ経由で何らかの方法でアクセスする必要があります。それ以外の場合、Springは例外について知る可能性はありません。@Transactionalアノテーションを通過する例外のみが、トランザクションをロールバックのみとしてマークできます。
Yaroslav Stavnichiy 2017年

43

再コーディングまたは再構築する必要なく、原因となっている例外をすばやくフェッチするには、ブレークポイントをオンに設定します

org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or
org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3

スタックを上って、通常はインターセプターに行きます。そこで、いくつかのcatchブロックから原因となっている例外を読み取ることができます。


6
Hibernate 4.3.11では、それはorg.hibernate.jpa.internal.TransactionImpl
Wim Deblauwe

とても素敵な友達!
ラファエルアンドラーデ

ありがとう!Hibernateの新しいバージョン(5.4.17)では、クラスはでorg.hibernate.engine.transaction.internal.TransactionImplあり、メソッドはsetRollbackOnlyです。
Peter Catalin

11

アプリケーションの実行中に、この例外に苦労しました。

最後に、問題はSQLクエリにありました。クエリが間違っていることを意味します。

クエリを確認してください。これは私の提案です


1
明確にするには:1. SQL構文にエラーがある場合2.例外でロールバックするように設定されている3. readOnlyトランザクションがある場合、SQL構文により、ロールバックをトリガーする例外が発生し、ロールバックがトリガーされるため、失敗する読み取り専用」モード。
デイブ

7

...コードのセクションでスローおよびキャッチされる例外を探します。ランタイムおよびロールバックアプリケーションの例外により、他の場所でキャッチされた場合でも、ビジネスメソッドからスローされるとロールバックが発生します。

コンテキストを使用して、トランザクションにロールバックのマークが付けられているかどうかを確認できます。

@Resource
private SessionContext context;

context.getRollbackOnly();

1
原因を見つけたようですが、なぜこれが起こっているのかわかりません。内部メソッドが例外をスローしますが、それをキャッチし、ログに記録して無視します。ただし、トランザクションはいずれにしてもロールバックとしてマークされます。どうすればそれを防ぐことができますか?私が適切にキャッチした例外がトランザクションに影響を与えたくありません。
Vojtěch

あるSessionContext春に標準クラス?私にはそれはむしろEJB3であり、私のSpringアプリケーションには含まれていません。
Vojtěch

3
悪いのは、それが春に関するものであるという事実を逃したことです。とにかく、TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()利用できるようなものがあるはずです。
Mareen 2013年

2

解決策で良い説明を見つけました:https : //vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/

1)トランザクション制御が本当に必要ない場合は、ネストされたメソッドから@Transacionalを削除します。そのため、例外があったとしても、気泡が発生し、トランザクションに影響を与えることはありません。

または:

2)ネストされたメソッドがトランザクション制御を必要とする場合、例外をスローしてロールバックのみとしてマークされていても、それを伝播ポリシーのREQUIRE_NEWとしてそのようにすると、呼び出し元は影響を受けません。


1

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>

これらの行をコメント化すると、ロールバックの原因となる例外が表示されます;)


0

以下のコードをproductRepositoryに適用します

@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);

junitテスト中にコードの下に適用します

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

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

}

私のコードでは問題なく機能しています

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.