web-dev-qa-db-ja.com

@Transactionalアノテーションなしで別のメソッドを呼び出す@Transactionalメソッド?

@TransactionalとマークされたServiceクラスのメソッドを見ましたが、@Transactionalとマークされていない同じクラスの他のメソッドも呼び出していました。

別のメソッドへの呼び出しが原因で、アプリケーションがDBへの別の接続を開いたり、親トランザクションを中断したりするなどのことですか?

@Transactionalアノテーションを持つ別のメソッドによって呼び出される、アノテーションのないメソッドのデフォルトの動作は何ですか?

65
goe

トランザクションブロック内で@Transactionalなしでメソッドを呼び出すと、親トランザクションは新しいメソッドに進みます。親メソッド(@Transactional)からの同じ接続を使用し、呼び出されたメソッドで発生する例外(@Transactionalなし)は、トランザクション定義で設定されたとおりにトランザクションをロールバックします。

同じインスタンス内で@Transactionalを使用するメソッドから@Transactionalアノテーションを使用してメソッドを呼び出した場合、呼び出されたメソッドのトランザクション動作はトランザクションに影響を与えません。ただし、トランザクション定義を持つ別のメソッドからトランザクション定義を持つメソッドを呼び出し、それらが異なるインスタンスにある場合、呼び出されたメソッドのコードは、呼び出されたメソッドで指定されたトランザクション定義に従います。

詳細は、 spring transaction documentation のセクションDeclarative transaction managementで見つけることができます。

Springの宣言的なトランザクションモデルは、AOPプロキシを使用します。そのため、AOPプロキシはトランザクションの作成を担当します。 AOPプロキシは、インスタンス内のメソッドがインスタンスの外部から呼び出された場合にのみアクティブになります。

91
Arun P Johny
  • それは、別のメソッドの呼び出しが原因で、アプリケーションがDBへの別の接続を開いたり、親トランザクションを一時停止したりすることを意味していますか?

それは 伝播レベル に依存します。ここにすべての可能なレベルがあります

たとえば、伝播レベルが [〜#〜] nested [〜#〜] の場合、現在のトランザクションは「一時停止」され、新しいトランザクションが作成されます(注:ネストされたトランザクションの実際の作成は、特定のトランザクションマネージャーでのみ動作します

  • @Transactional注釈を使用して別のメソッドによって呼び出される注釈のないメソッドのデフォルトの動作は何ですか?

デフォルトの伝播レベル(「ビヘイビア」と呼ぶもの)は [〜#〜] required [〜#〜] です。 @Transactionalアノテーションを持つ(またはXMLを介して宣言的に処理される)「内部」メソッドが呼び出された場合、同じトランザクション内で実行されます、たとえば「新しいものはありません」が作成されます。

20
tolitius

@Transactionalはトランザクション境界(開始/終了)をマークしますが、トランザクション自体はスレッドにバインドされます。トランザクションが開始されると、元のメソッドが戻り、トランザクションがコミット/ロールバックされるまで、メソッド呼び出し全体に伝播します。

@Transactionalアノテーションを持つ別のメソッドが呼び出される場合、伝播はそのアノテーションの伝播属性に依存します。

8
sourcedelica

上記の返信から質問に対する答えを推測した可能性があります。内部メソッドに@Transactionalアノテーションが付けられていない場合、内部メソッドは外部メソッドに影響します。

内部メソッドにもREQUIRES_NEWの@Transactionalアノテーションが付けられている場合、以下が発生します。

...
@Autowired
private TestDAO testDAO;

@Autowired
private SomeBean someBean;

@Override
@Transactional(propagation=Propagation.REQUIRED)
public void outerMethod(User user) {
  testDAO.insertUser(user);
  try{
    someBean.innerMethod();
  } catch(RuntimeException e){
    // handle exception
  }
}


@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void innerMethod() {
  throw new RuntimeException("Rollback this transaction!");
}

内部メソッドにはREQUIRES_NEWアノテーションが付けられ、RuntimeExceptionがスローされるため、トランザクションはロールバックに設定されますが、外部トランザクションには影響しません。外部トランザクションは、内部トランザクションが開始されると一時停止され、内部トランザクションが終了した後に再開されます。これらは互いに独立して実行されるため、外部トランザクションは正常にコミットできます。

2
saran3h