Utilizzo di più origini dati in Spring Batch

Sto cercando di configurare un paio di origini dati all’interno di Spring Batch. All’avvio, Spring Batch lancia la seguente eccezione:

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

Snippet dalla configurazione batch

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

Non sono sicuro del motivo per cui vedo questa eccezione, perché ho visto alcune configurazioni basate su XML per il batch di Spring che dichiarano più origini dati. Sto usando la versione core Spring Batch 3.0.1.RELEASE con Spring Boot versione 1.1.5.RELEASE. Qualsiasi aiuto sarebbe molto apprezzato.

AbstractBatchConfiguration tenta di cercare BatchConfigurer prima nel contenitore, se non viene trovato tenta quindi di crearlo da solo – questo è dove viene lanciato IllegalStateException dove c’è più di un bean DataSource nel contenitore.

L’approccio alla risoluzione del problema è impedire la creazione del bean DefaultBatchConfigurer in AbstractBatchConfiguration . Per farlo suggeriamo di creare DefaultBatchConfigurer per container Spring usando @Component annotazione @Component :

La class di configurazione in cui è posizionato @EnableBatchProcessing è in grado di annotare con @ComponentScan che esegue la scansione del pacchetto che contiene la class vuota derivata da DefaultBatchConfigurer :

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

il codice completo di quella class derivata vuota è qui:

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

In questa configurazione l’annotazione @Primary funziona per il bean DataSource come nell’esempio seguente:

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

Questo funziona per la versione Spring Batch 3.0.3.RELEASE

La soluzione più semplice per rendere @Primary annotazione @Primary sul lavoro DataSource potrebbe essere solo aggiungendo @ComponentScan(basePackageClasses = DefaultBatchConfigurer.class) insieme all’annotazione @EnableBatchProcessing :

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

È necessario fornire il proprio BatchConfigurer. La spring non vuole prendere quella decisione per te

 @Configuration @EnableBatchProcessing public class BatchConfig { @Bean BatchConfigurer configurer(@Qualifier("batchDataSource") DataSource dataSource){ return new DefaultBatchConfigurer(dataSource); } ... 

Se posso aggiungere alla domanda di cui sopra, le implicazioni di avere 2 contesti di transazione uno per ogni DS. Come integrare la transazione XA con il passo Batch in quanto dovremmo garantire la gestione TXN a livello di passo? Il requisito è come in una fase batch, abbiamo bisogno di quanto segue.

  1. leggi da DS 1 – jpaItemReader
  2. scrivere su DS2 – JPAItemwriter
  3. leggi da DS2 – JPAItemreader
  4. scrivere su Ds1 – JPAItemwriter
  5. Commit all txns Step completato.

Innanzitutto, crea un BatchConfigurer personalizzato

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

Poi,

 @Configuration @EnableBatchProcessing @ComponentScan("package") public class JobConfig { // define job, step, ... } 

Vorrei fornire una soluzione qui, che è molto simile a quella di @vanarchi, ma sono riuscito a inserire tutte le configurazioni necessarie in un’unica class.

Per completezza, la soluzione qui presuppone che l’origine dati primaria sia 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", ""}); } 

}

TRE punti chiave in questa soluzione:

  1. Questa class è annotata con @EnableBatchProcessing e @Configuration , nonché estesa da DefaultBatchConfigurer . In questo modo, BatchConfigurer spring-batch a utilizzare il nostro configuratore batch personalizzato quando AbstractBatchConfiguration tenta di cercare BatchConfigurer ;
  2. Annotare il bean batchDataSource come @Primary , che indica a spring-batch di utilizzare questa origine dati come origine dati per l’archiviazione delle 9 tabelle correlate al lavoro.
  3. Sovrascrivi protected JobRepository createJobRepository() throws Exception metodo protected JobRepository createJobRepository() throws Exception , che rende il bean JobRepository utilizzare l’origine dati primaria, nonché utilizzare un’istanza transactionManager diversa dalle altre origini dati.

La soluzione più semplice è estendere DefaultBatchConfigurer e autorizzare l’origine dati tramite un qualificatore:

 @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 a margine (poiché si occupa anche dell’utilizzo di più origini dati): se si utilizza autoconfig per eseguire script di inizializzazione dei dati, si potrebbe notare che non si sta inizializzando sull’origine dati che ci si aspetterebbe. Per quel problema, dai un’occhiata a questo: https://github.com/spring-projects/spring-boot/issues/9528