web-dev-qa-db-ja.com

複数のデータソースにまたがるトランザクションを伴うSpring @Transactional

1つのトランザクションの一部として2つのデータソースを更新する必要があります。あれは -

  1. DB1で更新を行います。
  2. 次に、DB2で別の更新を行います。

DB2での更新が失敗した場合、DB1とDB2の両方をロールバックしてロールバックしたいと思います。これは@Transactionalを使用して実現できますか?

これがサンプルコードです-

@Transactional(value="db01TransactionManager")
public void updateDb01() {
    Entity01 entity01 = repository01.findOne(1234);
    entity01.setName("Name");
    repository01.save(entity01);

    //Calling method to update DB02
    updateDb02();
}

@Transactional(value="db02TransactionManager")
public void updateDb02() {
    Entity02 entity02 = repository02.findOne(1234);
    entity02.setName("Name");
    repository02.save(entity02);

    //Added this to force a roll back for testing
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}

私の問題は、updateDb02のsetRollbackOnly()がDb01トランザクションのみをロールバックすることです。

7
Do Will

ChainedTransactionManagerを使用してこの問題を解決しました- http://docs.spring.io/spring-data/commons/docs/1.6.2.RELEASE/api/org/springframework/data/transaction/ChainedTransactionManager.html

スプリングブート構成:

    @Bean(name = "chainedTransactionManager")
    public ChainedTransactionManager transactionManager(@Qualifier("primaryDs") PlatformTransactionManager ds1,
                                                    @Qualifier("secondaryDs") PlatformTransactionManager ds2) {
         return new ChainedTransactionManager(ds1, ds2);
    }

そして、次のように使用できます。

@Transactional(value="chainedTransactionManager")
public void updateDb01() {
    Entity01 entity01 = repository01.findOne(1234);
    entity01.setName("Name");
    repository01.save(entity01);

    //Calling method to update DB02
    updateDb02();
}

public void updateDb02() {
    Entity02 entity02 = repository02.findOne(1234);
    entity02.setName("Name");
    repository02.save(entity02);

    //Added this to force a roll back for testing
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
19
kolmant

最良の方法は、@Transactionalとして注釈が付けられる3番目のメソッドを作成することです。

@Transactional(readOnly = false)
public void updateCommon(){
  upbateDb01();
  upbateDb02();
}

春のドキュメントによると、トランザクションコントロールは最初のアノテーションが表示されたときに開始されるため、この場合、updateCommonが呼び出されたときに単一のトランザクションが開始されます。 [〜#〜] update [〜#〜]しかし、これはCrudRepositoryまたはそのようなものを使用する場合に機能します。

複数のデータソースがある場合は、グローバルトランザクション管理の概念を使用してみてください。これは春のドキュメントからのサンプルです:

@Inject private PlatformTransactionManager txManager; 

TransactionTemplate template  = new TransactionTemplate(this.txManager); 
template.execute( new TransactionCallback<Object>(){ 
  public void doInTransaction(TransactionStatus status){ 
   // work done here will be wrapped by a transaction and committed. 
   // the transaction will be rolled back if 
   // status.setRollbackOnly(true) is called or an exception is thrown 
  } 
});

そしてここにリンクがあります: http://spring.io/blog/2011/08/15/configuring-spring-and-jta-without-full-Java-ee/ 使用したことがありません私自身のため、このトピックを深く探求しませんでした。それが役に立てば幸い

2
Yuriy Tsarkov

私はあなたがあなたのtxnsを以下のように定義したと信じています。

@Bean(name="db01TransactionManager") 
@Autowired
DataSourceTransactionManager tm1(@Qualifier ("datasource1") DataSource datasource) {
    DataSourceTransactionManager txm  = new DataSourceTransactionManager(datasource);
    return txm;
}

@Bean(name="db02TransactionManager") 
@Autowired
DataSourceTransactionManager tm2(@Qualifier ("datasource2") DataSource datasource) {
    DataSourceTransactionManager txm  = new DataSourceTransactionManager(datasource);
    return txm;
}

現在、最も簡単な方法は、両方のトランザクションを試行、キャッチ、およびロールバックすることです。ただし、委任したい場合は、以下のオプションがあります。

独自のロールバックメソッドを作成してオーバーライドし、使用します。

@Bean(name=“allTransactionManager") 
@Autowired
DataSourceTransactionManager tm2(@Qualifier ("datasource1”) DataSource datasource1, @Qualifier ("datasource2") DataSource datasource2) {

    DataSourceTransactionManager txm  = new MyDataSourceTransactionManager(datasource1,datasouce2);
        return txm;
}

そして、独自のトランザクションマネージャーを次のように定義します。

MyDataSourceTransactionManager extends DataSourceTransactionManager{
DataSourceTransactionManager tm1; 
DataSourceTransactionManager tm2; 

MyDataSourceTransactionManager(Datasource ds1,Datasource d2){
  tm1 = new DataSourceTransactionManager(DataSource);
  tm2 =new DataSourceTransactionManager(DataSource);
}
// override and for roll back, rollback for both of tm1 and tm2. Thus all actions are delegated in this class

}

次に、同期的に動作させたい場所であれば、DAOレイヤーにこれを使用します。

 @Transactional("allTransactionManager")

これで、両方のタイプのトランザクションに対してロールバックまたはコミットできる独自のトランザクションマネージャができました。

0
surya

理解した。

異なるトランザクションマネージャを使用できるようにするには、メソッドを異なるBeanにする必要があります。

0
Do Will