samples - spring batch processing in java
Uso de mĂșltiples DataSources en Spring Batch (6)
Estoy tratando de configurar un par de fuentes de datos dentro de Spring Batch. Al inicio, Spring Batch lanza la siguiente excepción:
To use the default BatchConfigurer the context must contain no more thanone DataSource, found 2
Fragmento de la configuración por lotes
@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
}
...
}
No estoy seguro de por qué estoy viendo esta excepción, porque he visto una configuración basada en xml para el lote de Spring que declara múltiples fuentes de datos. Estoy usando Spring Batch core version 3.0.1.RELEASE con Spring Boot versión 1.1.5.RELEASE. Cualquier ayuda sería muy apreciada.
Debe proporcionar su propio BatchConfigurer. Spring no quiere tomar esa decisión por ti
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Bean
BatchConfigurer configurer(@Qualifier("batchDataSource") DataSource dataSource){
return new DefaultBatchConfigurer(dataSource);
}
...
La solución más simple es extender el DefaultBatchConfigurer y autocablear su fuente de datos a través de un calificador:
@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);
}
}
Nota al margen (ya que esto también trata sobre el uso de varias fuentes de datos): si usa la configuración automática para ejecutar las secuencias de comandos de inicialización de datos, puede notar que no se está inicializando en la fuente de datos que espera. Para ese tema, eche un vistazo a esto: https://github.com/spring-projects/spring-boot/issues/9528
Me gustaría ofrecer una solución aquí, que es muy similar a la respuesta de @vanarchi, pero logré poner todas las configuraciones necesarias en una clase.
En aras de la exhaustividad, la solución aquí supone que el origen de datos primario es 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", ""});
}
}
TRES puntos clave en esta solución:
- Esta clase se anota con
@EnableBatchProcessing
y@Configuration
, y también se extiende desdeDefaultBatchConfigurer
. Al hacer esto, instruimos a spring-batch para que use nuestro configurador por lotes personalizado cuandoAbstractBatchConfiguration
intente buscarBatchConfigurer
; - Anote batchDataSource bean como
@Primary
, que indica a spring-batch que use este origen de datos como fuente de datos para almacenar las 9 tablas relacionadas con el trabajo. - Anular el
protected JobRepository createJobRepository() throws Exception
método deprotected JobRepository createJobRepository() throws Exception
, que hace que el bean jobRepository use el origen de datos primario, así como el uso de una instancia de transactionManager diferente de los otros orígenes de datos.
Primero, cree un BatchConfigurer personalizado
@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 {
...
}
}
Entonces,
@Configuration
@EnableBatchProcessing
@ComponentScan("package")
public class JobConfig {
// define job, step, ...
}
Si puedo agregar a la pregunta anterior, las implicaciones de tener 2 contextos de transacción uno para cada DS. ¿Cómo integrar la transacción XA con el paso por lotes ya que necesitaríamos asegurar la administración de TXN en el nivel de paso? El requisito es que en un paso por lotes necesitamos lo siguiente.
- leer desde DS 1 - jpaItemReader
- escribir en DS2 - JPAItemwriter
- leer desde DS2 - JPAItemreader
- escribir en Ds1 - JPAItemwriter
- Confirmar todos los txns Paso completado.
AbstractBatchConfiguration
intenta buscar el BatchConfigurer
en el contenedor primero; si no se encuentra, intenta crearlo él mismo; aquí es donde se IllegalStateException
donde hay más de un bean de DataSource
en el contenedor.
El enfoque para resolver el problema es evitar la creación del bean DefaultBatchConfigurer
en AbstractBatchConfiguration
. Para hacerlo, @Component
crear DefaultBatchConfigurer
por contenedor Spring utilizando la anotación @Component
:
La clase de configuración donde se coloca @EnableBatchProcessing
podemos anotar con @ComponentScan
que escanea el paquete que contiene la clase vacía que se deriva de DefaultBatchConfigurer
:
package batch_config;
...
@EnableBatchProcessing
@ComponentScan(basePackageClasses = MyBatchConfigurer.class)
public class MyBatchConfig {
...
}
el código completo de esa clase derivada vacía está aquí:
package batch_config.components;
import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
import org.springframework.stereotype.Component;
@Component
public class MyBatchConfigurer extends DefaultBatchConfigurer {
}
En esta configuración, la anotación @Primary
funciona para el bean DataSource
como en el siguiente ejemplo:
@Configuration
public class BatchTestDatabaseConfig {
@Bean
@Primary
public DataSource dataSource()
{
return .........;
}
}
Esto funciona para Spring Batch versión 3.0.3.RELEASE
La solución más simple para realizar la anotación @Primary
en DataSource
podría ser simplemente agregar @ComponentScan(basePackageClasses = DefaultBatchConfigurer.class)
junto con la anotación @EnableBatchProcessing
:
@Configuration
@EnableBatchProcessing
@ComponentScan(basePackageClasses = DefaultBatchConfigurer.class)
public class MyBatchConfig {