web-dev-qa-db-fra.com

Spring @Transactional avec une transaction sur plusieurs sources de données

Je dois mettre à jour deux sources de données dans le cadre d'une transaction. C'est -

  1. Je fais une mise à jour dans DB1.
  2. Ensuite, je fais une autre mise à jour dans DB2.

Si la mise à jour dans DB2 échoue, je souhaite annuler à la fois DB1 et DB2 pour annuler. Cela peut-il être accompli en utilisant @Transactional?

Voici un exemple de code -

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

Mon problème est que, le setRollbackOnly () dans updateDb02 annule uniquement la transaction Db01.

7
Do Will

J'ai résolu ce problème en utilisant ChainedTransactionManager - http://docs.spring.io/spring-data/commons/docs/1.6.2.RELEASE/api/org/springframework/data/transaction/ChainedTransactionManager.html

Configuration de démarrage de printemps:

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

Et puis vous pouvez l'utiliser comme suit:

@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

La meilleure façon est de créer une troisième méthode, qui sera annotée comme @Transactional.

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

Selon une documentation Spring, le contrôle des transactions démarre lorsque la première annotation apparaît, dans ce cas, une seule transaction démarre lorsque updateCommon est invoquée. [~ # ~] mise à jour [~ # ~] Mais cela fonctionnera si vous utilisez CrudRepository ou quelque chose comme ça.

En cas de sources de données multiples, vous pouvez essayer d'utiliser une conception de gestion globale des transactions. Voici un exemple d'une documentation de printemps:

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

Et voici un lien: http://spring.io/blog/2011/08/15/configuring-spring-and-jta-without-full-Java-ee/ Je ne l'ai jamais utilisé pour moi, donc je n'ai pas exploré ce sujet en profondeur. J'espère que cela vous aidera

2
Yuriy Tsarkov

Je crois que vous avez défini vos txns comme ci-dessous.

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

Maintenant, le moyen le plus simple consiste à essayer, intercepter et annuler pour les deux transactions. Mais si vous souhaitez toujours déléguer, il existe une option, comme indiqué ci-dessous.

Créez vos propres méthodes de remplacement et remplacez-les et utilisez-les.

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

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

Et définissez votre propre gestionnaire de transactions comme.

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

}

Ensuite, utilisez-le pour les couches dao où vous voulez travailler de manière synchrone.

 @Transactional("allTransactionManager")

Nous avons donc maintenant vos propres gestionnaires de transactions, qui sont capables d'annuler ou de s'engager ensemble pour les deux types de transactions.

0
surya

Deviner.

Les méthodes doivent être dans des beans différents pour pouvoir utiliser différents gestionnaires de transactions.

0
Do Will