Java Batch Tutorial

Dans le monde d’aujourd’hui, internet a changé la façon dont nous vivons nos vies et l’une des principales raisons pour cela est l’utilisation d’internet pour la plupart des tâches quotidiennes. Cela conduit à une énorme quantité de données disponibles pour le traitement.

Certains des exemples où d’énormes données sont impliquées sont le traitement des bulletins de salaire, des relevés bancaires, le calcul des intérêts, etc. Alors imaginez si tous ces travaux devaient être effectués manuellement, cela prendrait des âges pour terminer ces travaux.

Comment le fait-on à l’époque actuelle ? La réponse est le traitement par lots.

Introduction

Le traitement par lots est effectué sur des données en vrac, sans intervention manuelle, et à long terme. Il peut être gourmand en données ou en calculs. Les travaux par lots peuvent être exécutés selon un calendrier prédéfini ou être lancés à la demande. De plus, comme les travaux par lots sont généralement des travaux de longue durée, les vérifications constantes et le redémarrage à partir d’une certaine défaillance sont des caractéristiques communes que l’on retrouve dans les travaux par lots.

1.1 Histoire du traitement par lots de Java

Le traitement par lots pour la plate-forme Java a été introduit dans le cadre de la spécification JSR 352, faisant partie de la plate-forme Java EE 7, définit le modèle de programmation pour les applications par lots plus un runtime pour exécuter et gérer les travaux par lots.

1.2 Architecture de Java Batch

Le diagramme ci-dessous montre les composants de base pour le traitement par lots.

Architecture pour le traitement par lots de Java

L’architecture pour les applications de traitement par lots résout les préoccupations de traitement par lots comme les travaux, les étapes, les référentiels, les modèles de lecteur processeur écrivain, les chunks, les points de contrôle, le traitement parallèle, le flux, les reprises, le séquençage, le partitionnement, etc.

Comprenons le flux de l’architecture.

  • Le dépôt de jobs contient les jobs qui doivent être exécutés.
  • JobLauncher tire un job du dépôt de jobs.
  • Chaque job contient des étapes. Les étapes sont ItemReader, ItemProcessor et ItemWriter.
  • Lecteur d’éléments est celui qui lit les données.
  • Processeur d’éléments est celui qui va traiter les données en fonction de la logique métier.
  • L’écrivain d’élément va réécrire les données vers la source définie.

1.3 Composants du traitement par lot.

Nous allons maintenant essayer de comprendre les composants du traitement par lot en détail.

  • Travail : Un job comprend l’ensemble du processus de traitement par lot. Il contient une ou plusieurs étapes. Un job est constitué à l’aide d’un langage de spécification des tâches (JSL) qui spécifie l’ordre dans lequel les étapes doivent être exécutées. Dans la norme JSR 352, le JSL est spécifié dans un fichier XML appelé fichier XML de travail. Un job est essentiellement un conteneur contenant des étapes.
  • Étape : Une étape est un objet de domaine qui contient une phase indépendante et séquentielle du job. Une étape contient toute la logique et les données nécessaires pour effectuer le traitement réel. La définition d’une étape reste vague conformément à la spécification du lot car le contenu d’une étape est purement spécifique à l’application et peut être aussi complexe ou simple que le développeur le souhaite. Il existe deux types d’étapes : les chunk et les task oriented.
  • Job Operator : Il fournit une interface pour gérer tous les aspects du traitement des travaux, ce qui inclut les commandes opérationnelles, comme le démarrage, le redémarrage et l’arrêt, ainsi que les commandes de référentiel de travaux, comme la récupération des exécutions de travaux et d’étapes.
  • Référentiel de travaux : Il contient des informations sur les travaux en cours d’exécution et des données historiques sur le travail. JobOperator fournit des API pour accéder à ce référentiel. Un JobRepository pourrait être mis en œuvre en utilisant, une base de données ou un système de fichiers.

La section suivante aidera à comprendre certains caractères communs d’une architecture batch.

1.3 Étapes dans le Job

Une étape est une phase indépendante d’un Job. Comme nous l’avons vu précédemment, il existe deux types d’étapes dans un Job. Nous allons essayer de comprendre les deux types en détail ci-dessous.

1.3.1 Étapes orientées chunk

Les étapes chunk vont lire et traiter un élément à la fois et regrouper les résultats dans un chunk. Les résultats sont ensuite stockés lorsque le chunk atteint une taille prédéfinie. Le traitement orienté chunk rend le stockage des résultats plus efficace lorsque l’ensemble de données est énorme. Il contient trois parties.

  • Le lecteur d’éléments lit l’entrée l’une après l’autre à partir d’une source de données qui peut être une base de données, un fichier plat, un fichier journal, etc.
  • Le processeur traitera les données une par une en fonction de la logique métier définie.
  • Un écrivain écrit les données en chunks. La taille du chunk est prédéfinie et est configurable

Dans le cadre des étapes du chunk, il existe des points de contrôle qui fournissent des informations au framework pour l’achèvement des chunks. S’il y a une erreur pendant le traitement d’un chunk, le processus peut redémarrer sur la base du dernier point de contrôle.

1.3.2 Étapes orientées tâches

Il exécute une tâche autre que le traitement des éléments d’une source de données. Ce qui inclut la création ou la suppression de répertoires, le déplacement de fichiers, la création ou la suppression de tables de base de données, etc. Les étapes de tâches ne sont généralement pas de longue durée par rapport aux étapes de chunk.

Dans un scénario normal, les étapes orientées tâches sont utilisées après les étapes orientées chunk où il y a un nettoyage nécessaire. Par exemple, nous obtenons des fichiers journaux en tant que sortie d’une application. Les étapes de chunk sont utilisées pour traiter les données et obtenir des informations significatives à partir des fichiers journaux.

L’étape de tâche est ensuite utilisée pour nettoyer les anciens fichiers journaux qui ne sont plus nécessaires.

1.3.3 Traitement parallèle

Les travaux par lots effectuent souvent des opérations de calcul coûteuses et traitent de grandes quantités de données. Les applications par lots peuvent bénéficier du traitement parallèle dans deux scénarios.

  • Les étapes qui sont indépendantes par nature peuvent s’exécuter sur différents threads.
  • Les étapes axées sur les morceaux où le traitement de chaque élément est indépendant des résultats du traitement des éléments précédents peuvent s’exécuter sur plus d’un thread.

Le traitement par lots aide à terminer les tâches et à effectuer des opérations plus rapidement pour des données énormes.

Outils et technologies

Regardons les technologies et l’outil utilisés pour construire le programme.

  • Eclipse Oxygen.2. Release (4.7.2)
  • Java – version 9.0.4
  • Gradle- 4.3
  • Spring boot – 2.0.1-Release
  • SQL Database

Structure du projet

La structure du projet se présentera comme dans l’image ci-dessous.

Structure de projet pour Java Batch

La structure de projet ci-dessus utilise Gradle. Ce projet peut également être créé en utilisant maven et le build.gralde sera remplacé par le fichier pom.xml. La structure du projet différera légèrement avec l’utilisation de Maven pour la construction.

Un objectif du programme

Dans le cadre du programme, nous allons essayer de créer une simple application batch java en utilisant spring boot. Cette application effectuera les tâches suivantes.

  1. Lecture : – Lire les données des employés à partir d’un fichier CSV.
  2. Traiter les données : – Convertir les données des employés en majuscules.
  3. Écrire : – Réécrire les données traitées des employés dans la base de données.

4.1 Gradle build

Nous utilisons Gradle pour le build dans le cadre du programme. Le fichier build.gradle aura l’aspect suivant.

build.gradle

buildscript { repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE") }}apply plugin: 'java'apply plugin: 'eclipse'apply plugin: 'idea'apply plugin: 'org.springframework.boot'apply plugin: 'io.spring.dependency-management'bootJar { baseName = 'java-batch' version = '1.0'}repositories { mavenCentral()}sourceCompatibility = 1.8targetCompatibility = 1.8dependencies { compile("org.springframework.boot:spring-boot-starter-batch") compile("org.hsqldb:hsqldb") testCompile("junit:junit")}

Dans le fichier build.gradle ci-dessus, apply plugin: 'java' nous indique le plugin qui doit être défini. Pour nous, c’est le plugin Java.
repositories{} nous permet de connaître le dépôt d’où la dépendance doit être tirée. Nous avons choisi mavenCentral pour tirer les jars de la dépendance. Nous pouvons utiliser jcenter également pour tirer les jars de dépendance respectifs.

dependencies {} tag est utilisé pour fournir les détails du fichier jar nécessaire qui doit être tiré pour le projet. apply plugin: 'org.springframework.boot' ce plugin est utilisé pour spécifier un projet spring-boot. boot jar{} spécifiera les propriétés du jar qui sera généré à partir du build.

4.2 Exemple de fichier de données

Afin de fournir des données pour la phase de lecture, nous utiliserons un fichier CSV contenant des données sur les employés.

Le fichier se présentera comme indiqué ci-dessous.

Echantillon de fichier CSV

John,FosterJoe,ToyJustin,TaylorJane,ClarkJohn,Steve

Le fichier de données échantillon contient le nom et le prénom de l’employé. Nous utiliserons les mêmes données pour le traitement puis l’insertion dans la base de données.

4.3 Scripts SQL

Nous utilisons la base de données HSQL qui est une base de données à mémoire. Le script se présentera comme indiqué ci-dessous.

Scripts SQL

DROP TABLE employee IF EXISTS;CREATE TABLE employee ( person_id BIGINT IDENTITY NOT NULL PRIMARY KEY, first_name VARCHAR(20), last_name VARCHAR(20));

Spring Boot exécute schema-@@platform@@.sql automatiquement au démarrage. -all est la valeur par défaut pour toutes les plateformes. Donc la création de la table se fera d’elle-même au démarrage de l’application et elle sera disponible jusqu’à ce que l’application soit opérationnelle.

4.4 Classe modèle

Nous allons créer une classe Employee.java comme classe modèle. La classe se présentera comme indiqué ci-dessous.

Classe modèle pour le programme

package com.batch;public class Employee { private String lastName; private String firstName; public Employee() { } public Employee(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public String toString() { return "firstName: " + firstName + ", lastName: " + lastName; }}

@Override est utilisée pour surcharger l’implémentation par défaut de la méthode toString().

4.5 Classe de configuration

Nous allons créer une classe BatchConfiguration.java qui sera la classe de configuration pour le traitement par lots. Le fichier java aura l’aspect suivant.

BatchConfiguration.java

package com.batch.config;import javax.sql.DataSource;import org.springframework.batch.core.Job;import org.springframework.batch.core.JobExecutionListener;import org.springframework.batch.core.Step;import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;import org.springframework.batch.core.launch.support.RunIdIncrementer;import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;import org.springframework.batch.item.database.JdbcBatchItemWriter;import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder;import org.springframework.batch.item.file.FlatFileItemReader;import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;import org.springframework.batch.item.file.mapping.DefaultLineMapper;import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import org.springframework.jdbc.core.JdbcTemplate;import com.batch.Employee;import com.batch.processor.EmployeeItemProcessor;@Configuration@EnableBatchProcessingpublic class BatchConfiguration { @Autowired public JobBuilderFactory jobBuilderFactory; @Autowired public StepBuilderFactory stepBuilderFactory; // tag::readerwriterprocessor @Bean public FlatFileItemReader reader() { return new FlatFileItemReaderBuilder() .name("EmployeeItemReader") .resource(new ClassPathResource("sample-data.csv")) .delimited() .names(new String{"firstName", "lastName"}) .fieldSetMapper(new BeanWrapperFieldSetMapper() {{ setTargetType(Employee.class); }}) .build(); } @Bean public EmployeeItemProcessor processor() { return new EmployeeItemProcessor(); } @Bean public JdbcBatchItemWriter writer(DataSource dataSource) { return new JdbcBatchItemWriterBuilder() .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>()) .sql("INSERT INTO employee (first_name, last_name) VALUES (:firstName, :lastName)") .dataSource(dataSource) .build(); } // end::readerwriterprocessor // tag::jobstep @Bean public Job importUserJob(JobCompletionNotificationListener listener, Step step1) { return jobBuilderFactory.get("importUserJob") .incrementer(new RunIdIncrementer()) .listener(listener) .flow(step1) .end() .build(); } @Bean public Step step1(JdbcBatchItemWriter writer) { return stepBuilderFactory.get("step1") .<Employee, Employee> chunk(10) .reader(reader()) .processor(processor()) .writer(writer) .build(); } // end::jobstep}

@EnableBatchProcessing annotation est utilisée pour activer le traitement par lots.
JobBuilderFactory est la fabrique qui est utilisée pour construire un travail.
StepBuilderFactory est utilisée pour la création d’étapes.
La méthode step1() a une propriété chunk(). C’est la propriété utilisée pour le chunking de l’entrée dans une taille définie. Pour nous, la taille est 10.

4.6 Processeur d’éléments

Le processeur d’éléments est une interface qui sera responsable du traitement des données. Nous implémenterons l’interface dans EmployeeItemProcessor.java. La classe java aura l’aspect suivant.

EmployeeItemProcessor.java

package com.batch.processor;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.batch.item.ItemProcessor;import com.batch.Employee;public class EmployeeItemProcessor implements ItemProcessor<Employee, Employee> { private static final Logger log = LoggerFactory.getLogger(EmployeeItemProcessor.class); @Override public Employee process(Employee emp) throws Exception { final String firstName = emp.getFirstName().toUpperCase(); final String lastName = emp.getLastName().toUpperCase(); final Employee transformedEmployee = new Employee(firstName, lastName); log.info("Converting (" + emp + ") into (" + transformedEmployee + ")"); return transformedEmployee; }}

Dans la méthode process(), nous obtiendrons les données et nous les transformerons en nom majuscule.

4.7 Classe JobExecutionSupportListener

JobExecutionListenerSupport est l’interface qui notifiera lorsque le travail est terminé. Dans le cadre de l’interface, nous avons la méthode afterJob. Cette méthode est utilisée pour afficher l’achèvement du travail.

JobCompletionNotificationListener.java

package com.batch.config;import java.util.List;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.batch.core.BatchStatus;import org.springframework.batch.core.JobExecution;import org.springframework.batch.core.listener.JobExecutionListenerSupport;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import org.springframework.stereotype.Component;import com.batch.Employee;@Componentpublic class JobCompletionNotificationListener extends JobExecutionListenerSupport {private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);private final JdbcTemplate jdbcTemplate;@Autowiredpublic JobCompletionNotificationListener(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}@Overridepublic void afterJob(JobExecution jobExecution) {RowMapper rowMapper = (rs, rowNum) -> {Employee e = new Employee();e.setFirstName(rs.getString(1));e.setLastName(rs.getString(2));return e;};if(jobExecution.getStatus() == BatchStatus.COMPLETED) {log.info("!!! JOB FINISHED! Time to verify the results");List empList= jdbcTemplate.query("SELECT first_name, last_name FROM employee",rowMapper);log.info("Size of List "+empList.size());for (Employee emp: empList) {log.info("Found: "+emp.getFirstName()+" "+emp.getLastName());}}}}

Dans cette méthode, nous obtenons les données de la base de données après l’achèvement du travail et nous imprimons le résultat sur la console pour vérifier le traitement qui a été effectué sur les données.

4.8 Classe d’application

Nous allons créer une classe d’application qui contiendra la méthode principale responsable du déclenchement du programme batch java. La classe se présentera comme indiqué ci-dessous.

Application.java

package com.batch;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Application { public static void main(String args) throws Exception { SpringApplication.run(Application.class, args); }}

@SpringBootApplication est l’annotation utilisée pour spécifier un programme comme un programme spring boot.

Output

Exécutons l’application comme une application Java. Nous obtiendrons la sortie suivante sur la console.

La sortie du programme JavaBatch

Le flux de travail du programme batch est très clairement disponible dans la sortie. Le Job commence avec importUserJob, puis l’exécution de l’étape-1 commence où il convertit les données lues en majuscules.

Post-traitement de l’étape, nous pouvons voir le résultat en majuscules sur la console.

Sommaire

Dans ce tutoriel, nous avons appris les choses suivantes:

  1. Java batch contient des Jobs qui peuvent contenir plusieurs étapes.
  2. Chaque étape est une combinaison de lecture, traitement, écriture.
  3. Nous pouvons découper les données en différentes tailles pour le traitement.

7. Télécharger le projet Eclipse

Ce tutoriel était un tutoriel pour JavaBatch avec SpringBoot.