Использование нескольких источников данных в 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", ""});
}
}
ТРИ ключевых момента в этом решении:
- Этот класс отмечен
@EnableBatchProcessing
а также@Configuration
, а также продлен отDefaultBatchConfigurer
, Делая это, мы инструктируем Spring-Batch использовать наш индивидуальный пакетный конфигуратор, когдаAbstractBatchConfiguration
пытается искатьBatchConfigurer
; - Аннотировать компонент batchDataSource как
@Primary
, который инструктирует Spring-Batch использовать этот источник данных в качестве источника данных для хранения 9 таблиц, связанных с заданиями. - 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 на уровне шага? Требование, как на шаге партии, нам нужно следующее.
- читать из DS 1 - jpaItemReader
- написать в DS2 - JPAItemwriter
- читать из DS2 - JPAItemreader
- написать в Ds1 - JPAItemwriter
- Совершить все 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);
}
}