Cours Spring Boot Batch
Cours Spring Boot Batch
Achref El Mouelhi
1 Introduction
Spring Batch
Spring Batch
H I ©
Framework open-source (unique) pour le traitement par lots
Fournissant des fonctions réutilisables pour
U L grands volumes
Edes
de données
L MO
r e f
Fonctionnement principal E= { lecture de données ⇒ traitement de
A ch de données }
données ⇒ écriture
©
Configurable par XML ou classes Java
Spring Batch
H I ©
EL
La pointeuse renvoie généralement un fichier texte (d’extension
.csv)
M OU
e
le format de date etrtout f EL les pointages doubles, corriger
Il faut lire les données, supprimer
Spring Batch
H I ©
U EL
O
f E LM
ch r e
©A
Source : documentation officielle
Spring Batch
Explication
Spring Batch
Exemple avec chunk(2)
H I ©
U EL
O
f E LM
ch r e
©A
Spring Batch
Création de projet Spring Boot
Chercher Spring, dans Spring Boot sélectionner Spring Starter Project et cli-
quer sur Next >
H I ©
EL
Saisir
OU
cours-spring-batch dans Name,
M
E L
com.example dans Group,
f
c hr e
cours-spring-batch dans Artifact
© 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
Spring Batch
Explication
I ©
Tous les autres packages dao, model... doivent être dans le package
H
demo.
UEL
O
f E LM
ch r e
©A
Spring Batch
Explication
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
Spring Batch
@NoArgsConstructor
@AllArgsConstructor
@Data
H I ©
@Entity
UEL
public class Personne {
O
@Id
f E LM
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long num;
ch r e
©A
private String nom;
private String prenom;
private Long salaire;
}
Spring Batch
package com.example.demo.dao;
H I ©
EL
import org.springframework.data.jpa.repository.JpaRepository;
U
O
LM
import com.example.demo.model.Personne;
r e f E
public interface PersonneRepository extends JpaRepository<Personne,
Long> {
ch
} ©A
Spring Batch
Spring Batch
Contexte et objectif
spring.datasource.url = jdbc:mysql://localhost:3306/cours_batch?
createDatabaseIfNotExist=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
H I ©
EL
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.
MySQLDialect
O U
f E LM
ch r e
©A
spring.datasource.url = jdbc:mysql://localhost:3306/cours_batch?
createDatabaseIfNotExist=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
H I ©
EL
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.
MySQLDialect
O U
E LM
Indiquons également l’emplacement de notre fichier CSV
f
input-file=classpath:/data.csv
ch r e
©A
spring.datasource.url = jdbc:mysql://localhost:3306/cours_batch?
createDatabaseIfNotExist=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
H I ©
EL
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.
MySQLDialect
O U
E LM
Indiquons également l’emplacement de notre fichier CSV
f
input-file=classpath:/data.csv
ch r e
©A
Demandons à Spring Boot de ne pas lancer les jobs au démarrage de l’application
spring.batch.job.enabled = false
spring.datasource.url = jdbc:mysql://localhost:3306/cours_batch?
createDatabaseIfNotExist=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
H I ©
EL
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.
MySQLDialect
O U
E LM
Indiquons également l’emplacement de notre fichier CSV
f
input-file=classpath:/data.csv
ch r e
©A
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.jdbc.initialize-schema=always
Spring Batch
package com.example.demo.batch;
H I ©
import org.springframework.context.annotation.Configuration;
UEL
O
@Configuration
public class SpringBatchConfig {
f E LM
ch r e
©A
}
Spring Batch
Dans la classe SpringBatchConfig, nous commençons par injecter les éléments
indispensable pour le lancement d’un job
@Configuration
public class SpringBatchConfig {
@Value("${input.file}")
private Resource resource;
@Autowired
H I ©
private ItemWriter<Personne> itemWriter;
UEL
@Autowired
O
LM
private ItemProcessor<Personne, Personne> itemProcessor;
}
r e f E
ch
©A
Spring Batch
Dans la classe SpringBatchConfig, nous commençons par injecter les éléments
indispensable pour le lancement d’un job
@Configuration
public class SpringBatchConfig {
@Value("${input.file}")
private Resource resource;
@Autowired
H I ©
private ItemWriter<Personne> itemWriter;
UEL
@Autowired
O
LM
private ItemProcessor<Personne, Personne> itemProcessor;
}
r e f E
ch
©A
Les imports nécessaires
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
Spring Batch
Définissons un Bean pour le Job (dans SpringBatchConfig)
@Bean
public Job job(JobRepository jobRepository, Step step1) {
return new JobBuilder("Recrutement", jobRepository)
.start(step1)
.build();
}
// Recrutement : nom du job
H I ©
U EL
O
f E LM
ch r e
©A
Spring Batch
Définissons un Bean pour le Job (dans SpringBatchConfig)
@Bean
public Job job(JobRepository jobRepository, Step step1) {
return new JobBuilder("Recrutement", jobRepository)
.start(step1)
.build();
}
// Recrutement : nom du job
H I ©
U EL
O
f E LM
Nous devons donner à Spring plus de détail sur le Step
@Bean
ch r e
©A
public Step step1(JobRepository jobRepository, PlatformTransactionManager
platformTransactionManager) {
return new StepBuilder("première étape: chargement de fichier", jobRepository)
.<Personne, Personne>chunk(2, platformTransactionManager)
.reader(reader())
.processor(itemProcessor)
.writer(itemWriter)
.build();
}
Spring Batch
@Bean
public FlatFileItemReader<Personne> reader() {
return new FlatFileItemReaderBuilder<Personne>()
.name("personItemReader")
.resource(resource).linesToSkip(1)
.delimited()
H I ©
EL
.names(new String[] { "nom", "prenom", "salaire" })
{
O U
.fieldSetMapper(new BeanWrapperFieldSetMapper<Personne>() {
LM
setTargetType(Personne.class);
}
}
}).build();
r e f E
ch
©A
Spring Batch
@Bean
public FlatFileItemReader<Personne> reader() {
return new FlatFileItemReaderBuilder<Personne>()
.name("personItemReader")
.resource(resource).linesToSkip(1)
.delimited()
H I ©
EL
.names(new String[] { "nom", "prenom", "salaire" })
{
O U
.fieldSetMapper(new BeanWrapperFieldSetMapper<Personne>() {
LM
setTargetType(Personne.class);
}
}
}).build();
r e f E
ch
©A
Les imports nécessaires
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
Spring Batch
Explication
©A
names() : pour spécifier les noms des attributs de la classe Personne
présents dans le fichier
Spring Batch
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;
H I ©
@Component
U EL
O
public class PersonneItemProcessor implements ItemProcessor<Personne, Personne> {
@Override
f E LM
public Personne process(final Personne personne) throws Exception {
r e
var nom = personne.getNom().toUpperCase();
ch
personne.setNom(nom);
©A
var prenom = personne.getPrenom();
prenom = prenom.substring(0, 1).toUpperCase() + prenom.substring(1).toLowerCase
();
personne.setPrenom(prenom);
return personne;
}
}
Spring Batch
package com.example.demo.batch;
import org.springframework.batch.item.Chunk;
©
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
H I
EL
import org.springframework.stereotype.Component;
import com.example.demo.dao.PersonneRepository;
import com.example.demo.model.Personne;
O U
@Component
f E LM
@Autowired
ch e
public class PersonneItemWriter implements ItemWriter<Personne> {
r
private PersonneRepository personneRepository;
@Override
©A
public void write(Chunk<? extends Personne> chunk) throws Exception {
System.out.println(chunk);
personneRepository.saveAll(chunk);
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;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.web.bind.annotation.GetMapping;
H I ©
EL
import org.springframework.web.bind.annotation.RestController;
O U
LM
import lombok.AllArgsConstructor;
@RestController
r e f E
ch
@AllArgsConstructor
©A
public class JobInvokerController {
@Autowired
private JobLauncher jobLauncher;
@Autowired
private Job processJob;
Spring Batch
Ajoutons l’action qui permettra de lancer le Job
@RestController
@AllArgsConstructor
public class JobInvokerController {
r e f E
public String handle() throws Exception {
ch
JobParameters jobParameters = new JobParametersBuilder()
©A
.addLong("time", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(processJob, jobParameters);
Spring Batch
Objectif
H I ©
Supprimer le fichier data.csv après ajout
U 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
©
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
Spring Batch
Dans la classe SpringBatchConfig, ajoutons la déclaration de notre Tasklet
@Configuration
public class SpringBatchConfig {
@Autowired
private ItemWriter<Personne> itemWriter;
@Autowired
H I ©
@Value("${input.file}")
UEL
private ItemProcessor<Personne, Personne> itemProcessor;
O
LM
private Resource resource;
@Autowired
private Tasklet tasklet;
r e f E
ch
©A
// ...
Spring Batch
Dans la classe SpringBatchConfig, ajoutons la déclaration de notre Tasklet
@Configuration
public class SpringBatchConfig {
@Autowired
private ItemWriter<Personne> itemWriter;
@Autowired
H I ©
@Value("${input.file}")
UEL
private ItemProcessor<Personne, Personne> itemProcessor;
O
LM
private Resource resource;
@Autowired
private Tasklet tasklet;
r e f E
ch
©A
// ...
L’import nécessaire
import org.springframework.batch.core.step.tasklet.Tasklet;
Spring Batch
Modifions le Job précédent pour lancer le Tasklet après step1
@Bean
public Job job(JobRepository jobRepository, @Qualifier("step1") Step
step1, @Qualifier("deleteFile") Step deleteFile) {
return new JobBuilder("Recrutement", jobRepository)
.start(step1)
.next(deleteFile)
H I ©
}
.build();
UEL
O
f E LM
ch r e
©A
Spring Batch
Modifions le Job précédent pour lancer le Tasklet après step1
@Bean
public Job job(JobRepository jobRepository, @Qualifier("step1") Step
step1, @Qualifier("deleteFile") Step deleteFile) {
return new JobBuilder("Recrutement", jobRepository)
.start(step1)
.next(deleteFile)
H I ©
}
.build();
UEL
O
f E LM
ch r e
Définissons le Tasklet défini dans le Job
@Bean
©A
public Step deleteFile(JobRepository jobRepository,
PlatformTransactionManager platformTransactionManager) {
return new StepBuilder("deuxième étape : suppression du fichier
CSV", jobRepository)
.tasklet(tasklet, platformTransactionManager)
.build();
}
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("${input.file}")
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;
}
}
Spring Batch
Rien à changer dans le contrôleur précédent
@RestController
public class JobInvokerController {
@Autowired
private JobLauncher jobLauncher;
H I ©
@Autowired
private Job processJob;
U EL
O
@RequestMapping("/loadData")
f E LM
ch r e
public String handle() throws Exception {
©A
JobParameters jobParameters = new JobParametersBuilder()
.addLong("time", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(processJob, jobParameters);
Spring Batch
....
H I ©
UEL
Executing step: [deuxième étape : suppression du fichier CSV]
Fichier supprimé : C:\Users\elmou\eclipse-workspace\first-spring-batch\
O
LM
target\classes\data.csv
e E
Step: [deuxième étape : suppression du fichier CSV] executed in 368ms
f
Job: [SimpleJob: [name=Recrutement]] completed with the following
r
ch
parameters: [{time=1600800074441}] and the following status: [
©A
COMPLETED] in 2s114ms
....
Spring Batch
Explication
ch r e
cours-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
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
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.
Spring Batch
Objectif
H I ©
EL
Supprimer le contrôleur
M OU
E
Programmer l’exécutionf du L (chaque minute par exemple)
Job
chr e
© A
Spring Batch
package com.example.demo.batch;
H I ©
import org.springframework.stereotype.Component;
UEL
O
@Component
public class ScheduledJob {
f E LM
ch r e
©A
}
Spring Batch
package com.example.demo.batch;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.launch.JobLauncher;
H I ©
EL
import org.springframework.stereotype.Component;
import lombok.AllArgsConstructor;
O U
@Component
f E LM
@AllArgsConstructor
ch r e
©A
public class ScheduledJob {
Spring Batch
Préparons la méthode qui va lancer le Job
package com.example.demo.batch;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import
import
org.springframework.batch.core.launch.JobLauncher;
org.springframework.stereotype.Component;
H I ©
import lombok.AllArgsConstructor;
U EL
@Component
O
LM
@AllArgsConstructor
public class ScheduledJob {
r e f E
ch
private JobLauncher jobLauncher;
private Job job;
©A
public void perform() throws Exception {
System.out.println("Job execution : start");
JobParameters jobParameters = new JobParametersBuilder()
.addLong("time", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(job, jobParameters);
}
}
Spring Batch
Programmons le lancement de ce Job toutes les minutes
package com.example.demo.batch;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import
import
org.springframework.scheduling.annotation.Scheduled;
org.springframework.stereotype.Component;
H I ©
import lombok.AllArgsConstructor;
U EL
O
LM
@Component
@AllArgsConstructor
public class ScheduledJob {
r e f E
ch
private JobLauncher jobLauncher;
©A
private Job job;
Spring Batch
Explication
E L
peut la modifier ainsi : ref
L’unité par défaut pour fixedDelay et fixedRate est milliseconde, mais on
A
@Scheduled(fixedDelaych = 5, timeUnit = TimeUnit.SECONDS)
©
Pour fixedDelay et fixedRate, on peut spécifier ainsi le délai avant le
premier lancement :
@Scheduled(initialDelay = 1000, fixedRate = 5000)
Spring Batch
H I ©
U EL
O
f E LM
ch r e
©A
Source : documentation officielle (Pour plus de détails :
https://docs.spring.io/spring-framework/docs/
current/reference/html/integration.html#
scheduling-cron-expression)
Spring Batch
package com.example.demo;
import org.springframework.boot.SpringApplication;
H I ©
import org.springframework.boot.autoconfigure.SpringBootApplication;
U
import org.springframework.scheduling.annotation.EnableScheduling; EL
O
@SpringBootApplication
f E LM
@EnableScheduling
ch r e
public class CoursSpringBatchApplication {
©A
public static void main(String[] args) {
SpringApplication.run(CoursSpringBatchApplication.class, args);
}
}