Use of multiple DataSources in Spring Batch
You must provide your own BatchConfigurer. Spring does not want to make that decision for you
@Configuration@EnableBatchProcessingpublic class BatchConfig { @Bean BatchConfigurer configurer(@Qualifier("batchDataSource") DataSource dataSource){ return new DefaultBatchConfigurer(dataSource); }...
AbstractBatchConfiguration
tries to lookup BatchConfigurer
in container first, if it is not found then tries to create it itself - this is where IllegalStateException
is thrown where there is more than one DataSource
bean in container.
The approach to solving the problem is to prevent from creation the DefaultBatchConfigurer
bean in AbstractBatchConfiguration
.To do it we hint to create DefaultBatchConfigurer
by Spring container using @Component
annotation:
The configuration class where @EnableBatchProcessing
is placed we can annotate with @ComponentScan
that scan the package that contains the empty class that is derived from DefaultBatchConfigurer
:
package batch_config;...@EnableBatchProcessing@ComponentScan(basePackageClasses = MyBatchConfigurer.class)public class MyBatchConfig { ...}
the full code of that empty derived class is here:
package batch_config.components;import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;import org.springframework.stereotype.Component;@Componentpublic class MyBatchConfigurer extends DefaultBatchConfigurer {}
In this configuration the @Primary
annotation works for DataSource
bean as in the example below:
@Configurationpublic class BatchTestDatabaseConfig { @Bean @Primary public DataSource dataSource() { return .........; }}
This works for the Spring Batch version 3.0.3.RELEASE
The simplest solution to make @Primary
annotation on DataSource
work might be just adding @ComponentScan(basePackageClasses = DefaultBatchConfigurer.class)
along with @EnableBatchProcessing
annotation:
@Configuration@EnableBatchProcessing@ComponentScan(basePackageClasses = DefaultBatchConfigurer.class)public class MyBatchConfig {
I would like to provide a solution here, which is very similar to the one answered by @vanarchi, but I managed to put all the necessary configurations into one class.
For the sake of completeness, the solution here assumes that primary datasource is hsql.
@Configuration@EnableBatchProcessingpublic class BatchConfiguration extends DefaultBatchConfigurer {@Bean@Primarypublic 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;}@Overrideprotected 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 : ''@PostConstructpublic void getDbManager(){ DatabaseManagerSwing.main( new String[] { "--url", "jdbc:hsqldb:mem:testdb", "--user", "sa", "--password", ""});}
}
THREE key points in this solution:
- This class is annotated with
@EnableBatchProcessing
and@Configuration
, as well as extended fromDefaultBatchConfigurer
. By doing this, we instruct spring-batch to use our customized batch configurer whenAbstractBatchConfiguration
tries to lookupBatchConfigurer
; - Annotate batchDataSource bean as
@Primary
, which instruct spring-batch to use this datasource as its datasource of storing the 9 job related tables. - Override
protected JobRepository createJobRepository() throws Exception
method, which makes the jobRepository bean to use the primary datasource, as well as use a different transactionManager instance from the other datasource(s).