web-dev-qa-db-ja.com

Spring Beanで新しいトランザクションを開始する

我々は持っています:

@Transactional(propagation = Propagation.REQUIRED)
public class MyClass implementes MyInterface { ...

MyInterfaceには、go()という1つのメソッドがあります。

Go()を実行すると、メソッドが完了したときにコミット/ロールバックする新しいトランザクションを開始します-これで問題ありません。

ここで、go()で、@Transactional(propagation = Propagation.REQUIRES_NEWを持つMyClassのプライベートメソッドを呼び出してみましょう。 SpringはREQUIRES_NEWアノテーションを「無視」し、新しいトランザクションを開始しないようです。これは、Spring AOPがインターフェイスレベル(MyInterface)で動作し、MyClassメソッドの呼び出しをインターセプトしないためだと考えています。これは正しいです?

Go()トランザクション内で新しいトランザクションを開始する方法はありますか?呼び出す唯一の方法は、anotherREQUIRES_NEWとして設定されたトランザクションを持つSpring管理Beanですか?


更新:クライアントがgo()を実行するときに、クラスではなくインターフェイスへの参照を介して実行することを追加します。

@Autowired
MyInterface impl;

impl.go();
43
Marcus Leon

Springリファレンス2.5から:

プロキシを使用する場合、@Transactional注釈は、パブリック可視性を持つメソッドにのみ適用する必要があります。 @Transactionalアノテーションで保護されたメソッド、プライベートメソッド、またはパッケージに表示されるメソッドにアノテーションを付けた場合、エラーは発生しませんが、アノテーション付きメソッドは構成済みのトランザクション設定を示しません。

そのため、Springは非パブリックメソッドの@Transactionalアノテーションを無視します。

また、

プロキシモード(デフォルト)では、プロキシを介して着信する「外部」メソッド呼び出しのみがインターセプトされます。つまり、呼び出されたメソッドが@Transactionalでマークされていても、「自己呼び出し」、つまりターゲットオブジェクト内のメソッドがターゲットオブジェクトの他のメソッドを呼び出すと、実行時に実際のトランザクションにつながりません。

したがって、メソッドpublicを作成しても、同じクラスのメソッド内からメソッドを呼び出しても、新しいトランザクションは開始されません。

トランザクション設定でaspectjモードを使用すると、トランザクション関連のコードがクラスに組み込まれ、実行時にプロキシが作成されません。

詳細については、 参照ドキュメント を参照してください。

これを行う別の可能な方法は、クラス自体でクラスのスプリングプロキシを取得し、thisではなくそのメソッドを呼び出すことです。

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class SomeService {

    @Autowired
    private ApplicationContext applicationContext;

    private SomeService  getSpringProxy() {
        return applicationContext.getBean(this.getClass());
    }

    private void doSomeAndThenMore() {
        // instead of
        // this.doSometingPublicly();
        // do the following to run in transaction
        getSpringProxy().doSometingPublicly();
    }

    public void doSometingPublicly() {
        //do some transactional stuff here
    }

}
76
Abhinav Sarkar

@Transactionalは、Spring AOPの動作方法により、publicメソッド上にある場合にのみ気付かれます。

ただし、 プログラムで新しいトランザクションを開始する 必要に応じて、TransactionTemplateを使用して、たとえば.

TransactionTemplate txTemplate = new TransactionTemplate(txManager);                
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallback<Object>() {
    public Object doInTransaction(TransactionStatus status) {
        // do stuff
    }
});
42
skaffman

要するに、トランザクションの動作を実現するためにプロキシを介してメソッドを呼び出す必要があります。質問で尋ねられたように、同じBeanで「REQUIRES_NEW」を呼び出すことができます。そのためには、「自己」参照を作成する必要があります。春には簡単ではありません。 @Resourceアノテーションを使用して挿入する必要があります。

@Service("someService")
public class ServieImpl implements Service {

   @Resource(name = "someService")
   Service selfReference;

   @Transactional
   public void firstMethod() {
       selfReference.secondMethod();
   }

   @Transactional(propagation = Propagation.REQUIRES_NEW) 
   public void secondMethod() {    
         //do in new transaction
   }

} 

FirstMethodの呼び出しは、「this」ではなくプロキシを呼び出し、「REQUIRES_NEW」トランザクションを機能させる必要があります。

5
wmlynarski