Vous êtes sur la page 1sur 42

Spring Batch

Achref El Mouelhi

Docteur de l’université d’Aix-Marseille


Chercheur en programmation par contrainte (IA)
Ingénieur en génie logiciel

elmouelhi.achref@gmail.com

H & H: Research and Training 1 / 32


Plan

1 Introduction

2 Exemple avec un Job et un Step

3 Exemple avec un Job, un Tasklet et un Step

H & H: Research and Training 2 / 32


Introduction

Spring Batch

Spring Batch

H I ©
Framework open-source (unique) pour le traitement par lots

U
Fournissant des fonctions réutilisables pour L grands volumes
Edes
de données
L MO
r e
Fonctionnement principalf E= { lecture de données ⇒ traitement de
données ⇒ A ch de données }
écriture
©
Configurable par XML ou classes Java

H & H: Research and Training 3 / 32


Introduction

Spring Batch

Exemple réel : opération de pointage

Les employés d’une entreprise pointent en début et fin de journée


H I ©
EL
La pointeuse renvoie généralement un fichier texte (d’extension
.csv)
M OU
f E L les pointages doubles, corriger
Il faut lire les données, supprimer
le format de dateh e
c etrtout sauvegarder dans la base de données
Traitement
A
© périodique et répétitif
Pour automatiser le lancement, on peut utiliser Spring Batch

H & H: Research and Training 4 / 32


Introduction

Spring Batch

H I ©
U EL
O
f E LM
ch r e
©A
Source : documentation officielle

H & H: Research and Training 5 / 32


Introduction

Spring Batch

Explication

Job = { Step } (conteneur de Step).


Step = ItemReader + ItemProcessor + ItemWriter.
H I ©
Chunk indique le nombre d’item à chargerE
U en Lmémoire. Par
exemple : si on passe la valeur 4 à O
d’L
M chunk, on lit les items un par
un, une fois que le nombre
e f E éléments lus est égal à 4, le bloc
entier est traité par rItemProcessor
c h puis écrit par ItemWriter,

© A avec les autres de la même manière.


et on continue
JobLauncher exécute les jobs.
JobRepository contient les méta-données relatives aux différents
traitements.

H & H: Research and Training 6 / 32


Introduction

Spring Batch
Exemple avec chunk(2)

H I ©
U EL
O
f E LM
ch r e
©A

Source : documentation officielle

H & H: Research and Training 7 / 32


Introduction

Spring Batch
Création de projet Spring Boot

Aller dans File > New > Other

Chercher Spring, dans Spring Boot sélectionner Spring Starter Project et cli-
quer sur Next >

H I ©
EL
Saisir

OU
first-spring-batch dans Name,
M
E L
com.example dans Group,
f
c e
hr dans Artifact
firstspringbatch
© A
com.example.demo dans Package
Cliquer sur Next

Chercher et cocher les cases correspondantes aux Spring Data JPA, MySQL Driver,
Spring Web, Spring Boot DevTools, Lombok et Spring Batch

Cliquer sur Next puis sur Finish

H & H: Research and Training 8 / 32


Introduction

Spring Batch
Explication

Le package contenant le point d’entrée de notre application (la classe


contenant le puclic static void main) est com.example.demo

I ©
Tous les autres packages dao, model... doivent être dans le package
H
demo.
UEL
O
f E LM
ch r e
©A

H & H: Research and Training 9 / 32


Introduction

Spring Batch
Explication

Le package contenant le point d’entrée de notre application (la classe


contenant le puclic static void main) est com.example.demo

I ©
Tous les autres packages dao, model... doivent être dans le package
H
demo.
U EL
O
f
Pour la suite, nous considérons E LM
r e
ch à définir dans com.example.demo.model
A
une entité Personne
©
une interface DAO PersonneRepository à définir dans
com.example.demo.dao

un contrôleur REST PersonneController à définir dans


com.example.demo.dao

H & H: Research and Training 9 / 32


Introduction

Spring Batch

Créons une entité Personne dans com.example.demo.model

@NoArgsConstructor
@AllArgsConstructor
@Data
H I ©
@Entity
UEL
public class Personne {
O
@Id
f E LM
private Long num;
ch r e
@GeneratedValue(strategy = GenerationType.IDENTITY)

©A
private String nom;
private String prenom;
private Long salaire;
}

H & H: Research and Training 10 / 32


Introduction

Spring Batch

Préparons notre interface DAO PersonneRepository


package com.example.demo.dao;

import org.springframework.data.jpa.repository. H I ©
EL
OU
JpaRepository;

L M
f E
import com.example.demo.model.Personne;
r e
A chPersonneRepository
©
public interface
JpaRepository<Personne, Long> {
extends

H & H: Research and Training 11 / 32


Introduction

Spring Batch

Considérons le fichier data.csv à créer dans


src/main/resources
H I ©
nom_p,prenom_p,salaire_p
UEL
O
wick,john,1700
dalton,jack,2000
f E LM
maggio,carol,1900
ch r e
hoffman,mike,2300
©A

H & H: Research and Training 12 / 32


Exemple avec un Job et un Step

Spring Batch

Contexte et objectif

H I ©
La RH de notre entreprise nous fournit régulièrement un fichier

EL
.csv contenant les noms et prénoms des nouvelles recrues ainsi
que les salaires négociés.
M OU
À partir de ce fichier, nousE
f L
souhaitons ajouter ces personnes dans

chr
la base de données. e
© A
Nous souhaitons aussi que le nom et la première lettre du prénom
soient écrits en majuscule.

H & H: Research and Training 13 / 32


Exemple avec un Job et un Step

Dans application.properties, ajoutons les données permettant la connexion à la


base de données et la configuration de Hibernate
spring.datasource.url = jdbc:mysql://localhost:3306/batch?
serverTimezone=UTC
spring.datasource.username = root
spring.datasource.password = root
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.
H I ©
EL
MySQL8Dialect

model.naming.PhysicalNamingStrategyStandardImpl
O U
spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.

f E LM
ch r e
©A

H & H: Research and Training 14 / 32


Exemple avec un Job et un Step

Dans application.properties, ajoutons les données permettant la connexion à la


base de données et la configuration de Hibernate
spring.datasource.url = jdbc:mysql://localhost:3306/batch?
serverTimezone=UTC
spring.datasource.username = root
spring.datasource.password = root
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.
H I ©
EL
MySQL8Dialect

model.naming.PhysicalNamingStrategyStandardImpl
O U
spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.

f E LM
r e
Indiquons également l’emplacement de notre fichier CSV

ch
©A
inputFile=classpath:/data.csv

H & H: Research and Training 14 / 32


Exemple avec un Job et un Step

Dans application.properties, ajoutons les données permettant la connexion à la


base de données et la configuration de Hibernate
spring.datasource.url = jdbc:mysql://localhost:3306/batch?
serverTimezone=UTC
spring.datasource.username = root
spring.datasource.password = root
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.
H I ©
EL
MySQL8Dialect

model.naming.PhysicalNamingStrategyStandardImpl
O U
spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.

f E LM
r e
Indiquons également l’emplacement de notre fichier CSV

ch
©A
inputFile=classpath:/data.csv

Demandons à Spring Boot de ne pas lancer les jobs au démarrage de l’application


spring.batch.job.enabled = false

H & H: Research and Training 14 / 32


Exemple avec un Job et un Step

Dans application.properties, ajoutons les données permettant la connexion à la


base de données et la configuration de Hibernate
spring.datasource.url = jdbc:mysql://localhost:3306/batch?
serverTimezone=UTC
spring.datasource.username = root
spring.datasource.password = root
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.
H I ©
EL
MySQL8Dialect

model.naming.PhysicalNamingStrategyStandardImpl
O U
spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.

f E LM
r e
Indiquons également l’emplacement de notre fichier CSV

ch
©A
inputFile=classpath:/data.csv

Demandons à Spring Boot de ne pas lancer les jobs au démarrage de l’application


spring.batch.job.enabled = false

Autorisons Spring Batch à générer ses tables nécessaires pour la gestion de lots
spring.batch.initialize-schema=always

H & H: Research and Training 14 / 32


Exemple avec un Job et un Step

Spring Batch

Créons une classe de configuration pour Spring Batch


package com.example.demo.batch;

import org.springframework.batch.core.configuration.
H I ©
annotation.EnableBatchProcessing;
UEL
O
LM
import org.springframework.context.annotation.
Configuration;
r e f E
@Configuration
ch
©A
@EnableBatchProcessing
public class SpringBatchConfig {

H & H: Research and Training 15 / 32


Exemple avec un Job et un Step

Spring Batch
Dans la classe SpringBatchConfig, nous commençons par injecter les 5 éléments
indispensable pour le lancement d’un job
@Configuration
@EnableBatchProcessing
public class SpringBatchConfig {
@Autowired private JobBuilderFactory jobBuilderFactory;
@Autowired private StepBuilderFactory stepBuilderFactory;
H I ©
@Autowired private ItemReader<Personne> itemReader;
UEL
@Autowired private ItemWriter<Personne> itemWriter;
O
LM
@Autowired private ItemProcessor<Personne, Personne> itemProcessor;
}

r e f E
ch
©A

H & H: Research and Training 16 / 32


Exemple avec un Job et un Step

Spring Batch
Dans la classe SpringBatchConfig, nous commençons par injecter les 5 éléments
indispensable pour le lancement d’un job
@Configuration
@EnableBatchProcessing
public class SpringBatchConfig {
@Autowired private JobBuilderFactory jobBuilderFactory;
@Autowired private StepBuilderFactory stepBuilderFactory;
H I ©
@Autowired private ItemReader<Personne> itemReader;
UEL
@Autowired private ItemWriter<Personne> itemWriter;
O
LM
@Autowired private ItemProcessor<Personne, Personne> itemProcessor;
}

r e f E
ch
©A
Les imports nécessaires
import org.springframework.batch.core.configuration.annotation.
JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.
StepBuilderFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;

H & H: Research and Training 16 / 32


Exemple avec un Job et un Step

Spring Batch
Définissons un Bean pour le Job (dans SpringBatchConfig)

@Bean
public Job job(Step step1) {
return jobBuilderFactory.get("Recrutement") // le nom de notre job
.start(step1)

}
.build();

H I ©
UEL
O
f E LM
ch r e
©A

H & H: Research and Training 17 / 32


Exemple avec un Job et un Step

Spring Batch
Définissons un Bean pour le Job (dans SpringBatchConfig)

@Bean
public Job job(Step step1) {
return jobBuilderFactory.get("Recrutement") // le nom de notre job
.start(step1)

}
.build();

H I ©
UEL
O
f E LM
Nous devons donner à Spring plus de détail sur le Step

@Bean
ch r e
©A
public Step step1() {
return stepBuilderFactory.get("première étape: chargement de fichier"
)
.<Personne, Personne>chunk(2)
.reader(itemReader)
.processor(itemProcessor)
.writer(itemWriter)
.build();
}

H & H: Research and Training 17 / 32


Exemple avec un Job et un Step

Définissons un Bean pour la lecture de données (dans SpringBatchConfig)


@Bean
public FlatFileItemReader<Personne> reader(@Value("${inputFile}")
Resource resource) {
return new FlatFileItemReaderBuilder<Personne>()
.name("personItemReader")
.resource(resource)
.linesToSkip(1)
.delimited()
H I ©
EL
.names(new String[]{"nom", "prenom", "salaire"})

setTargetType(Personne.class);
O U
.fieldSetMapper(new BeanWrapperFieldSetMapper<Personne>() {{

}})
.build();
f E LM
}
ch r e
©A

H & H: Research and Training 18 / 32


Exemple avec un Job et un Step

Définissons un Bean pour la lecture de données (dans SpringBatchConfig)


@Bean
public FlatFileItemReader<Personne> reader(@Value("${inputFile}")
Resource resource) {
return new FlatFileItemReaderBuilder<Personne>()
.name("personItemReader")
.resource(resource)
.linesToSkip(1)
.delimited()
H I ©
EL
.names(new String[]{"nom", "prenom", "salaire"})

setTargetType(Personne.class);
O U
.fieldSetMapper(new BeanWrapperFieldSetMapper<Personne>() {{

}})
.build();
f E LM
}
ch r e
©A
Les imports nécessaires
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.
FlatFileItemReaderBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;

H & H: Research and Training 18 / 32


Exemple avec un Job et un Step

Spring Batch

Explication

@Value("${inputFile}") Resource resource : permet de


récupérer le nom de fichier défini dans application.properties et
l’injecter dans resource
H I ©
UEL
O
linesToSkip(1) : pour ignorer la première ligne (contenant l’entête)
de notre fichier CSV
f E LM
ch r e
delimeted() : pour construire un objet DelimitedLineTokenizer

©A
names() : pour spécifier les noms des attributs de la classe Personne
présents dans le fichier

fieldSetMapper() : pour spécifier le type de mapping (ici le type de


sortie est un objet de la classe Personne)

H & H: Research and Training 19 / 32


Exemple avec un Job et un Step

Définissons un Component appelé PersonneItemProcessor pour spécifier ce qu’on fera


avec l’objet lu

package com.example.demo.batch;

import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;
import com.example.demo.model.Personne;

@Component
H I ©
EL
public class PersonneItemProcessor implements ItemProcessor<Personne,
Personne> {
O U
@Override
f E LM
Exception {
ch r e
public Personne process(final Personne personne) throws

©A
var nom = personne.getNom().toUpperCase();
personne.setNom(nom);
var prenom = personne.getPrenom();
prenom = prenom.substring(0, 1).toUpperCase() + prenom.
substring(1).toLowerCase();
personne.setPrenom(prenom);
return personne;
}
}

H & H: Research and Training 20 / 32


Exemple avec un Job et un Step

Définissons un Component appelé PersonneItemWriter pour écrire l’objet transformé

package com.example.demo.batch;

import java.util.List;

import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

H I ©
EL
import com.example.demo.dao.PersonneRepository;
import com.example.demo.model.Personne;
O U
@Component
f E LM
@Autowired
ch r e
public class PersonneItemWriter implements ItemWriter<Personne> {

©A
private PersonneRepository personneRepository;

@Override
public void write(List<? extends Personne> items) throws
Exception {
System.out.println(items);
personneRepository.saveAll(items);
}
}

H & H: Research and Training 21 / 32


Exemple avec un Job et un Step

Spring Batch
Créons un contrôleur (qui nous permettra de lancer le Job) et commençons par injecter le
Job et JobLauncher

package com.example.demo.controller;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
H I ©
EL
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import
O U
org.springframework.web.bind.annotation.RequestMapping;
import
LM
org.springframework.web.bind.annotation.RestController;

f E
@RestController
ch r e
©A
public class JobInvokerController {

@Autowired
private JobLauncher jobLauncher;

@Autowired
private Job processJob;

H & H: Research and Training 22 / 32


Exemple avec un Job et un Step

Ajoutons l’action qui permettra de lancer le Job

package com.example.demo.controller;

@RestController
public class JobInvokerController {

@Autowired
private JobLauncher jobLauncher;

H I ©
@Autowired
U EL
private Job processJob;
O
@RequestMapping("/loadData")
f E LM
r e
public String handle() throws Exception {

ch
©A
JobParameters jobParameters = new JobParametersBuilder()
.addLong("time", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(processJob, jobParameters);

return "Le job a été exécuté";


}
}

H & H: Research and Training 23 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch

Objectif
H I ©
U
Supprimer le fichier data.csv après ajout L la base de
Edans
données
L MO
Utiliser un Tasklet e
r f
apr Ele premier Step pour supprimer le
ès
fichier A ch
©

H & H: Research and Training 24 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch

Tasklet
Comme un Step
H I ©
sans ItemWriter
UEL
O
généralement sans valeur de retour
f E LM
Utilisé souvent pour
ch r e
©A
appeler une procédure stockée
exécuter un script

H & H: Research and Training 25 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch

Dans la classe SpringBatchConfig, ajoutons la déclaration de notre Tasklet

@Configuration
@EnableBatchProcessing
public class SpringBatchConfig {
@Autowired private JobBuilderFactory jobBuilderFactory;
H I ©
EL
@Autowired private StepBuilderFactory stepBuilderFactory;
@Autowired private ItemReader<Personne> itemReader;
@Autowired private ItemWriter<Personne> itemWriter;
O U
f
@Autowired private Tasklet tasklet;
E LM
@Autowired private ItemProcessor<Personne, Personne> itemProcessor;

ch r e
©A
// ...

H & H: Research and Training 26 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch

Dans la classe SpringBatchConfig, ajoutons la déclaration de notre Tasklet

@Configuration
@EnableBatchProcessing
public class SpringBatchConfig {
@Autowired private JobBuilderFactory jobBuilderFactory;
H I ©
EL
@Autowired private StepBuilderFactory stepBuilderFactory;
@Autowired private ItemReader<Personne> itemReader;
@Autowired private ItemWriter<Personne> itemWriter;
O U
f
@Autowired private Tasklet tasklet;
E LM
@Autowired private ItemProcessor<Personne, Personne> itemProcessor;

ch r e
©A
// ...

L’import nécessaire

import org.springframework.batch.core.step.tasklet.Tasklet;

H & H: Research and Training 26 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch
Modifions le Job précédent pour lancer le Tasklet après step1

@Bean
public Job job(@Qualifier("step1") Step step1, @Qualifier("deleteFile")
Step deleteFile) {
return jobBuilderFactory.get("Recrutement")
.start(step1)
H I ©
EL
.next(deleteFile)

}
.build();
O U
f E LM
ch r e
©A

H & H: Research and Training 27 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch
Modifions le Job précédent pour lancer le Tasklet après step1

@Bean
public Job job(@Qualifier("step1") Step step1, @Qualifier("deleteFile")
Step deleteFile) {
return jobBuilderFactory.get("Recrutement")
.start(step1)
H I ©
EL
.next(deleteFile)

}
.build();
O U
f E LM
ch r e
Définissons le Tasklet défini dans le Job

@Bean ©A
public Step deleteFile() {
return this.stepBuilderFactory.get("deuxième étape : suppression
du fichier CSV")
.tasklet(tasklet)
.build();
}

H & H: Research and Training 27 / 32


Exemple avec un Job, un Tasklet et un Step

Définissons un Component appelé FileDeletingTasklet pour la suppression de notre fichier CSV

package com.example.demo.batch;

import java.io.File;

import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import
import
org.springframework.beans.factory.annotation.Value;
org.springframework.core.io.Resource;
H I ©
EL
import org.springframework.stereotype.Component;

@Component
O U
LM
public class FileDeletingTasklet implements Tasklet {

@Value("${inputFile}")

r e
private Resource resource;
f E
ch
©A
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
throws Exception {
File file = resource.getFile();
boolean deleted = file.delete();
if (!deleted) {
throw new UnexpectedJobExecutionException("Impossible de supprimer le
fichier " + file.getPath());
}
System.out.println("Fichier supprimé : " + file.getPath());
return RepeatStatus.FINISHED;
}
}

H & H: Research and Training 28 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch
Rien à changer dans le contrôleur précédent

@RestController
public class JobInvokerController {

@Autowired
private JobLauncher jobLauncher;

H I ©
@Autowired
U EL
private Job processJob;
O
@RequestMapping("/loadData")
f E LM
r e
public String handle() throws Exception {

ch
©A
JobParameters jobParameters = new JobParametersBuilder()
.addLong("time", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(processJob, jobParameters);

return "Le job a été exécuté";


}
}

H & H: Research and Training 29 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch

Lancez le projet et allez à localhost:8080/loadData et vérifiez la présence des


messages suivants

....
H I ©
EL
Executing step: [deuxième étape : suppression du fichier CSV]
U
O
Fichier supprimé : C:\Users\elmou\eclipse-workspace\first-spring-batch\

LM
target\classes\data.csv

r e E
Step: [deuxième étape : suppression du fichier CSV] executed in 368ms
f
Job: [SimpleJob: [name=Recrutement]] completed with the following

ch
parameters: [{time=1600800074441}] and the following status: [

©A
COMPLETED] in 2s114ms
....

H & H: Research and Training 30 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch
Explication

Un message nous indique que le fichier data.csv a été


supprimé
Ce n’est pas le data.csv qu’on a créé dans
H I ©
src/main/resources
UEL
O
f E LM
Le chemin affiché indique que le fichier supprimé se trouvait dans

ch r e
first-spring-batch/target/classes

©A
En effet, Spring a déplacé data.csv qu’on a créé dans
src/main/resources dans target/classes/
Faites un clic droit sur le projet et cliquez sur Refresh et allez
vérifiez que Spring a de nouveau déplacé data.csv dans
first-spring-batch/target/classes

H & H: Research and Training 31 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch

Question
Pourquoi Spring Boot a t-il redémarré l’application après suppression
de fichier ? H I ©
UEL
O
f E LM
ch r e
©A

H & H: Research and Training 32 / 32


Exemple avec un Job, un Tasklet et un Step

Spring Batch

Question
Pourquoi Spring Boot a t-il redémarré l’application après suppression
de fichier ? H I ©
UEL
O
f E LM
Réponse
ch r e
©A
Devtools a détecté un changement (suppression de fichier) et par
conséquence a redémarré l’application.

H & H: Research and Training 32 / 32

Vous aimerez peut-être aussi