Welkom bij het Spring Batch Voorbeeld. Spring Batch is een module van het Spring Framework voor de uitvoering van batchtaken. We kunnen Spring Batch gebruiken om een reeks taken te verwerken.
Spring Batch Voorbeeld
Voordat we het Spring Batch-voorbeeldprogramma doornemen, laten we eerst wat ideeën over Spring Batch-terminologieën opdoen.
- A job can consist of ‘n’ number of steps. Each step contains Read-Process-Write task or it can have single operation, which is called tasklet.
- Read-Process-Write houdt in dat je gegevens leest van een bron zoals een database, CSV enzovoort, de gegevens verwerkt en deze vervolgens schrijft naar een bron zoals een database, CSV, XML enzovoort.
- Een taaklet betekent het uitvoeren van een enkele taak of bewerking, zoals het schoonmaken van verbindingen, het vrijmaken van middelen nadat de verwerking is voltooid.
- Read-Process-Write en taaklets kunnen aan elkaar worden gekoppeld om een taak uit te voeren.
Spring Batch Voorbeeld
Laten we een werkend voorbeeld bekijken voor de implementatie van Spring Batch. We zullen het volgende scenario overwegen voor implementatiedoeleinden. Een CSV-bestand met gegevens moet worden geconverteerd naar XML samen met de gegevens en de tags worden genoemd naar de kolomnaam. Hieronder staan de belangrijke tools en bibliotheken die worden gebruikt voor het voorbeeld van Spring Batch.
- Apache Maven 3.5.0 – voor projectopbouw en afhankelijkheidsbeheer.
- Eclipse Oxygen Release 4.7.0 – IDE voor het maken van een spring batch maven-toepassing.
- Java 1.8
- Spring Core 4.3.12.RELEASE
- Spring OXM 4.3.12.RELEASE
- Spring JDBC 4.3.12.RELEASE
- Spring Batch 3.0.8.RELEASE
- MySQL Java Driver 5.1.25 – gebruik op basis van uw MySQL-installatie. Dit is vereist voor Spring Batch metagegevenstabellen.
Mappenstructuur voor Spring Batch-voorbeeld
Onderstaande afbeelding illustreert alle componenten in ons Spring Batch-voorbeeldproject.
Spring Batch Maven-afhankelijkheden
Hieronder staat de inhoud van het pom.xml-bestand met alle benodigde afhankelijkheden voor ons Spring Batch-voorbeeldproject.
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.spring</groupId>
<artifactId>SpringBatchExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBatchDemo</name>
<url>https://maven.apache.org</url>
<properties>
<jdk.version>1.8</jdk.version>
<spring.version>4.3.12.RELEASE</spring.version>
<spring.batch.version>3.0.8.RELEASE</spring.batch.version>
<mysql.driver.version>5.1.25</mysql.driver.version>
<junit.version>4.11</junit.version>
</properties>
<dependencies>
<!-- Spring Core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring jdbc, for database -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring XML to/back object -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- MySQL database driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.driver.version}</version>
</dependency>
<!-- Spring Batch dependencies -->
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
<version>${spring.batch.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-infrastructure</artifactId>
<version>${spring.batch.version}</version>
</dependency>
<!-- Spring Batch unit test -->
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<version>${spring.batch.version}</version>
</dependency>
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.10</version>
</dependency>
</dependencies>
<build>
<finalName>spring-batch</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Spring Batch Verwerking CSV-Invoerbestand
Hier is de inhoud van ons voorbeeld CSV-bestand voor spring batch verwerking.
1001,Tom,Moody, 29/7/2013
1002,John,Parker, 30/7/2013
1003,Henry,Williams, 31/7/2013
Spring Batch Taakconfiguratie
We moeten een lenteboon en lente batchtaak definiëren in een configuratiebestand. Hieronder staat de inhoud van het bestand job-batch-demo.xml
, het is het belangrijkste onderdeel van het spring batch-project.
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:batch="https://www.springframework.org/schema/batch" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/batch
https://www.springframework.org/schema/batch/spring-batch-3.0.xsd
https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
">
<import resource="../config/context.xml" />
<import resource="../config/database.xml" />
<bean id="report" class="com.journaldev.spring.model.Report"
scope="prototype" />
<bean id="itemProcessor" class="com.journaldev.spring.CustomItemProcessor" />
<batch:job id="DemoJobXMLWriter">
<batch:step id="step1">
<batch:tasklet>
<batch:chunk reader="csvFileItemReader" writer="xmlItemWriter"
processor="itemProcessor" commit-interval="10">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="csvFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="classpath:csv/input/report.csv" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="id,firstname,lastname,dob" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="com.journaldev.spring.ReportFieldSetMapper" />
<!-- if no data type conversion, use BeanWrapperFieldSetMapper to map
by name <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="report" /> </bean> -->
</property>
</bean>
</property>
</bean>
<bean id="xmlItemWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter">
<property name="resource" value="file:xml/outputs/report.xml" />
<property name="marshaller" ref="reportMarshaller" />
<property name="rootTagName" value="report" />
</bean>
<bean id="reportMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.journaldev.spring.model.Report</value>
</list>
</property>
</bean>
</beans>
- We gebruiken
FlatFileItemReader
om het CSV-bestand te lezen,CustomItemProcessor
om de gegevens te verwerken en naar een XML-bestand te schrijven met behulp vanStaxEventItemWriter
. batch:job
– Deze tag definieert de taak die we willen maken. De eigenschap Id geeft de ID van de taak aan. We kunnen meerdere taken definiëren in één xml-bestand.- batch:step – Deze tag wordt gebruikt om verschillende stappen van een lente batchtaak te definiëren.
- Er worden twee verschillende verwerkingsstijlen aangeboden door het Spring Batch Framework, namelijk “Taakstap-georiënteerd” en “Chunk-georiënteerd”. De Chunk-georiënteerde stijl die in dit voorbeeld wordt gebruikt, verwijst naar het één voor één lezen van de gegevens en het maken van ‘chunks’ die zullen worden geschreven binnen een transactiegrens.
- lezer: Spring bean gebruikt voor het lezen van de gegevens. We hebben in dit voorbeeld de bean
csvFileItemReader
gebruikt, die een instantie is vanFlatFileItemReader
. - processor: dit is de klasse die wordt gebruikt voor het verwerken van de gegevens. We hebben in dit voorbeeld de
CustomItemProcessor
gebruikt. - schrijver: bean gebruikt voor het schrijven van gegevens naar een xml-bestand.
- commit-interval: Deze eigenschap definieert de grootte van de chunk die wordt bevestigd zodra de verwerking is voltooid. Dit betekent in feite dat ItemReader de gegevens één voor één zal lezen en ItemProcessor het ook op dezelfde manier zal verwerken, maar ItemWriter zal de gegevens pas schrijven wanneer het overeenkomt met de grootte van de commit-interval.
- Drie belangrijke interfaces die worden gebruikt als onderdeel van dit project zijn
ItemReader
,ItemProcessor
enItemWriter
van het pakketorg.springframework.batch.item
.
Spring Batch Model Class
Eerst lezen we een CSV-bestand in naar een Java-object en gebruiken vervolgens JAXB om het naar een XML-bestand te schrijven. Hieronder staat onze modelklasse met de vereiste JAXB annotaties.
package com.journaldev.spring.model;
import java.util.Date;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "record")
public class Report {
private int id;
private String firstName;
private String lastName;
private Date dob;
@XmlAttribute(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@XmlElement(name = "firstname")
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@XmlElement(name = "lastname")
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@XmlElement(name = "dob")
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
@Override
public String toString() {
return "Report [id=" + id + ", firstname=" + firstName + ", lastName=" + lastName + ", DateOfBirth=" + dob
+ "]";
}
}
Let op dat de velden van de modelklasse hetzelfde moeten zijn als gedefinieerd in de configuratie van de Spring Batch-mapper, d.w.z. property name="names" value="id,firstname,lastname,dob"
in ons geval.
Spring Batch FieldSetMapper
A custom FieldSetMapper
is needed to convert a Date. If no data type conversion is required, then only BeanWrapperFieldSetMapper
should be used to map the values by name automatically. The java class which extends FieldSetMapper
is ReportFieldSetMapper
.
package com.journaldev.spring;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
import com.journaldev.spring.model.Report;
public class ReportFieldSetMapper implements FieldSetMapper {
private SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
public Report mapFieldSet(FieldSet fieldSet) throws BindException {
Report report = new Report();
report.setId(fieldSet.readInt(0));
report.setFirstName(fieldSet.readString(1));
report.setLastName(fieldSet.readString(2));
// standaardformaat yyyy-MM-dd
// fieldSet.readDate(4);
String date = fieldSet.readString(3);
try {
report.setDob(dateFormat.parse(date));
} catch (ParseException e) {
e.printStackTrace();
}
return report;
}
}
Spring Batch Item Processor
Volgens de jobconfiguratie wordt eerst een itemProcessor uitgevoerd voordat de itemWriter wordt aangeroepen. We hebben een klasse CustomItemProcessor.java
hiervoor aangemaakt.
package com.journaldev.spring;
import org.springframework.batch.item.ItemProcessor;
import com.journaldev.spring.model.Report;
public class CustomItemProcessor implements ItemProcessor<Report, Report> {
public Report process(Report item) throws Exception {
System.out.println("Processing..." + item);
String fname = item.getFirstName();
String lname = item.getLastName();
item.setFirstName(fname.toUpperCase());
item.setLastName(lname.toUpperCase());
return item;
}
}
We kunnen gegevens manipuleren in de implementatie van ItemProcessor, zoals te zien is dat ik voornaam en achternaamwaarden naar hoofdletters omzet.
Spring-configuratiebestanden
In ons lente batch configuratiebestand hebben we twee extra configuratiebestanden geïmporteerd – context.xml
en database.xml
.
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<!-- stored job-meta in memory -->
<!--
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager" />
</bean>
-->
<!-- stored job-meta in database -->
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseType" value="mysql" />
</bean>
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
</beans>
- jobRepository – De JobRepository is verantwoordelijk voor het opslaan van elk Java-object in de juiste metagegevenstabel voor Spring Batch.
- transactionManager – deze is verantwoordelijk voor het commiten van de transactie zodra de grootte van het commit-interval en de verwerkte gegevens gelijk zijn.
- jobLauncher – Dit is het hart van Spring Batch. Deze interface bevat de run-methode die wordt gebruikt om de taak te starten.
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:jdbc="https://www.springframework.org/schema/jdbc" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd
https://www.springframework.org/schema/jdbc
https://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd">
<!-- connect to database -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/Test" />
<property name="username" value="test" />
<property name="password" value="test123" />
</bean>
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<!-- create job-meta tables automatically -->
<!-- <jdbc:initialize-database data-source="dataSource"> <jdbc:script location="org/springframework/batch/core/schema-drop-mysql.sql"
/> <jdbc:script location="org/springframework/batch/core/schema-mysql.sql"
/> </jdbc:initialize-database> -->
</beans>
Spring Batch maakt gebruik van enkele metagegevenstabellen om informatie over batchtaken op te slaan. We kunnen deze laten maken vanuit de Spring Batch-configuraties, maar het is raadzaam om dit handmatig te doen door de SQL-bestanden uit te voeren, zoals je kunt zien in de gecommentarieerde code hierboven. Vanuit het oogpunt van beveiliging is het beter om geen DDL-uitvoeringsrechten te geven aan de Spring Batch-databasegebruiker.
Spring Batch-tabellen
De Spring Batch-tabellen komen zeer nauw overeen met de domeinobjecten die ze in Java vertegenwoordigen. Bijvoorbeeld – JobInstance, JobExecution, JobParameters en StepExecution worden gemapt naar respectievelijk BATCH_JOB_INSTANCE, BATCH_JOB_EXECUTION, BATCH_JOB_EXECUTION_PARAMS en BATCH_STEP_EXECUTION. ExecutionContext wordt gemapt naar zowel BATCH_JOB_EXECUTION_CONTEXT als BATCH_STEP_EXECUTION_CONTEXT. De JobRepository is verantwoordelijk voor het opslaan en opslaan van elk Java-object in de juiste tabel. Hieronder vindt u de details van elke metagegevenstabel.
- Batch_job_instance: De tabel BATCH_JOB_INSTANCE bevat alle relevante informatie over een JobInstance.
- Batch_job_execution_params: De tabel BATCH_JOB_EXECUTION_PARAMS bevat alle relevante informatie over het JobParameters-object.
- Batch_job_execution: De tabel BATCH_JOB_EXECUTION bevat gegevens die relevant zijn voor het JobExecution-object. Er wordt elke keer een nieuwe rij toegevoegd wanneer een taak wordt uitgevoerd.
- Batch_step_execution: De tabel BATCH_STEP_EXECUTION bevat alle relevante informatie over het StepExecution-object.
- Batch_job_execution_context: De tabel BATCH_JOB_EXECUTION_CONTEXT bevat gegevens die relevant zijn voor de ExecutionContext van een taak. Er is precies één Job ExecutionContext voor elke JobExecution, en het bevat alle jobniveau-gegevens die nodig zijn voor die specifieke taakuitvoering. Deze gegevens vertegenwoordigen meestal de status die moet worden opgehaald na een fout, zodat een JobInstance kan worden opnieuw gestart vanaf het punt waar het was mislukt.
- Batch_step_execution_context: De tabel BATCH_STEP_EXECUTION_CONTEXT bevat gegevens die relevant zijn voor de ExecutionContext van een stap. Er is precies één ExecutionContext voor elke StepExecution, en het bevat alle gegevens die nodig zijn om te worden opgeslagen voor een specifieke uitvoering van een stap. Deze gegevens vertegenwoordigen doorgaans de status die moet worden opgehaald na een fout, zodat een JobInstance opnieuw kan worden gestart vanaf het punt waar het is mislukt.
- Batch_job_execution_seq: Deze tabel bevat de gegevens van de uitvoeringsvolgorde van een taak.
- Batch_step_execution_seq: Deze tabel bevat de gegevens voor de volgorde van stapuitvoering.
- Batch_job_seq: Deze tabel bevat de gegevens voor de volgorde van taken voor het geval dat we meerdere taken hebben, krijgen we meerdere rijen.
Spring Batch Test Programma
Ons Spring Batch voorbeeldproject is klaar, de laatste stap is het schrijven van een testklasse om het uit te voeren als een Java-programma.
package com.journaldev.spring;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
String[] springConfig = { "spring/batch/jobs/job-batch-demo.xml" };
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(springConfig);
JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
Job job = (Job) context.getBean("DemoJobXMLWriter");
JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis())
.toJobParameters();
try {
JobExecution execution = jobLauncher.run(job, jobParameters);
System.out.println("Exit Status : " + execution.getStatus());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Done");
context.close();
}
}
Voer eenvoudig het bovenstaande programma uit en u krijgt een uitvoer-XML zoals hieronder.
<?xml version="1.0" encoding="UTF-8"?><report><record id="1001"><dob>2013-07-29T00:00:00+05:30</dob><firstname>TOM</firstname><lastname>MOODY</lastname></record><record id="1002"><dob>2013-07-30T00:00:00+05:30</dob><firstname>JOHN</firstname><lastname>PARKER</lastname></record><record id="1003"><dob>2013-07-31T00:00:00+05:30</dob><firstname>HENRY</firstname><lastname>WILLIAMS</lastname></record></report>
Dat is alles voor het Spring Batch-voorbeeld, u kunt het definitieve project downloaden vanaf de onderstaande link.
Download Spring Batch Voorbeeldproject
Referentie: Officiële Gids
Source:
https://www.digitalocean.com/community/tutorials/spring-batch-example