Java Batch Tutorial

I dagens värld har internet förändrat hur vi lever våra liv och en av de viktigaste orsakerna till det är användningen av internet för de flesta dagliga sysslor. Detta leder till enorma mängder data som kan bearbetas.

Några exempel där enorma data är inblandade är bearbetning av lönebesked, kontoutdrag, ränteberäkning osv. Föreställ dig att det skulle ta evigheter att slutföra alla dessa uppgifter om de skulle göras manuellt.

Hur görs det i dagens läge? Svaret är batchbehandling.

Introduktion

Batchbehandling utförs på bulkdata, utan manuellt ingripande och på lång sikt. Den kan vara data- eller beräkningsintensiv. Batchjobb kan köras enligt ett fördefinierat schema eller initieras på begäran. Eftersom batchjobb vanligtvis är långvariga är ständiga kontroller och omstart efter ett visst fel vanliga funktioner i batchjobb.

1.1 Historik för Java Batch Processing

Batch processing för Java Platform introducerades som en del av JSR 352-specifikationen, en del av Java EE 7-plattformen, definierar programmeringsmodellen för batch-applikationer plus en körtid för att köra och hantera batch-jobben.

1.2 Arkitektur för Java Batch

Nedanstående diagram visar de grundläggande komponenterna för batchbehandling.

Arkitektur för Java Batch Processing

Arkitekturen för batchapplikationer löser problem med batchbehandling som jobb, steg, repositories, läsare-processor-skrivarmönster, chunks, checkpoints, parallellbehandling, flöde, retries, sekvensering, partitionering osv.

Låt oss förstå arkitekturens flöde.

  • Job repository innehåller de jobb som måste köras.
  • JobLauncher hämtar ett jobb från Job repository.
  • Alla jobb innehåller steg. Stegen är ItemReader, ItemProcessor och ItemWriter.
  • Item Reader är den som läser data.
  • Item Process är den som behandlar data baserat på affärslogik.
  • Ett artikelskrivare skriver tillbaka data till den definierade källan.

1.3 Komponenter för batchbehandling.

Vi ska nu försöka förstå komponenterna för batchbehandling i detalj.

  • Jobb: Ett jobb omfattar hela batchprocessen. Det innehåller ett eller flera steg. Ett jobb sätts ihop med hjälp av ett jobbspecifikationsspråk (Job Specification Language, JSL) som anger i vilken ordning stegen måste utföras. I JSR 352 specificeras JSL i en XML-fil som kallas jobb-XML-fil. Ett jobb är i princip en behållare som innehåller steg.
  • Steg: Ett steg är ett domänobjekt som innehåller en oberoende, sekventiell fas i jobbet. Ett steg innehåller all nödvändig logik och data för att utföra den faktiska bearbetningen. Definitionen av ett steg hålls vag enligt batchspecifikationen eftersom innehållet i ett steg är rent applikationsspecifikt och kan vara så komplext eller enkelt som utvecklaren vill. Det finns två typer av steg: chunk- och uppgiftsorienterade.
  • Job Operator: Det tillhandahåller ett gränssnitt för att hantera alla aspekter av jobbbearbetning, vilket inkluderar operativa kommandon, såsom start, omstart och stopp, samt kommandon för jobbförvaring, såsom hämtning av jobb- och stegutföranden.
  • Job Repository: Detta är ett gränssnitt för att hantera alla aspekter av jobbbearbetning: Det innehåller information om jobb som körs för närvarande och historiska data om jobbet. JobOperator tillhandahåller API:er för att få tillgång till detta arkiv. Ett JobRepository kan implementeras med hjälp av en databas eller ett filsystem.

Följande avsnitt hjälper till att förstå några vanliga tecken i en batcharkitektur.

1.3 Steg i ett jobb

Ett steg är en oberoende fas i ett jobb. Som diskuterats ovan finns det två typer av steg i ett jobb. Vi kommer att försöka förstå båda typerna i detalj nedan.

1.3.3.1 Chunk-orienterade steg

Chunk-steg läser och bearbetar ett objekt i taget och grupperar resultaten i en chunk. Resultaten lagras sedan när chunken når en fördefinierad storlek. Chunk-orienterad behandling gör lagringen av resultat effektivare när datamängden är stor. Den innehåller tre delar.

  • Artikelläsaren läser inmatningen en efter en från en datakälla som kan vara en databas, en platt fil, en loggfil osv.
  • Processorn bearbetar uppgifterna en efter en baserat på den definierade affärslogiken.
  • En skrivare skriver uppgifterna i chunks. Storleken på chunken är fördefinierad och kan konfigureras

Som en del av chunk-stegen finns det kontrollpunkter som ger information till ramverket om att chunken är klar. Om det uppstår ett fel under en chunk-bearbetning kan processen starta om baserat på den senaste kontrollpunkten.

1.3.2 Uppgiftsorienterade steg

Det utför andra uppgifter än bearbetning av objekt från en datakälla. Vilket inkluderar skapande eller borttagande av kataloger, flyttning av filer, skapande eller borttagande av databastabeller osv. Uppgiftsorienterade steg är vanligtvis inte långvariga jämfört med chunk-steg.

I ett normalt scenario används uppgiftsorienterade steg efter chunk-orienterade steg där det behövs en upprensning. Vi får till exempel loggfiler som ett resultat av ett program. Chunk-stegen används för att bearbeta data och få fram meningsfull information ur loggfilerna.

Approjektsteget används sedan för att städa upp äldre loggfiler som inte längre behövs.

1.3.3 Parallell bearbetning

Batchjobb utför ofta dyra beräkningsoperationer och bearbetar stora datamängder. Batchprogram kan dra nytta av parallellbearbetning i två scenarier.

  • Steg som är oberoende till sin natur kan köras på olika trådar.
  • Kunstyckningsorienterade steg där bearbetningen av varje objekt är oberoende av resultaten av bearbetningen av tidigare objekt kan köras på mer än en tråd.

Batchbehandling hjälper till att avsluta uppgifter och utföra operationer snabbare för enorma data.

Verktyg och teknik

Låt oss titta på den teknik och det verktyg som används för att bygga programmet.

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

Projektstruktur

Projektstrukturen kommer att se ut som i bilden nedan.

Projektstruktur för Java Batch

Projektstrukturen ovan använder Gradle. Detta projekt kan också skapas med hjälp av maven och build.gralde kommer att ersättas med pom.xml-filen. Projektets struktur kommer att skjuta upp något med användningen av Maven för byggandet.

En målsättning med programmet

Som en del av programmet kommer vi att försöka skapa en enkel java batch-applikation med hjälp av spring boot. Denna applikation kommer att utföra följande uppgifter.

  1. Läsa: – Läsa uppgifter om anställda från en CSV-fil.
  2. Behandla uppgifterna: – Konvertera uppgifterna om anställda till versaler.
  3. Skriv: – Skriv de bearbetade medarbetardata tillbaka till databasen.

4.1 Gradle build

Vi använder Gradle för buildet som en del av programmet. build.gradle-filen kommer att se ut som nedan.

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 ovanstående build.gradle-fil talar apply plugin: 'java' om för oss vilken plugin som måste ställas in. För oss är det Java-plugin.
repositories{} låter oss veta från vilket arkiv beroendet ska hämtas. Vi har valt mavenCentral för att hämta beroendejockvarorna. Vi kan också använda jcenter för att hämta de respektive beroendejokrarna.

dependencies {} taggen används för att ge nödvändiga detaljer om de jar-filer som ska hämtas för projektet. apply plugin: 'org.springframework.boot' Detta insticksmodul används för att ange ett spring-boot-projekt. boot jar{} anger egenskaperna för den jar-fil som kommer att genereras från bygget.

4.2 Provdatafil

För att tillhandahålla data för läsfasen kommer vi att använda en CSV-fil som innehåller uppgifter om anställda.

Filen kommer att se ut som nedan.

Prov CSV-fil

John,FosterJoe,ToyJustin,TaylorJane,ClarkJohn,Steve

Provdatafilen innehåller för- och efternamn på den anställde. Vi kommer att använda samma data för bearbetning och sedan infoga den i databasen.

4.3 SQL-skript

Vi använder HSQL-databasen som är en minnesbaserad databas. Skriptet kommer att se ut som nedan.

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 körs schema-@@platform@@.sql automatiskt när den startas. -all är standard för alla plattformar. Så skapandet av tabellen kommer att ske på egen hand när programmet startar och den kommer att vara tillgänglig tills programmet är igång.

4.4 Modellklass

Vi kommer att skapa en Employee.java-klass som modellklass. Klassen kommer att se ut som nedan.

Modellklass för 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 används för att åsidosätta standardimplementationen av toString()-metoden.

4.5 Konfigurationsklass

Vi kommer att skapa en BatchConfiguration.java-klass som kommer att vara konfigurationsklass för batchbehandling. Javafilen kommer att se ut som nedan.

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}

@EnableBatchProcessingannotationen används för att aktivera batchbehandling.
JobBuilderFactory är fabriken som används för att bygga upp ett jobb.
StepBuilderFactory används för att skapa steg.
Metoden step1() har en egenskap chunk(). Detta är den egenskap som används för att dela inmatningen i en definierad storlek. För oss är storleken 10.

4.6 Artikelprocessor

Artikelprocessor är ett gränssnitt som kommer att ansvara för behandlingen av data. Vi kommer att implementera gränssnittet i EmployeeItemProcessor.java. Javaklassen kommer att se ut som nedan.

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 kommer vi att hämta data och omvandla den till ett stort namn.

4.7 JobExecutionSupportListener-klassen

JobExecutionListenerSupport är gränssnittet som kommer att meddela när jobbet är klart. Som en del av gränssnittet har vi afterJob metod. Den här metoden används för att skicka ut när jobbet är slutfört.

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 den här metoden hämtar vi data från databasen efter det att jobbet har slutförts och vi skriver ut resultatet på konsolen för att verifiera den bearbetning som utfördes på data.

4.8 Applikationsklass

Vi kommer att skapa en applikationsklass som kommer att innehålla den huvudmetod som ansvarar för att trigga java-batchprogrammet. Klassen kommer att se ut som nedan.

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 är den annotation som används för att specificera ett program som ett Spring Boot-program.

Output

Låt oss exekvera programmet som ett Java-program. Vi får följande utdata på konsolen.

Utdata från JavaBatch-programmet

Arbetsflödet för batchprogrammet är mycket tydligt tillgängligt i utdata. Jobbet startar med importUserJob, sedan startar utförandet av steg-1 där det omvandlar de lästa uppgifterna till versaler.

Efter stegets bearbetning kan vi se resultatet i versaler i konsolen.

Sammanfattning

I den här handledningen har vi lärt oss följande saker:

  1. Java Batch innehåller jobb som kan innehålla flera steg.
  2. Varje steg är en kombination av läsning, bearbetning och skrivning.
  3. Vi kan dela upp data i olika storlekar för bearbetning.

7. Ladda ner Eclipse-projektet

Detta var en handledning för JavaBatch med SpringBoot.