tutorial samples framework fieldsetmapper ejemplo batch baeldung java spring datasource spring-batch

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:

  1. Esta clase se anota con @EnableBatchProcessing y @Configuration , y también se extiende desde DefaultBatchConfigurer . Al hacer esto, instruimos a spring-batch para que use nuestro configurador por lotes personalizado cuando AbstractBatchConfiguration intente buscar BatchConfigurer ;
  2. 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.
  3. Anular el protected JobRepository createJobRepository() throws Exception método de protected 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.

  1. leer desde DS 1 - jpaItemReader
  2. escribir en DS2 - JPAItemwriter
  3. leer desde DS2 - JPAItemreader
  4. escribir en Ds1 - JPAItemwriter
  5. 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 {