Использование нескольких источников данных в Spring Batch

Я пытаюсь настроить пару источников данных в Spring Batch. При запуске Spring Batch выдает следующее исключение:

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

Фрагмент из пакетной конфигурации

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

Не уверен, почему я вижу это исключение, потому что я видел некоторые основанные на XML конфигурации для пакета Spring, которые объявляют несколько источников данных. Я использую ядро ​​Spring Batch версии 3.0.1.RELEASE с версией Spring Boot 1.1.5.RELEASE. Любая помощь будет принята с благодарностью.

8 ответов

Решение

AbstractBatchConfiguration пытается искать BatchConfigurer сначала в контейнере, если он не найден, то пытается создать его сам - это где IllegalStateException брошен там, где есть более одного DataSource фасоль в контейнере.

Подход к решению проблемы заключается в предотвращении создания DefaultBatchConfigurer боб в AbstractBatchConfiguration, Чтобы сделать это, мы намекаем на создание DefaultBatchConfigurer с помощью контейнера Spring @Component аннотация:

Класс конфигурации, где @EnableBatchProcessing размещен мы можем комментировать @ComponentScan которые сканируют пакет, который содержит пустой класс, производный от DefaultBatchConfigurer:

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

полный код этого пустого производного класса находится здесь:

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

В этой конфигурации @Primary аннотация работает для DataSource боб, как в примере ниже:

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

Это работает для Spring Batch версии 3.0.3.

Самое простое решение сделать @Primary аннотация на DataSource работа может быть просто добавлением @ComponentScan(basePackageClasses = DefaultBatchConfigurer.class) вместе с @EnableBatchProcessing аннотация:

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

Вы должны предоставить свой собственный BatchConfigurer. Весна не хочет принимать это решение за вас

@Configuration
@EnableBatchProcessing
public class BatchConfig {

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

...

Я хотел бы предложить здесь решение, очень похожее на то, на которое ответил @vanarchi, но мне удалось собрать все необходимые конфигурации в одном классе.

Для полноты картины в данном решении предполагается, что основным источником данных является 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", ""});
}

}

ТРИ ключевых момента в этом решении:

  1. Этот класс отмечен @EnableBatchProcessing а также @Configuration, а также продлен от DefaultBatchConfigurer, Делая это, мы инструктируем Spring-Batch использовать наш индивидуальный пакетный конфигуратор, когда AbstractBatchConfiguration пытается искать BatchConfigurer;
  2. Аннотировать компонент batchDataSource как @Primary, который инструктирует Spring-Batch использовать этот источник данных в качестве источника данных для хранения 9 таблиц, связанных с заданиями.
  3. Override protected JobRepository createJobRepository() throws Exception метод, который заставляет bean-компонент jobRepository использовать первичный источник данных, а также использовать другой экземпляр TransactionsManager из других источников данных.

Самое простое решение - расширить DefaultBatchConfigurer и автоматически связать ваш источник данных с помощью квалификатора:

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

Примечание (поскольку это также касается использования нескольких источников данных): если вы используете autoconfig для запуска сценариев инициализации данных, вы можете заметить, что он не инициализируется в ожидаемом источнике данных. Для решения этой проблемы посмотрите на это: https://github.com/spring-projects/spring-boot/issues/9528

Вы можете определить компоненты ниже и убедиться, что в файле application.properties есть записи, необходимые для

@Configuration
@PropertySource("classpath:application.properties")
public class DataSourceConfig {

    @Primary
    @Bean(name = "abcDataSource")
    @ConfigurationProperties(prefix = "abc.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }


    @Bean(name = "xyzDataSource")
    @ConfigurationProperties(prefix = "xyz.datasource")
    public DataSource xyzDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }
}

application.properties

abc.datasource.jdbc-url=XXXXX
abc.datasource.username=XXXXX
abc.datasource.password=xxxxx
abc.datasource.driver-class-name=org.postgresql.Driver

...........
...........
...........
...........

Здесь вы можете обратиться к: Настройка загрузки Spring и использование двух источников данных

Если я могу добавить к вышеупомянутому вопросу, последствия наличия двух контекстов транзакции один для каждого DS. Как интегрировать транзакцию XA с Batch step, поскольку нам необходимо обеспечить управление TXN на уровне шага? Требование, как на шаге партии, нам нужно следующее.

  1. читать из DS 1 - jpaItemReader
  2. написать в DS2 - JPAItemwriter
  3. читать из DS2 - JPAItemreader
  4. написать в Ds1 - JPAItemwriter
  5. Совершить все txns Шаг завершен.

Сначала создайте пользовательский BatchConfigurer

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

}

Затем,

@Configuration
@EnableBatchProcessing
@ComponentScan("package")
public class JobConfig {
    // define job, step, ...
}
      @Configuration
public class BathConfiguration extends DefaultTaskConfigurer {
    public BathConfiguration(@Qualifier("applicationDataSource") DataSource dataSource) {
        super(dataSource);
    }
}

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