Introduzione a Spring Batch

1. Introduzione

In questo articolo ci concentreremo su un'introduzione pratica e incentrata sul codice di Spring Batch. Spring Batch è un framework di elaborazione progettato per un'esecuzione robusta dei lavori.

È la versione corrente 3.0, che supporta Spring 4 e Java 8. Supporta anche JSR-352, che è una nuova specifica Java per l'elaborazione batch.

Ecco alcuni casi d'uso interessanti e pratici del framework.

2. Nozioni di base sul flusso di lavoro

Il batch Spring segue l'architettura batch tradizionale in cui un repository di lavori svolge il lavoro di pianificazione e interazione con il lavoro.

Un lavoro può avere più passaggi e ogni passaggio segue tipicamente la sequenza di lettura dei dati, elaborazione e scrittura.

E ovviamente il framework farà la maggior parte del lavoro pesante per noi qui, specialmente quando si tratta del lavoro di persistenza di basso livello per gestire i lavori, usando sqlite per il repository dei lavori.

2.1. Il nostro caso d'uso di esempio

Il semplice caso d'uso che affronteremo qui è: migreremo alcuni dati di transazioni finanziarie da CSV a XML.

Il file di input ha una struttura molto semplice: contiene una transazione per riga, composta da: un nome utente, l'ID utente, la data della transazione e l'importo:

username, userid, transaction_date, transaction_amount devendra, 1234, 31/10/2015, 10000 john, 2134, 3/12/2015, 12321 robin, 2134, 2/02/2015, 23411

3. Il Maven POM

Le dipendenze richieste per questo progetto sono spring core, spring batch e sqlite jdbc connector:

   org.xerial sqlite-jdbc 3.15.1   org.springframework spring-oxm 5.2.0.RELEASE   org.springframework spring-jdbc 5.2.0.RELEASE   org.springframework.batch spring-batch-core 4.2.0.RELEASE 

4. Spring Batch Config

La prima cosa che faremo è configurare Spring Batch con XML:

Ovviamente è disponibile anche una configurazione Java:

@Configuration @EnableBatchProcessing public class SpringConfig { @Value("org/springframework/batch/core/schema-drop-sqlite.sql") private Resource dropReopsitoryTables; @Value("org/springframework/batch/core/schema-sqlite.sql") private Resource dataReopsitorySchema; @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.sqlite.JDBC"); dataSource.setUrl("jdbc:sqlite:repository.sqlite"); return dataSource; } @Bean public DataSourceInitializer dataSourceInitializer(DataSource dataSource) throws MalformedURLException { ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); databasePopulator.addScript(dropReopsitoryTables); databasePopulator.addScript(dataReopsitorySchema); databasePopulator.setIgnoreFailedDrops(true); DataSourceInitializer initializer = new DataSourceInitializer(); initializer.setDataSource(dataSource); initializer.setDatabasePopulator(databasePopulator); return initializer; } private JobRepository getJobRepository() throws Exception { JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean(); factory.setDataSource(dataSource()); factory.setTransactionManager(getTransactionManager()); factory.afterPropertiesSet(); return (JobRepository) factory.getObject(); } private PlatformTransactionManager getTransactionManager() { return new ResourcelessTransactionManager(); } public JobLauncher getJobLauncher() throws Exception { SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); jobLauncher.setJobRepository(getJobRepository()); jobLauncher.afterPropertiesSet(); return jobLauncher; } }

5. Spring Batch Job Config

Scriviamo ora la nostra descrizione del lavoro per il lavoro da CSV a XML:

                           com.baeldung.spring_batch_intro.model.Transaction           

E, naturalmente, la simile configurazione del lavoro basata su Java:

public class SpringBatchConfig { @Autowired private JobBuilderFactory jobs; @Autowired private StepBuilderFactory steps; @Value("input/record.csv") private Resource inputCsv; @Value("file:xml/output.xml") private Resource outputXml; @Bean public ItemReader itemReader() throws UnexpectedInputException, ParseException { FlatFileItemReader reader = new FlatFileItemReader(); DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer(); String[] tokens = { "username", "userid", "transactiondate", "amount" }; tokenizer.setNames(tokens); reader.setResource(inputCsv); DefaultLineMapper lineMapper = new DefaultLineMapper(); lineMapper.setLineTokenizer(tokenizer); lineMapper.setFieldSetMapper(new RecordFieldSetMapper()); reader.setLineMapper(lineMapper); return reader; } @Bean public ItemProcessor itemProcessor() { return new CustomItemProcessor(); } @Bean public ItemWriter itemWriter(Marshaller marshaller) throws MalformedURLException { StaxEventItemWriter itemWriter = new StaxEventItemWriter(); itemWriter.setMarshaller(marshaller); itemWriter.setRootTagName("transactionRecord"); itemWriter.setResource(outputXml); return itemWriter; } @Bean public Marshaller marshaller() { Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); marshaller.setClassesToBeBound(new Class[] { Transaction.class }); return marshaller; } @Bean protected Step step1(ItemReader reader, ItemProcessor processor, ItemWriter writer) { return steps.get("step1"). chunk(10) .reader(reader).processor(processor).writer(writer).build(); } @Bean(name = "firstBatchJob") public Job job(@Qualifier("step1") Step step1) { return jobs.get("firstBatchJob").start(step1).build(); } }

OK, quindi ora che abbiamo l'intera configurazione, analizziamola e iniziamo a discuterne.

5.1. Leggi i dati e crea oggetti con ItemReader

Per prima cosa abbiamo configurato cvsFileItemReader che leggerà i dati dal record.csv e li convertirà nell'oggetto Transaction :

@SuppressWarnings("restriction") @XmlRootElement(name = "transactionRecord") public class Transaction { private String username; private int userId; private LocalDateTime transactionDate; private double amount; /* getters and setters for the attributes */ @Override public String toString() { return "Transaction [username=" + username + ", userId=" + userId + ", transactionDate=" + transactionDate + ", amount=" + amount + "]"; } }

Per farlo, utilizza un mappatore personalizzato:

public class RecordFieldSetMapper implements FieldSetMapper { public Transaction mapFieldSet(FieldSet fieldSet) throws BindException { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyy"); Transaction transaction = new Transaction(); transaction.setUsername(fieldSet.readString("username")); transaction.setUserId(fieldSet.readInt(1)); transaction.setAmount(fieldSet.readDouble(3)); String dateString = fieldSet.readString(2); transaction.setTransactionDate(LocalDate.parse(dateString, formatter).atStartOfDay()); return transaction; } }

5.2. Elaborazione dei dati con ItemProcessor

Abbiamo creato il nostro elaboratore di articoli, CustomItemProcessor . Questo non elabora nulla relativo all'oggetto della transazione, tutto ciò che fa è passare l'oggetto originale proveniente dal lettore allo scrittore:

public class CustomItemProcessor implements ItemProcessor { public Transaction process(Transaction item) { return item; } }

5.3. Scrittura di oggetti in FS con ItemWriter

Infine, memorizzeremo questa transazione in un file xml situato in xml / output.xml :

5.4. Configurazione del lavoro batch

Quindi tutto ciò che dobbiamo fare è collegare i punti con un lavoro, utilizzando la sintassi batch: job .

Notare l' intervallo di commit : è il numero di transazioni da tenere in memoria prima di eseguire il commit del batch su itemWriter ; manterrà le transazioni in memoria fino a quel punto (o fino a quando non si incontra la fine dei dati di input):

5.5. Esecuzione del lavoro batch

Ecco fatto - ora configuriamo ed eseguiamo tutto:

public class App { public static void main(String[] args) { // Spring Java config AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(SpringConfig.class); context.register(SpringBatchConfig.class); context.refresh(); JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher"); Job job = (Job) context.getBean("firstBatchJob"); System.out.println("Starting the batch job"); try { JobExecution execution = jobLauncher.run(job, new JobParameters()); System.out.println("Job Status : " + execution.getStatus()); System.out.println("Job completed"); } catch (Exception e) { e.printStackTrace(); System.out.println("Job failed"); } } }

6. Conclusione

Questo tutorial ti dà un'idea di base di come lavorare con Spring Batch e come usarlo in un semplice caso d'uso.

Mostra come sviluppare facilmente la pipeline di elaborazione batch e come personalizzare le diverse fasi di lettura, elaborazione e scrittura.

L' implementazione completa di questo tutorial può essere trovata nel progetto github: questo è un progetto basato su Eclipse, quindi dovrebbe essere facile da importare ed eseguire così com'è.