Utilisation de plusieurs sources de données dans Spring Batch

J'essaie de configurer quelques sources de données dans Spring Batch. Au démarrage, Spring Batch lance 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 Spring batch qui déclare plusieurs sources de données. J'utilise Spring Batch core version 3.0.1.Version avec Spring Boot Version 1.1.5.PUBLIER. Toute aide serait grandement apprécier.

23
demandé sur Ahmed Bhaila 2014-08-28 08:00:49

6 réponses

AbstractBatchConfiguration essaie de recherche BatchConfigurer dans le récipient d'abord, si il n'est pas trouvé, alors essaie de créer elle - même- c'est là IllegalStateException est jetée où il n'y a plus d'un DataSource bean dans le conteneur.

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

La classe de configuration où @EnableBatchProcessing est placé nous pouvons annoter avec @ComponentScan qui analyse le paquet contenant 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, le @Primary annotation des œuvres pour DataSource bean comme dans l'exemple ci-dessous:

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

Cela fonctionne pour le Lot de printemps version 3.0.3.Libération

La solution La plus simple pour faire de @Primary annotation sur DataSource travail pourrait être juste l'ajout de @ComponentScan(basePackageClasses = DefaultBatchConfigurer.class) avec @EnableBatchProcessing annotation:

@Configuration
@EnableBatchProcessing
@ComponentScan(basePackageClasses = DefaultBatchConfigurer.class)
public class MyBatchConfig {
13
répondu vanarchi 2017-03-22 14:20:45

Vous devez fournir votre propre BatchConfigurer. Spring 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);
      }

...
16
répondu PeterSan 2015-05-20 09:44:01

Si je puis Ajouter à la question ci-dessus, les implications d'avoir 2 contextes de transaction un pour chaque DS. Comment intégrer la transaction XA avec Batch step comme nous aurions besoin pour assurer la gestion TXN au niveau de l'étape? L'exigence est comme dans une étape de lot, nous avons besoin de ce qui suit.

  1. Lire À partir de DS 1 -- jpaItemReader
  2. écrire dans DS2-JPAItemwriter
  3. Lire À partir de DS2-JPAItemreader
  4. écrire dans Ds1-JPAItemwriter
  5. Commit tous les TXN Étape compléter.
1
répondu saiD 2017-12-25 15:26:04

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                      {
        ...
    }

}

Puis,

@Configuration
@EnableBatchProcessing
@ComponentScan("package")
public class JobConfig {
    // define job, step, ...
}
0
répondu Dayong 2015-03-27 21:39:57

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

Par souci d'exhaustivité, la solution ici suppose 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 dans cette solution:

  1. Cette classe est annotée avec @EnableBatchProcessing et @Configuration, ainsi qu'une extension à partir de DefaultBatchConfigurer. En faisant cela, nous demandons à spring-batch d'utiliser notre configurateur par lots personnalisé lorsque AbstractBatchConfiguration tente de rechercher BatchConfigurer;
  2. annotez le bean batchDataSource en tant que @Primary, qui demande à spring-batch d'utiliser cette source de données comme source de données pour stocker les 9 tables liées au travail.
  3. Override protected JobRepository createJobRepository() throws Exception méthode, qui permet au bean jobRepository d'utiliser la source de données principale, ainsi que d'utiliser une instance transactionManager différente de la ou des autres sources de données.
0
répondu imarchuang 2018-01-01 03:38:08

La solution la plus simple consiste à étendre le DefaultBatchConfigurer et autowire votre source de données via un qualificateur:

@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 concerne également l'utilisation de plusieurs sources de données): si vous utilisez autoconfig pour exécuter des scripts d'initialisation de données, vous remarquerez peut-être qu'il ne s'initialise pas sur la source de données que vous attendez. Pour ce problème, jetez un oeil à ceci: https://github.com/spring-projects/spring-boot/issues/9528

0
répondu user3474985 2018-07-30 21:51:22