web-dev-qa-db-fra.com

Utilisation de plusieurs DataSources dans Spring Batch

J'essaie de configurer quelques sources de données dans Spring Batch. Au démarrage, Spring Batch lève l'exception suivante:

To use the default BatchConfigurer the context must contain no more thanone DataSource, found 2

Extrait de la configuration par lots

@Configuration
@EnableBatchProcessing 
public class BatchJobConfiguration {

    @Primary
    @Bean(name = "baseDatasource")
    public DataSource dataSource() {
         // first datasource definition here
    }
    @Bean(name = "secondaryDataSource")
    public DataSource dataSource2() {
         // second datasource definition here
    }
    ...
}

Je ne sais pas pourquoi je vois cette exception, car j'ai vu une configuration basée sur XML pour le lot Spring qui déclare plusieurs sources de données. J'utilise la version 3.0.1.RELEASE du noyau Spring Batch avec la version 1.1.5.RELEASE de Spring Boot. Toute aide serait grandement appréciée.

31
Ahmed Bhaila

AbstractBatchConfiguration essaie de chercher BatchConfigurer dans le conteneur d'abord, s'il n'est pas trouvé, essaie de le créer lui-même - c'est là que IllegalStateException est jeté là où il y a plus d'un DataSource bean dans le conteneur.

L'approche pour résoudre le problème consiste à empêcher la création du bean DefaultBatchConfigurer dans AbstractBatchConfiguration. Pour ce faire, nous vous suggérons de créer DefaultBatchConfigurer par conteneur Spring en utilisant l'annotation @Component :

La classe de configuration où @EnableBatchProcessing est placée, nous pouvons annoter avec @ComponentScan qui scanne le paquet qui contient la classe vide dérivée de DefaultBatchConfigurer:

package batch_config;
...
@EnableBatchProcessing
@ComponentScan(basePackageClasses = MyBatchConfigurer.class)
public class MyBatchConfig {
    ...
}

le code complet de cette classe dérivée vide est ici:

package batch_config.components;
import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
import org.springframework.stereotype.Component;
@Component
public class MyBatchConfigurer extends DefaultBatchConfigurer {
}

Dans cette configuration, l'annotation @Primary fonctionne pour le bean DataSource comme dans l'exemple ci-dessous:

@Configuration
public class BatchTestDatabaseConfig {
    @Bean
    @Primary
    public DataSource dataSource()
    {
        return .........;
    }
}

Cela fonctionne pour la version 3.0.3 de Spring Batch.

La solution la plus simple pour faire une annotation @Primary Sur DataSource fonctionne peut-être simplement en ajoutant @ComponentScan(basePackageClasses = DefaultBatchConfigurer.class) avec l'annotation @EnableBatchProcessing:

@Configuration
@EnableBatchProcessing
@ComponentScan(basePackageClasses = DefaultBatchConfigurer.class)
public class MyBatchConfig {
19
vanarchi

Vous devez fournir votre propre BatchConfigurer. Le printemps ne veut pas prendre cette décision pour vous

@Configuration
@EnableBatchProcessing
public class BatchConfig {

     @Bean
      BatchConfigurer configurer(@Qualifier("batchDataSource") DataSource dataSource){
        return new DefaultBatchConfigurer(dataSource);
      }

...
22
PeterSan

La solution la plus simple consiste à étendre le DefaultBatchConfigurer et à câbler automatiquement votre source de données via un qualificatif:

@Component
public class MyBatchConfigurer extends DefaultBatchConfigurer {

    /**
     * Initialize the BatchConfigurer to use the datasource of your choosing
     * @param firstDataSource
     */
    @Autowired
    public MyBatchConfigurer(@Qualifier("firstDataSource") DataSource firstDataSource) {
        super(firstDataSource);
    }
}

Note latérale (car cela traite également de l'utilisation de plusieurs sources de données): Si vous utilisez la configuration automatique pour exécuter des scripts d'initialisation des données, vous remarquerez peut-être qu'elle ne s'initialise pas sur la source de données que vous attendez. Pour ce problème, jetez un œil à ceci: https://github.com/spring-projects/spring-boot/issues/9528

2
user3474985

Je voudrais fournir une solution ici, qui est très similaire à celle à laquelle répond @vanarchi, mais j'ai réussi à mettre toutes les configurations nécessaires dans une seule classe.

Par souci d'exhaustivité, la solution suppose ici que la source de données principale est hsql.

@Configuration
@EnableBatchProcessing
public class BatchConfiguration extends DefaultBatchConfigurer {

@Bean
@Primary
public DataSource batchDataSource() {

    // no need shutdown, EmbeddedDatabaseFactoryBean will take care of this
    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
    EmbeddedDatabase embeddedDatabase = builder
            .addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql")
            .addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql")
            .setType(EmbeddedDatabaseType.HSQL) //.H2 or .DERBY
            .build();
    return embeddedDatabase;
}

@Override
protected JobRepository createJobRepository() throws Exception {
    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDataSource(batchDataSource());
    factory.setTransactionManager(transactionManager());
    factory.afterPropertiesSet();

    return (JobRepository) factory.getObject();
}

private ResourcelessTransactionManager transactionManager() {
    return new ResourcelessTransactionManager();
}

//NOTE: the code below is just to provide developer an easy way to access the in-momery hsql datasource, as we configured it to the primary datasource to store batch job related data. Default username : sa, password : ''
@PostConstruct
public void getDbManager(){
    DatabaseManagerSwing.main(
            new String[] { "--url", "jdbc:hsqldb:mem:testdb", "--user", "sa", "--password", ""});
}

}

TROIS points clés de cette solution:

  1. Cette classe est annotée avec @EnableBatchProcessing Et @Configuration, Et étendue à partir de DefaultBatchConfigurer. Ce faisant, nous demandons à spring-batch d'utiliser notre configurateur de lots personnalisé lorsque AbstractBatchConfiguration essaie de rechercher BatchConfigurer;
  2. Annotez le bean batchDataSource en tant que @Primary, Qui indique à spring-batch d'utiliser cette source de données comme source de données pour stocker les 9 tables liées au travail.
  3. Remplacez la méthode protected JobRepository createJobRepository() throws Exception, qui oblige le bean jobRepository à utiliser la source de données principale, ainsi qu'à utiliser une instance de transactionManager différente des autres sources de données.
1
imarchuang

Si je peux ajouter à la question ci-dessus, les implications d'avoir 2 contextes de transaction un pour chaque DS. Comment intégrer la transaction XA à l'étape Batch car nous aurions besoin d'assurer la gestion TXN au niveau de l'étape? L'exigence est comme dans une étape de traitement par lots, nous avons besoin des éléments suivants.

  1. lire à partir de DS 1 - jpaItemReader
  2. écrire sur DS2 - JPAItemwriter
  3. lire à partir de DS2 - JPAItemreader
  4. écrire dans Ds1 - JPAItemwriter
  5. Valider tous les txns Étape terminée.
1
saiD

Tout d'abord, créez un BatchConfigurer personnalisé

@Configuration
@Component
public class TwoDataSourcesBatchConfigurer implements BatchConfigurer {

    @Autowired
    @Qualifier("dataSource1")
    DataSource dataSource;

    @Override
    public JobExplorer getJobExplorer() throws Exception {
        ...
    }

    @Override
    public JobLauncher getJobLauncher() throws Exception {
        ...
    }

    @Override
    public JobRepository getJobRepository() throws Exception {
        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        // use the autowired data source
        factory.setDataSource(dataSource);
        factory.setTransactionManager(getTransactionManager());
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    @Override
    public PlatformTransactionManager getTransactionManager() throws Exception                      {
        ...
    }

}

Alors,

@Configuration
@EnableBatchProcessing
@ComponentScan("package")
public class JobConfig {
    // define job, step, ...
}
0
Dayong