Bienvenue dans l’exemple de Spring Batch. Spring Batch est un module du framework Spring destiné à l’exécution de tâches en lot. Nous pouvons utiliser Spring Batch pour traiter une série de tâches.
Exemple de Spring Batch
Avant de passer à l’exemple de programme Spring Batch, prenons quelques notions sur les terminologies de Spring Batch.
- 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.
- La lecture-traitement-écriture consiste essentiellement à lire à partir d’une source telle qu’une base de données, un fichier CSV, etc., puis à traiter les données et à les écrire dans une autre source, telle qu’une base de données, un fichier CSV, un fichier XML, etc.
- Une tâchelette signifie effectuer une seule tâche ou opération, comme nettoyer les connexions, libérer les ressources une fois le traitement terminé.
- La lecture-traitement-écriture et les tâchelettes peuvent être enchaînées pour exécuter une tâche.
Exemple de Spring Batch
Prenons un exemple concret d’implémentation de Spring Batch. Nous allons considérer le scénario suivant à des fins d’implémentation. Un fichier CSV contenant des données doit être converti en XML, les données et les balises porteront le nom des colonnes. Ci-dessous, les outils et bibliothèques importants utilisés pour l’exemple de Spring Batch.
- Apache Maven 3.5.0 – pour la construction de projet et la gestion des dépendances.
- Eclipse Oxygen Release 4.7.0 – IDE pour la création d’une application spring batch maven.
- 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 – utilisez en fonction de votre installation MySQL. Ceci est requis pour les tables de métadonnées de Spring Batch.
Structure de répertoire d’exemple Spring Batch
L’image ci-dessous illustre tous les composants de notre projet exemple Spring Batch.
Dépendances de Maven Spring Batch
Voici le contenu du fichier pom.xml avec toutes les dépendances requises pour notre projet exemple spring batch.
<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>
Traitement de fichier d’entrée CSV avec Spring Batch
Voici le contenu de notre fichier CSV d’exemple pour le traitement de lot avec Spring Batch.
1001,Tom,Moody, 29/7/2013
1002,John,Parker, 30/7/2013
1003,Henry,Williams, 31/7/2013
Configuration de tâche Spring Batch
Nous devons définir un bean Spring et une tâche Spring Batch dans un fichier de configuration. Voici le contenu du fichier job-batch-demo.xml
, qui est la partie la plus importante du projet Spring Batch.
<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>
- Nous utilisons
FlatFileItemReader
pour lire le fichier CSV,CustomItemProcessor
pour traiter les données et écrire dans un fichier XML à l’aide deStaxEventItemWriter
. batch:job
– Cette balise définit la tâche que nous voulons créer. La propriété Id spécifie l’ID de la tâche. Nous pouvons définir plusieurs tâches dans un seul fichier XML.- batch:step – Cette balise est utilisée pour définir les différentes étapes d’une tâche Spring Batch.
- Deux types différents de styles de traitement sont proposés par le framework Spring Batch, qui sont « TaskletStep Oriented » et « Chunk Oriented ». Le style Chunk Oriented utilisé dans cet exemple consiste à lire les données une par une et à créer des « chunks » qui seront écrits, dans une transaction délimitée.
- reader: le bean Spring utilisé pour lire les données. Nous avons utilisé le bean
csvFileItemReader
dans cet exemple, qui est une instance deFlatFileItemReader
. - processor: c’est la classe utilisée pour traiter les données. Nous avons utilisé
CustomItemProcessor
dans cet exemple. - writer: le bean utilisé pour écrire les données dans un fichier XML.
- commit-interval: cette propriété définit la taille du chunk qui sera validé une fois le traitement terminé. Fondamentalement, cela signifie que l’ItemReader lira les données une par une et l’ItemProcessor les traitera de la même manière, mais l’ItemWriter écrira les données uniquement lorsqu’elles atteindront la taille de commit-interval.
- Trois interfaces importantes utilisées dans le cadre de ce projet sont
ItemReader
,ItemProcessor
etItemWriter
du packageorg.springframework.batch.item
.
Classe modèle Spring Batch
Tout d’abord, nous lisons le fichier CSV dans un objet Java, puis utilisons JAXB pour l’écrire dans un fichier XML. Voici notre classe modèle avec les annotations JAXB nécessaires.
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
+ "]";
}
}
Notez que les champs de la classe modèle doivent être les mêmes que ceux définis dans la configuration du mapper Spring Batch, c’est-à-dire property name="names" value="id,firstname,lastname,dob"
dans notre cas.
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));
// Format par défaut 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
Maintenant, comme défini dans la configuration du job, un itemProcessor sera déclenché avant l’itemWriter. Nous avons créé une classe CustomItemProcessor.java
à cet effet.
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;
}
}
Nous pouvons manipuler les données dans l’implémentation de l’ItemProcessor, comme vous pouvez le voir, je convertis les valeurs du prénom et du nom en majuscules.
Fichiers de configuration Spring
Dans notre fichier de configuration de lot de printemps, nous avons importé deux fichiers de configuration supplémentaires – context.xml
et 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 – Le JobRepository est responsable de stocker chaque objet Java dans sa table de métadonnées correcte pour le lot de printemps.
- transactionManager – cela est responsable de valider la transaction une fois que la taille de l’intervalle de validation et les données traitées sont égales.
- jobLauncher – C’est le cœur du lot de printemps. Cette interface contient la méthode run qui est utilisée pour déclencher le travail.
<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 utilise certaines tables de métadonnées pour stocker des informations sur les travaux en lot. Nous pouvons les faire créer à partir des configurations de lot de printemps, mais il est conseillé de le faire manuellement en exécutant les fichiers SQL, comme vous pouvez le voir dans le code commenté ci-dessus. Du point de vue de la sécurité, il est préférable de ne pas donner d’accès à l’exécution de DDL à l’utilisateur de la base de données du lot de printemps.
Tables de lot de printemps
Les tables Spring Batch correspondent très étroitement aux objets de domaine qui les représentent en Java. Par exemple – JobInstance, JobExecution, JobParameters et StepExecution sont associés respectivement à BATCH_JOB_INSTANCE, BATCH_JOB_EXECUTION, BATCH_JOB_EXECUTION_PARAMS et BATCH_STEP_EXECUTION. ExecutionContext est associé à la fois à BATCH_JOB_EXECUTION_CONTEXT et à BATCH_STEP_EXECUTION_CONTEXT. Le JobRepository est responsable de sauvegarder et stocker chaque objet Java dans sa table correcte. Voici les détails de chaque table de métadonnées.
- Batch_job_instance: La table BATCH_JOB_INSTANCE contient toutes les informations pertinentes à une JobInstance.
- Batch_job_execution_params: La table BATCH_JOB_EXECUTION_PARAMS contient toutes les informations pertinentes à l’objet JobParameters.
- Batch_job_execution: La table BATCH_JOB_EXECUTION contient des données pertinentes à l’objet JobExecution. Une nouvelle ligne est ajoutée à chaque fois qu’un Job est exécuté.
- Batch_step_execution: La table BATCH_STEP_EXECUTION contient toutes les informations pertinentes à l’objet StepExecution.
- Batch_job_execution_context: La table BATCH_JOB_EXECUTION_CONTEXT contient des données pertinentes à ExecutionContext d’un travail. Il existe exactement un ExecutionContext de travail pour chaque JobExecution, et il contient toutes les données de niveau de travail nécessaires pour cette exécution de travail particulière. Ces données représentent généralement l’état qui doit être récupéré après une défaillance afin qu’une JobInstance puisse redémarrer à partir de l’endroit où elle avait échoué.
- Contexte_d’exécution_de_l’étape_par_lot: La table BATCH_STEP_EXECUTION_CONTEXT contient des données pertinentes pour le Contexte d’Exécution d’une étape. Il y a exactement un ExecutionContext pour chaque StepExecution, et il contient toutes les données qui doivent être persistées pour une exécution d’étape particulière. Ces données représentent généralement l’état qui doit être récupéré après une défaillance afin qu’une JobInstance puisse redémarrer là où elle a échoué.
- Séquence_d’exécution_de_travail_par_lot: Cette table contient la séquence d’exécution des tâches.
- Séquence_d’exécution_de_l’étape_par_lot: Cette table contient les données de séquence pour l’exécution des étapes.
- Séquence_de_travail_par_lot: Cette table contient les données de séquence du travail au cas où nous aurions plusieurs travaux, nous obtiendrons plusieurs lignes.
Programme de Test Spring Batch
Notre projet d’exemple Spring Batch est prêt, la dernière étape consiste à écrire une classe de test pour l’exécuter en tant que programme Java.
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();
}
}
Il suffit d’exécuter le programme ci-dessus et vous obtiendrez une sortie XML comme ci-dessous.
<?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>
C’est tout pour l’exemple de Spring Batch, vous pouvez télécharger le projet final depuis le lien ci-dessous.
Télécharger le Projet Exemple Spring Batch
Référence: Guide Officiel
Source:
https://www.digitalocean.com/community/tutorials/spring-batch-example