web-dev-qa-db-ja.com

@Transactional(propagation = Propagation.REQUIRES_NEW)での奇妙な動作

これが私の問題です:

Java EE/Spring/Hibernateアプリケーションでバッチを実行しています。このバッチは_method1_を呼び出します。このメソッドは_method2_を呼び出します。これによりUserExceptionRuntimeExceptionを拡張するクラス)。次のようになります。

_@Transactional
public class BatchService implements IBatchService {
 @Transactional(propagation=Propagation.REQUIRES_NEW)
 public User method2(User user) {
   // Processing, which can throw a RuntimeException
 }

 public void method1() {
   // ...
   try {
     this.method2(user);
   } catch (UserException e) {
     // ...
   }
   // ...
 }
}
_

実行が続行されると例外がキャッチされますが、トランザクションが閉じられると_method1_の終わりに、RollbackExceptionがスローされます。

これがスタックトレースです:

_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:476)
    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.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.Java:202)
at $Proxy128.method1(Unknown Source)
at batch.BatchController.method1(BatchController.Java:202)
_

_method2_がこの例外をスローしていない場合は、正常に機能します。

私が試したこと:

  • _method1_に@Transactional(noRollbackFor={UserException.class}))を設定する
  • _method2_を試してみてください

しかし、それは何も変わりませんでした。

ロールバックが発生した別のトランザクションで例外がスローされるため、なぜそれが機能しないのかわかりません。私はこれを見ました: Jpaトランザクションjavax.persistence.RollbackException:rollbackOnlyとしてマークされたトランザクション しかし、それは本当に私を助けませんでした。

誰かが私に手がかりを与えることができれば、私は非常に素晴らしいでしょう。

更新

_propagation=Propagation.REQUIRES_NEW_(実際には例外を送信しているメソッド)によって呼び出されるメソッドに_method2_を設定することで、これを機能させました。このメソッドは、私のBatchServiceと非常によく似たクラスで定義されています。したがって、_method2_ではなくこのレベルで機能する理由がわかりません。

  • ドキュメントに記載されているように、メソッドがプライベートの場合、アノテーション_method2_は考慮されないため、_@Transactional_をパブリックに設定しました。

@Transactionalアノテーションは、インターフェイス定義、インターフェイスのメソッド、クラス定義、またはクラスのパブリックメソッドの前に配置できます。

  • また、Exceptionの代わりにRuntimeExceptionを使用しようとしましたが(より適切なため)、何も変更されませんでした。

それが機能しているとしても、それは奇妙な振る舞いをしているので質問は開いたままであり、なぜそれが本来あるべきように動作しないのかを理解したいと思います。

17
DessDess

Springトランザクションは、デフォルトで、トランザクションと例外を処理するプロキシでSpringBeanをラップすることによって機能します。 method2()からmethod1()を呼び出すと、このプロキシが完全にバイパスされるため、新しいトランザクションを開始できず、事実上method2()を呼び出すことになります。 method1()の呼び出しによって開かれたトランザクションと同じトランザクションから。

逆に、method1()から別の注入されたBeanのメソッドを呼び出す場合、実際にはトランザクションプロキシのメソッドを呼び出しています。したがって、このエイリアンメソッドがREQUIRES_NEWでマークされている場合、新しいトランザクションがプロキシによって開始され、method1()で例外をキャッチして、外部トランザクションを再開できます。

これは ドキュメント で説明されています。

36
JB Nizet