Java Batch Tutorial

In der heutigen Welt hat das Internet die Art und Weise verändert, wie wir unser Leben leben, und einer der Hauptgründe dafür ist die Nutzung des Internets für die meisten der täglichen Aufgaben. Dies führt zu riesigen Datenmengen, die verarbeitet werden müssen.

Einige Beispiele für die Verarbeitung großer Datenmengen sind Gehaltsabrechnungen, Kontoauszüge, Zinsberechnungen usw. Stellen Sie sich vor, all diese Aufgaben müssten manuell erledigt werden, und es würde ewig dauern, diese Aufgaben zu erledigen.

Wie wird das in der heutigen Zeit gemacht? Die Antwort lautet Stapelverarbeitung.

Einführung

Die Stapelverarbeitung wird mit Massendaten durchgeführt, ohne manuelle Eingriffe, und ist langwierig. Sie kann daten- oder rechenintensiv sein. Batch-Jobs können nach einem vordefinierten Zeitplan ausgeführt oder bei Bedarf gestartet werden. Da Batch-Jobs in der Regel lange laufen, sind ständige Überprüfungen und der Neustart nach einem bestimmten Fehler gängige Merkmale von Batch-Jobs.

1.1 Geschichte der Java Batch-Verarbeitung

Die Batch-Verarbeitung für die Java-Plattform wurde als Teil der JSR 352-Spezifikation eingeführt, die Teil der Java EE 7-Plattform ist und das Programmiermodell für Batch-Anwendungen sowie eine Laufzeit zur Ausführung und Verwaltung von Batch-Jobs definiert.

1.2 Architektur von Java Batch

Das folgende Diagramm zeigt die grundlegenden Komponenten für die Batch-Verarbeitung.

Architektur für Java Batch-Verarbeitung

Die Architektur für Batch-Anwendungen löst Probleme der Batch-Verarbeitung wie Jobs, Steps, Repositories, Reader Processor Writer Patterns, Chunks, Checkpoints, Parallelverarbeitung, Flow, Retries, Sequencing, Partitioning, etc.

Lassen Sie uns den Ablauf der Architektur verstehen.

  • Das Job-Repository enthält die Jobs, die ausgeführt werden müssen.
  • JobLauncher zieht einen Job aus dem Job-Repository.
  • Jeder Job enthält Schritte. Die Steps sind ItemReader, ItemProcessor und ItemWriter.
  • Item Reader ist derjenige, der die Daten liest.
  • Item Process ist derjenige, der die Daten basierend auf der Geschäftslogik verarbeitet.
  • Der Item Writer schreibt die Daten zurück in die definierte Quelle.

1.3 Komponenten der Stapelverarbeitung.

Wir werden nun versuchen, die Komponenten der Stapelverarbeitung im Detail zu verstehen.

  • Job: Ein Job umfasst den gesamten Batch-Prozess. Er enthält einen oder mehrere Schritte. Ein Job wird mit Hilfe einer Job Specification Language (JSL) zusammengestellt, die die Reihenfolge angibt, in der die Schritte ausgeführt werden müssen. In JSR 352 wird die JSL in einer XML-Datei spezifiziert, die als Job-XML-Datei bezeichnet wird. Ein Job ist im Grunde ein Container, der Steps enthält.
  • Step: Ein Step ist ein Domänenobjekt, das eine unabhängige, sequenzielle Phase des Jobs enthält. Ein Step enthält alle notwendige Logik und Daten, um die eigentliche Verarbeitung durchzuführen. Die Definition eines Steps ist gemäß der Batch-Spezifikation vage gehalten, da der Inhalt eines Steps rein anwendungsspezifisch ist und so komplex oder einfach sein kann, wie es der Entwickler wünscht. Es gibt zwei Arten von Steps: chunk- und task-orientiert.
  • Job Operator: Er bietet eine Schnittstelle zur Verwaltung aller Aspekte der Job-Verarbeitung, einschließlich operativer Befehle wie Start, Neustart und Stopp sowie Job-Repository-Befehle, wie das Abrufen von Job- und Step-Ausführungen.
  • Job Repository: Es enthält Informationen über aktuell laufende Jobs und historische Daten über den Job. JobOperator bietet APIs für den Zugriff auf dieses Repository. Ein JobRepository kann mit einer Datenbank oder einem Dateisystem implementiert werden.

Der folgende Abschnitt soll helfen, einige allgemeine Merkmale einer Batch-Architektur zu verstehen.

1.3 Steps in Job

Ein Step ist eine unabhängige Phase eines Jobs. Wie oben beschrieben, gibt es zwei Arten von Steps in einem Job. Im Folgenden werden wir versuchen, beide Typen im Detail zu verstehen.

1.3.1 Chunk-orientierte Steps

Chunk-Steps lesen und verarbeiten jeweils ein Element und fassen die Ergebnisse in einem Chunk zusammen. Die Ergebnisse werden dann gespeichert, wenn der Chunk eine vordefinierte Größe erreicht hat. Die chunk-orientierte Verarbeitung macht die Speicherung von Ergebnissen effizienter, wenn der Datensatz groß ist. Sie besteht aus drei Teilen.

  • Der Item-Reader liest die Eingaben nacheinander aus einer Datenquelle, die eine Datenbank, eine flache Datei, eine Protokolldatei usw. sein kann.
  • Der Prozessor verarbeitet die Daten nacheinander auf der Grundlage der festgelegten Geschäftslogik.
  • Ein Writer schreibt die Daten in Chunks. Die Größe der Chunks ist vordefiniert und kann konfiguriert werden

Als Teil der Chunk-Schritte gibt es Checkpoints, die dem Framework Informationen über den Abschluss der Chunks liefern. Tritt während der Verarbeitung eines Chunks ein Fehler auf, kann der Prozess auf der Grundlage des letzten Prüfpunkts neu gestartet werden.

1.3.2 Aufgabenorientierte Schritte

Es werden andere Aufgaben als die Verarbeitung von Elementen aus einer Datenquelle ausgeführt. Dazu gehören das Erstellen oder Entfernen von Verzeichnissen, das Verschieben von Dateien, das Erstellen oder Löschen von Datenbanktabellen usw. Task-Schritte sind im Vergleich zu Chunk-Schritten in der Regel nicht langwierig.

In einem normalen Szenario werden task-orientierte Schritte nach chunk-orientierten Schritten verwendet, wenn eine Bereinigung erforderlich ist. Zum Beispiel erhalten wir Protokolldateien als Ausgabe einer Anwendung. Die Chunk-Schritte werden für die Verarbeitung der Daten verwendet, um aussagekräftige Informationen aus den Protokolldateien zu erhalten.

Der Task-Schritt wird dann verwendet, um ältere Protokolldateien, die nicht mehr benötigt werden, zu bereinigen.

1.3.3 Parallelverarbeitung

Batch-Jobs führen oft teure Rechenoperationen durch und verarbeiten große Datenmengen. Batch-Anwendungen können in zwei Szenarien von der Parallelverarbeitung profitieren.

  • Schritte, die von ihrer Natur her unabhängig sind, können auf verschiedenen Threads laufen.
  • Stückorientierte Schritte, bei denen die Verarbeitung jedes Elements unabhängig von den Ergebnissen der Verarbeitung früherer Elemente ist, können auf mehr als einem Thread laufen.

Die Stapelverarbeitung hilft dabei, Aufgaben zu erledigen und Operationen für große Datenmengen schneller durchzuführen.

Werkzeuge und Technologien

Werfen wir einen Blick auf die Technologien und Werkzeuge, die für die Erstellung des Programms verwendet werden.

  • Eclipse Oxygen.2 Release (4.7.2)
  • Java – Version 9.0.4
  • Gradle- 4.3
  • Spring boot – 2.0.1-Release
  • HSQL Datenbank

Projektstruktur

Die Projektstruktur wird wie im folgenden Bild dargestellt aussehen.

Projektstruktur für Java Batch

Die obige Projektstruktur wird mit Gradle erstellt. Dieses Projekt kann auch mit Maven erstellt werden und die build.gralde wird durch die pom.xml Datei ersetzt. Die Struktur des Projekts wird sich bei der Verwendung von Maven für den Build leicht verschieben.

Ein Ziel des Programms

Als Teil des Programms werden wir versuchen, eine einfache Java-Batch-Anwendung mit Spring Boot zu erstellen. Diese Anwendung wird die folgenden Aufgaben ausführen.

  1. Lesen: – Lesen von Mitarbeiterdaten aus einer CSV-Datei.
  2. Verarbeiten der Daten: – Konvertieren der Mitarbeiterdaten in Großbuchstaben.
  3. Schreiben: – Schreiben der verarbeiteten Mitarbeiterdaten zurück in die Datenbank.

4.1 Gradle build

Wir verwenden Gradle für den Build als Teil des Programms. Die build.gradle Datei sieht wie unten gezeigt aus.

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")}

In der obigen build.gradle Datei sagt uns apply plugin: 'java' das Plugin, das gesetzt werden muss. Für uns ist es das Java-Plugin.
repositories{} gibt uns das Repository an, aus dem die Abhängigkeit gezogen werden soll. Wir haben mavenCentral gewählt, um die abhängigen Jars zu beziehen. Wir können auch jcenter verwenden, um die entsprechenden Abhängigkeits-Jars zu ziehen.

dependencies {} Tag wird verwendet, um die notwendigen Details der Jar-Dateien bereitzustellen, die für das Projekt gezogen werden sollen. apply plugin: 'org.springframework.boot' Dieses Plugin wird verwendet, um ein Spring-Boot-Projekt zu spezifizieren. boot jar{} gibt die Eigenschaften der Jar-Datei an, die aus dem Build generiert wird.

4.2 Beispieldatendatei

Um Daten für die Lesephase bereitzustellen, verwenden wir eine CSV-Datei, die Mitarbeiterdaten enthält.

Die Datei wird wie folgt aussehen.

Beispiel-CSV-Datei

John,FosterJoe,ToyJustin,TaylorJane,ClarkJohn,Steve

Die Beispieldatendatei enthält den Vor- und Nachnamen des Mitarbeiters. Wir werden dieselben Daten für die Verarbeitung und das Einfügen in die Datenbank verwenden.

4.3 SQL-Skripte

Wir verwenden eine HSQL-Datenbank, die eine speicherbasierte Datenbank ist. Das Skript wird wie unten gezeigt aussehen.

SQL-Skript

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 führt schema-@@platform@@.sql automatisch aus, wenn es startet. -all ist der Standard für alle Plattformen. Die Tabellenerstellung erfolgt also von selbst, wenn die Anwendung startet und ist verfügbar, bis die Anwendung läuft.

4.4 Modellklasse

Wir werden eine Klasse Employee.java als Modellklasse erstellen. Die Klasse wird wie unten gezeigt aussehen.

Modellklasse für das Programm

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 wird verwendet, um die Standardimplementierung der Methode toString() zu überschreiben.

4.5 Konfigurationsklasse

Wir werden eine Klasse BatchConfiguration.java erstellen, die die Konfigurationsklasse für die Stapelverarbeitung sein wird. Die Java-Datei wird wie unten gezeigt aussehen.

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 Die Annotation wird für die Aktivierung der Stapelverarbeitung verwendet.
JobBuilderFactory ist die Fabrik, die für die Erstellung eines Jobs verwendet wird.
StepBuilderFactory wird für die Schritt-Erstellung verwendet.
Die Methode step1() hat eine Eigenschaft chunk(). Diese Eigenschaft wird für die Zerlegung der Eingabe in eine bestimmte Größe verwendet. Für uns ist die Größe 10.

4.6 Item Processor

Item Processor ist eine Schnittstelle, die für die Verarbeitung der Daten verantwortlich ist. Wir werden die Schnittstelle in EmployeeItemProcessor.java implementieren. Die Java-Klasse wird wie unten gezeigt aussehen.

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; }}

In der process()Methode werden wir die Daten erhalten und sie in Großbuchstaben umwandeln.

4.7 JobExecutionSupportListener class

JobExecutionListenerSupport ist die Schnittstelle, die benachrichtigt, wenn der Job abgeschlossen ist. Als Teil der Schnittstelle gibt es die Methode afterJob. Diese Methode wird verwendet, um die Beendigung des Jobs zu melden.

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());}}}}

In dieser Methode holen wir die Daten aus der Datenbank nach Beendigung des Jobs und drucken das Ergebnis auf der Konsole aus, um die Verarbeitung der Daten zu überprüfen.

4.8 Anwendungsklasse

Wir werden eine Anwendungsklasse erstellen, die die Hauptmethode enthält, die für die Auslösung des Java-Batch-Programms verantwortlich ist. Die Klasse wird wie unten gezeigt aussehen.

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 ist die Annotation, die verwendet wird, um ein Programm als Spring Boot Programm zu spezifizieren.

Output

Lassen Sie uns die Anwendung als Java Anwendung ausführen. Wir erhalten die folgende Ausgabe auf der Konsole.

Die Ausgabe des JavaBatch-Programms

Der Arbeitsablauf des Batch-Programms ist in der Ausgabe sehr deutlich zu erkennen. Der Job beginnt mit importUserJob, dann beginnt die Ausführung von Schritt 1, bei dem die gelesenen Daten in Großbuchstaben umgewandelt werden.

Nach der Verarbeitung des Schritts können wir das Ergebnis in Großbuchstaben auf der Konsole sehen.

Zusammenfassung

In diesem Tutorial haben wir die folgenden Dinge gelernt:

  1. JavaBatch enthält Jobs, die mehrere Schritte enthalten können.
  2. Jeder Schritt ist eine Kombination aus Lesen, Verarbeiten, Schreiben.
  3. Wir können die Daten in verschiedene Größen zur Verarbeitung aufteilen.

7. Download des Eclipse-Projekts

Dies war ein Tutorial für JavaBatch mit SpringBoot.