Spring @Transactional с транзакцией через несколько источников данных

Я должен обновить два источника данных как часть одной транзакции. То есть -

  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();
}

Моя проблема в том, что setRollbackOnly() в updateDb02 откатывает только транзакцию Db01.

4 ответа

Я решил эту проблему с помощью ChainedTransactionManager - http://docs.spring.io/spring-data/commons/docs/1.6.2.RELEASE/api/org/springframework/data/transaction/ChainedTransactionManager.html

Конфигурация Spring Boot:

    @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();
}

Лучший способ - создать третий метод, который будет аннотирован как @Transactional,

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

Согласно весенней документации, управление транзакцией начинается, когда появляется первая аннотация, поэтому в этом случае одна транзакция начнется, когда updateCommon будет вызван.ОБНОВЛЕНИЕ Но это будет работать, если вы используете 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/ Я никогда не использовал его для себя, поэтому я не не исследовать эту тему глубоко. Надеюсь, это поможет

Догадаться.

Методы должны быть в разных компонентах, чтобы иметь возможность использовать разные менеджеры транзакций.

Я считаю, что вы определили свои 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

}

Затем используйте это для слоев дао, где вы хотите работать синхронно.

 @Transactional("allTransactionManager")

Так что теперь у нас есть собственные менеджеры транзакций, которые могут выполнять откат или фиксацию вместе для обоих типов транзакций.

Другие вопросы по тегам