Einführung in Spring Batch
1. Einführung
In diesem Artikel konzentrieren wir uns auf eine praktische, codeorientierte Einführung in Spring Batch. Spring Batch ist ein Verarbeitungsframework für die robuste Ausführung von Jobs.
Es ist die aktuelle Version 3.0, die Spring 4 und Java 8 unterstützt. Es unterstützt auch JSR-352, eine neue Java-Spezifikation für die Stapelverarbeitung.
Here areind einige interessante und praktische Anwendungsfälle des Frameworks.
2. Workflow-Grundlagen
Spring Batch folgt der traditionellen Batch-Architektur, bei der ein Job-Repository für die Planung und Interaktion mit dem Job zuständig ist.
Ein Job kann mehr als einen Schritt haben - und jeder Schritt folgt normalerweise der Abfolge des Lesens, Verarbeitens und Schreibens von Daten.
Und natürlich wird das Framework hier den größten Teil der Arbeit für uns erledigen - insbesondere wenn es um die Persistenzarbeit auf niedriger Ebene beim Umgang mit Jobs geht - undsqlite 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
Für dieses Projekt erforderliche Abhängigkeiten sind Federkern, Federbatch undsqlite JDBC-Anschluss:
4.0.0
org.example
spring-batch-intro
0.1-SNAPSHOT
jar
spring-batch-intro
http://maven.apache.org
UTF-8
4.2.0.RELEASE
3.0.5.RELEASE
3.8.11.2
org.xerial
sqlite-jdbc
${sqlite.version}
org.springframework
spring-oxm
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework.batch
spring-batch-core
${spring.batch.version}
4. Spring Batch Config
Als erstes konfigurieren wir Spring Batch mit XML:
Natürlich steht auch eine Java-Konfiguration zur Verfügung:
@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:
org.example.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 mit der Diskussion beginnen.
5.1. Lesen Sie Daten und erstellen Sie Objekte mitItemReader
Zuerst haben wir diecvsFileItemReader konfiguriert, die die Daten aus denrecord.csv lesen und in dasTransaction-Objekt konvertieren:
@SuppressWarnings("restriction")
@XmlRootElement(name = "transactionRecord")
public class Transaction {
private String username;
private int userId;
private Date 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 {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
Transaction transaction = new Transaction();
transaction.setUsername(fieldSet.readString("username"));
transaction.setUserId(fieldSet.readInt(1));
transaction.setAmount(fieldSet.readDouble(3));
String dateString = fieldSet.readString(2);
try {
transaction.setTransactionDate(dateFormat.parse(dateString));
} catch (ParseException e) {
e.printStackTrace();
}
return transaction;
}
}
5.2. Daten mitItemProcessor verarbeiten
Wir haben unseren eigenen ArtikelprozessorCustomItemProcessor 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. Schreiben von Objekten in den FS mitItemWriter
Schließlich werden wir diesetransaction in einer XML-Datei speichern, die sich unterxml/output.xml befindet:
5.4. Stapeljob konfigurieren
Alles was wir tun müssen, ist die Punkte mit einem Job zu verbinden - unter Verwendung derbatch:job-Syntax.
Beachten Sie diecommit-interval - das ist die Anzahl der Transaktionen, die gespeichert werden müssen, bevor der Stapel anitemWriter übergeben wird. 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 - jetzt richten wir alles ein und führen es aus:
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 vonhow to work with Spring Batch und wie Sie es in einem einfachen Anwendungsfall verwenden können.
Es wird gezeigt, wie Sie Ihre Stapelverarbeitungs-Pipeline auf einfache Weise entwickeln und wie Sie verschiedene Phasen des Lesens, Verarbeitens und Schreibens anpassen können.
Diefull implementation dieses Tutorials finden Sie inthe github project - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.