Java Batch Tutorial

I dagens verden har internettet ændret den måde, vi lever vores liv på, og en af de vigtigste grunde til det er brugen af internettet til de fleste af de daglige gøremål. Dette har ført til store mængder data, der er tilgængelige til behandling.

Som nogle af de eksempler, hvor der er tale om store mængder data, kan nævnes behandling af lønsedler, kontoudtog, renteberegning osv. Så forestil dig, at hvis alle disse opgaver skulle udføres manuelt, ville det tage evigheder at afslutte disse opgaver.

Hvordan gøres det i den nuværende tidsalder? Svaret er batchbehandling.

Indledning

Batchbehandling udføres på bulkdata, uden manuel indgriben, og langvarig. Det kan være data- eller beregningsintensivt. Batchjobs kan køres efter en foruddefineret tidsplan eller kan iværksættes efter behov. Da batchjobs normalt er langtidsjobs, er konstante kontroller og genstart efter en vis fejl også almindelige funktioner, der findes i batchjobs.

1.1 Historik for Java Batch Processing

Batch processing for Java Platform blev introduceret som en del af JSR 352-specifikationen, der er en del af Java EE 7-platformen, definerer programmeringsmodellen for batch-applikationer plus en runtime til at køre og administrere batchjobs.

1.2. Arkitektur for Java Batch

Nedenstående diagram viser de grundlæggende komponenter til batchbehandling.

Arkitektur for Java Batch Processing

Arkitekturen for batch-applikationer løser batchbehandlingsproblemer som jobs, trin, repositories, reader processor writer-mønstre, chunks, checkpoints, parallel behandling, flow, retries, sequencing, partitionering osv.

Lad os forstå arkitekturens flow.

  • Job repository indeholder de job, der skal køres.
  • JobLauncher trækker et job ud fra Job repository.
  • Et hvert job indeholder trin. Trinene er ItemReader, ItemProcessor og ItemWriter.
  • Item Reader er den, der læser dataene.
  • Item Process er den, der vil behandle dataene baseret på forretningslogik.
  • Element writer vil skrive dataene tilbage til den definerede kilde.

1.3 Batch Processing Components.

Vi vil nu forsøge at forstå batchbehandlingskomponenterne i detaljer.

  • Job: Et job omfatter hele batchprocessen. Det indeholder et eller flere trin. Et job sammensættes ved hjælp af et jobspecifikationssprog (Job Specification Language (JSL)), der angiver den rækkefølge, i hvilken trinene skal udføres. I JSR 352 specificeres JSL i en XML-fil, den såkaldte job XML-fil. Et job er grundlæggende en container, der indeholder trin.
  • Trin: Et trin er et domæneobjekt, der indeholder en uafhængig, sekventiel fase af jobbet. Et trin indeholder al den nødvendige logik og alle de data, der er nødvendige for at udføre den faktiske behandling. Definitionen af et trin holdes vag i henhold til batch-specifikationen, fordi indholdet af et trin er rent applikationsspecifikt og kan være lige så komplekst eller simpelt, som udvikleren ønsker. Der findes to slags trin: chunk- og opgaveorienterede.
  • Job Operator: Det giver en grænseflade til håndtering af alle aspekter af jobbehandling, hvilket omfatter operationelle kommandoer, såsom start, genstart og stop, samt kommandoer til jobrepositoriet, såsom hentning af job- og trinudførelser.
  • Job Repository: Det indeholder oplysninger om job, der kører i øjeblikket, og historiske data om jobbet. JobOperator giver API’er til at få adgang til dette repository. Et JobRepository kan implementeres ved hjælp af, en database eller et filsystem.

Det følgende afsnit vil hjælpe med at forstå nogle almindelige karakterer i en batch-arkitektur.

1.3 Trin i job

Et trin er en uafhængig fase af et job. Som diskuteret ovenfor er der to typer trin i et Job. Vi vil forsøge at forstå begge typer i detaljer nedenfor.

1.3.3.1 Chunk-orienterede trin

Chunk-trin vil læse og behandle et element ad gangen og gruppere resultaterne i et chunk. Resultaterne gemmes derefter, når chunken når en foruddefineret størrelse. Chunk-orienteret behandling gør lagring af resultater mere effektiv, når datasættet er stort. Den indeholder tre dele.

  • Elementlæseren læser input den ene efter den anden fra en datakilde, som kan være en database, flad fil, logfil osv.
  • Processoren behandler dataene en efter en på baggrund af den definerede forretningslogik.
  • En writer skriver dataene i chunks. Størrelsen af chunken er foruddefineret og kan konfigureres

Som en del af chunk-trinnene er der checkpoints, der giver oplysninger til rammen om færdiggørelsen af chunks. Hvis der opstår en fejl under en chunk-behandling, kan processen genstartes baseret på det sidste kontrolpunkt.

1.3.3.2 Opgaveorienterede trin

Det udfører andre opgaver end behandling af elementer fra en datakilde. Hvilket omfatter oprettelse eller fjernelse af mapper, flytning af filer, oprettelse eller sletning af databasetabeller osv. Opgaveorienterede trin er normalt ikke langvarige i forhold til chunk-trin.

I et normalt scenarie anvendes opgaveorienterede trin efter chunk-orienterede trin, hvor der er behov for oprydning. Vi får f.eks. logfiler som et output fra et program. De chunk-orienterede trin bruges til at behandle dataene og få meningsfulde oplysninger ud af logfilerne.

Det opgaveorienterede trin bruges derefter til at rydde op i ældre logfiler, som der ikke længere er brug for.

1.3.3.3 Parallel behandling

Batchjobs udfører ofte dyre beregningsoperationer og behandler store mængder af data. Batch-applikationer kan drage fordel af parallelbehandling i to scenarier.

  • Stræk, der er uafhængige i deres natur, kan køre på forskellige tråde.
  • Chunk-orienterede trin, hvor behandlingen af hvert element er uafhængig af resultaterne af behandlingen af tidligere elementer, kan køre på mere end én tråd.

Batchbehandling hjælper med at afslutte opgaver og udføre operationer hurtigere for store data.

Værktøjer og teknologier

Lad os se på de teknologier og det værktøj, der anvendes til opbygning af programmet.

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

Projektstruktur

Projektstrukturen vil se ud som vist i billedet nedenfor.

Projektstruktur for Java Batch

Den ovenstående projektstruktur bruger Gradle. Dette projekt kan også oprettes ved hjælp af maven, og build.gralde vil blive erstattet med pom.xml-filen. Strukturen af projektet vil udskyde lidt med brugen af Maven til build.

Et mål med programmet

Som en del af programmet vil vi forsøge at oprette en simpel java batch applikation ved hjælp af spring boot. Denne applikation vil udføre følgende opgaver.

  1. Læs: – Læs medarbejderdata fra en CSV-fil.
  2. Behandl dataene: – Konverter medarbejderdataene til alle store bogstaver.
  3. Skriv: – Skriv de behandlede medarbejderdata tilbage i databasen.

4.1 Gradle build

Vi bruger Gradle til build’et som en del af programmet. build.gradle-filen vil se ud som vist nedenfor.

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

I ovenstående build.gradle-fil fortæller apply plugin: 'java' os det plugin, der skal indstilles. For os er det Java-plugin.
repositories{} oplyser os om det repository, som afhængigheden skal hentes fra. Vi har valgt mavenCentral til at trække afhængighedens jars. Vi kan også bruge jcenter til at trække de respektive afhængighedsjars.

dependencies {}-tag bruges til at angive de nødvendige jar-filoplysninger, der skal trækkes for projektet. apply plugin: 'org.springframework.boot' dette plugin bruges til at angive et spring-boot-projekt. boot jar{} vil angive egenskaberne for den jar, der vil blive genereret fra buildet.

4.2 Eksempel på datafil

For at levere data til læsefasen vil vi bruge en CSV-fil, der indeholder medarbejderdata.

Filen vil se ud som vist nedenfor.

Eksempel på CSV-fil

John,FosterJoe,ToyJustin,TaylorJane,ClarkJohn,Steve

Eksempel på datafil indeholder for- og efternavn på medarbejderen. Vi vil bruge de samme data til behandling og derefter indsættelse i databasen.

4.3 SQL-scripts

Vi bruger HSQL-database, som er en hukommelsesbaseret database. Scriptet vil se ud som vist nedenfor.

SQL-script

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 kører schema-@@platform@@.sql automatisk, når den starter. -all er standard for alle platforme. Så tabeloprettelsen sker af sig selv, når programmet starter, og den vil være tilgængelig, indtil programmet er oppe at køre.

4.4 Modelklasse

Vi opretter en Employee.java-klasse som modelklasse. Klassen vil se ud som vist nedenfor.

Modelklasse for programmet

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 bruges til at overskrive standardimplementeringen af toString()-metoden.

4.5 Konfigurationsklasse

Vi vil oprette en BatchConfiguration.java-klasse, som vil være konfigurationsklassen for batchbehandling. Javafilen vil se ud som vist nedenfor.

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-annotationen bruges til at aktivere batchbehandling.
JobBuilderFactory er fabrikken, som bruges til at opbygge et job.
StepBuilderFactory bruges til trinoprettelse.
Metoden step1() har en egenskab chunk(). Dette er den egenskab, der bruges til at opdele input i en defineret størrelse. For os er størrelsen 10.

4.6 Item Processor

Itemprocessor er en grænseflade, som vil være ansvarlig for behandlingen af dataene. Vi vil implementere grænsefladen i EmployeeItemProcessor.java. Java-klassen vil se ud som vist nedenfor.

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

I process()-metoden henter vi dataene, og vi vil omdanne dem til et stort navn.

4.7 JobExecutionSupportListener-klassen

JobExecutionListenerSupport er den grænseflade, der giver besked, når jobbet er afsluttet. Som en del af grænsefladen har vi afterJob metode. Denne metode bruges til at bogføre afslutningen af jobbet.

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

I denne metode henter vi dataene fra databasen efter afslutningen af jobbet, og vi udskriver resultatet på konsollen for at verificere den behandling, der blev udført på dataene.

4.8 Applikationsklasse

Vi vil oprette en applikationsklasse, som vil indeholde den hovedmetode, der er ansvarlig for at udløse java-batchprogrammet. Klassen vil se ud som vist nedenfor.

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 er den annotation, der bruges til at angive et program som et spring boot-program.

Output

Lad os udføre programmet som et java-program. Vi får følgende output på konsollen.

Output af JavaBatch-program

Arbejdsgangen i batchprogrammet er meget tydeligt tilgængelig i output. Jobbet starter med importUserJob, derefter starter udførelsen af trin-1, hvor det konverterer de læste data til store bogstaver.

Efter behandlingen af trin kan vi se resultatet med store bogstaver på konsollen.

Summary

I denne tutorial har vi lært følgende ting:

  1. Java batch indeholder Jobs, som kan indeholde flere trin.
  2. Hvert trin er en kombination af læsning, behandling og skrivning.
  3. Vi kan dele dataene op i forskellige størrelser til behandling.

7. Download Eclipse-projektet

Dette var en tutorial for JavaBatch med SpringBoot.