Einführung in Spring Batch

1. Einleitung

In diesem Artikel konzentrieren wir uns auf ein praktisches, codeorientiertes Intro zu Spring Batch. Spring Batch ist ein Verarbeitungsframework, das für die robuste Ausführung von Jobs entwickelt wurde.

Es handelt sich um die aktuelle Version 3.0, die Spring 4 und Java 8 unterstützt. Sie enthält auch JSR-352, eine neue Java-Spezifikation für die Stapelverarbeitung.

Hier sind einige interessante und praktische Anwendungsfälle des Frameworks.

2. Workflow-Grundlagen

Spring Batch folgt der traditionellen Batch-Architektur, bei der ein Job-Repository die Arbeit zum Planen und Interagieren mit dem Job übernimmt.

Ein Job kann mehr als einen Schritt haben - und jeder Schritt folgt normalerweise der Reihenfolge des Lesens, Verarbeitens und Schreibens von Daten.

Und natürlich wird das Framework den größten Teil der Arbeit für uns erledigen - insbesondere, wenn es um die geringe Persistenz beim Umgang mit Jobs geht - und SQLite für das Job-Repository verwenden.

2.1. Unser Beispiel Usecase

Der einfache Anwendungsfall, den wir hier angehen werden, ist: Wir werden einige Finanztransaktionsdaten von CSV nach XML migrieren.

Die Eingabedatei hat eine sehr einfache Struktur - sie enthält eine Transaktion pro Zeile, bestehend aus: einem Benutzernamen, der Benutzer-ID, dem Datum der Transaktion und dem Betrag:

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. Der Maven POM

Abhängigkeiten für dieses Projekt erforderlich sind Federkerne, Feder Charge und SQLite jdbc Anschluss:

   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

Als erstes konfigurieren wir Spring Batch mit XML:

Natürlich ist auch eine Java-Konfiguration verfügbar:

@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

Schreiben wir nun unsere Jobbeschreibung für die CSV-zu-XML-Arbeit:

                           com.baeldung.spring_batch_intro.model.Transaction           

Und natürlich die ähnliche Java-basierte Jobkonfiguration:

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, jetzt, da wir die gesamte Konfiguration haben, wollen wir sie aufschlüsseln und anfangen, darüber zu diskutieren.

5.1. Lesen Sie Daten und erstellen Sie Objekte mit ItemReader

Zuerst haben wir den cvsFileItemReader konfiguriert, der die Daten aus der Datei record.csv liest und in das Transaktionsobjekt konvertiert :

@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 + "]"; } }

Dazu wird ein benutzerdefinierter Mapper verwendet:

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. Daten mit ItemProcessor verarbeiten

Wir haben unseren eigenen Artikelprozessor CustomItemProcessor erstellt . Dadurch wird nichts verarbeitet, was mit dem Transaktionsobjekt zusammenhängt. Es wird lediglich das ursprüngliche Objekt vom Leser an den Schreiber übergeben:

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

5.3. Objekte mit ItemWriter in den FS schreiben

Schließlich werden wir diese Transaktion in einer XML-Datei speichern, die sich unter xml / output.xml befindet :

5.4. Stapeljob konfigurieren

Wir müssen also nur die Punkte mit einem Job verbinden - mithilfe der Batch: Job- Syntax.

Beachten Sie die Commit-Intervall - das ist die Anzahl der Transaktionen ist im Speicher gehalten werden , bevor die Charge zur Begehung itemWriter ; Die Transaktionen werden bis zu diesem Zeitpunkt im Speicher gespeichert (oder bis das Ende der Eingabedaten erreicht ist):

5.5. Ausführen des Stapeljobs

Das war's - lassen Sie uns jetzt alles einrichten und ausführen:

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. Fazit

Dieses Tutorial gibt Ihnen eine grundlegende Vorstellung davon, wie Sie mit Spring Batch arbeiten und wie Sie es in einem einfachen Anwendungsfall verwenden.

Es zeigt, wie Sie Ihre Stapelverarbeitungspipeline einfach entwickeln und verschiedene Phasen beim Lesen, Verarbeiten und Schreiben anpassen können.

Die vollständige Implementierung dieses Tutorials finden Sie im Github-Projekt. Dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.