Spring @Transactional с транзакцией через несколько источников данных
Я должен обновить два источника данных как часть одной транзакции. То есть -
- Я делаю обновление в DB1.
- Затем я делаю еще одно обновление в 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")
Так что теперь у нас есть собственные менеджеры транзакций, которые могут выполнять откат или фиксацию вместе для обоих типов транзакций.