Java Batch Tutorial

Nykyaikana internet on muuttanut tapaa, jolla elämme elämäämme, ja yksi tärkeimmistä syistä siihen on internetin käyttö useimpiin päivittäisiin askareisiin. Tämä johtaa valtavaan tietomäärään, jota voidaan käsitellä.

Joitakin esimerkkejä, joissa on kyse valtavasta tietomäärästä, ovat palkkatodistusten, tiliotteiden, korkolaskelmien jne. käsittely. Kuvittele siis, että jos kaikki nämä työt pitäisi tehdä manuaalisesti, kestää ikuisuuksia saada nämä työt valmiiksi.

Miten se tehdään nykyaikana? Vastaus on Eräkäsittely.

Esittely

Eräkäsittely suoritetaan massatiedoille, ilman manuaalisia toimenpiteitä ja pitkäkestoisesti. Se voi olla data- tai laskentaintensiivinen. Eräkäsittelytyöt voidaan suorittaa ennalta määritellyn aikataulun mukaan tai ne voidaan käynnistää pyynnöstä. Koska erätyöt ovat yleensä pitkäkestoisia töitä, myös jatkuvat tarkistukset ja uudelleenkäynnistyminen tietystä vikatilanteesta ovat yleisiä erätöissä esiintyviä ominaisuuksia.

1.1 Java-eräkäsittelyn historia

Java-alustan eräkäsittely otettiin käyttöön osana Java EE 7 -alustaan kuuluvaa JSR 352 -määrittelyä, jossa määritellään ohjelmointimalli eräsovelluksille sekä ajoaika erätöiden suorittamiseen ja hallintaan.

1.2 Java-eräkäsittelyn arkkitehtuuri

Alhaalla olevassa kaaviossa näkyvät eräkäsittelyn peruskomponentit.

Javan eräkäsittelyn arkkitehtuuri

Eräkäsittelysovellusten arkkitehtuuri ratkaisee eräkäsittelyyn liittyvät ongelmat, kuten työt, askeleet, arkistot, lukija-prosessori-kirjoittaja-mallit, palaset, tarkistuspisteet, rinnakkaiskäsittely, virtaus, uusintayritykset, sekvensointi, partitioinnit jne.

Ymmärretäänpä arkkitehtuurin kulku.

  • Työn arkisto (Job repository) sisältää suoritettavat työt (jobs).
  • JobLauncher vetää työn (job) ulos työn arkistosta (Job repository).
  • Jokainen työ (job) sisältää vaiheita. Vaiheet ovat ItemReader, ItemProcessor ja ItemWriter.
  • Item Reader on se, joka lukee tiedot.
  • Item Process on se, joka käsittelee tiedot liiketoimintalogiikan perusteella.
  • Itemin kirjoittaja kirjoittaa tiedot takaisin määriteltyyn lähteeseen.

1.3 Eräkäsittelyn komponentit.

Yritämme nyt ymmärtää eräkäsittelyn komponentteja yksityiskohtaisesti.

  • Job: Työ käsittää koko eräprosessin. Se sisältää yhden tai useamman vaiheen. Työ kootaan käyttämällä Job Specification Language (JSL) -kieltä, jossa määritetään, missä järjestyksessä vaiheet on suoritettava. JSR 352:ssa JSL määritellään XML-tiedostossa, jota kutsutaan työn XML-tiedostoksi. Työ on periaatteessa säiliö, jossa on vaiheita.
  • Step: Askel on toimialueen objekti, joka sisältää työn itsenäisen, peräkkäisen vaiheen. Askel sisältää kaiken tarvittavan logiikan ja datan varsinaisen käsittelyn suorittamiseksi. Askeleen määritelmä pidetään epämääräisenä erämäärittelyn mukaisesti, koska askeleen sisältö on puhtaasti sovelluskohtainen ja se voi olla niin monimutkainen tai yksinkertainen kuin kehittäjä haluaa. Askelia on kahdenlaisia: lohko- ja tehtäväsuuntautuneita.
  • Job Operator: Se tarjoaa käyttöliittymän, jolla hallitaan kaikkia työnkäsittelyn osa-alueita, joihin kuuluvat operatiiviset komennot, kuten käynnistys, uudelleenkäynnistys ja pysäytys, sekä työn arkistokomennot, kuten työn ja askeleiden suoritusten haku.
  • Job Repository: Se sisältää tietoja parhaillaan suoritettavista töistä ja historiatietoja töistä. JobOperator tarjoaa API:t tämän arkiston käyttämiseen. JobRepository Voidaan toteuttaa käyttäen, tietokantaa tai tiedostojärjestelmää.

Seuraava jakso auttaa ymmärtämään eräarkkitehtuurin joitakin yleisiä merkkejä.

1.3 Työn vaiheet

Vaihe on työn itsenäinen vaihe. Kuten edellä käsiteltiin, Jobissa on kahdenlaisia vaiheita. Yritämme ymmärtää molempia tyyppejä yksityiskohtaisesti seuraavassa.

1.3.1 Palikkapainotteiset askeleet

Palikkapainotteiset askeleet lukevat ja käsittelevät yhden kohteen kerrallaan ja ryhmittelevät tulokset palikoiksi. Tulokset tallennetaan, kun kimpun koko on saavuttanut ennalta määritellyn koon. Kappalekohtainen käsittely tehostaa tulosten tallentamista, kun tietojoukko on suuri. Se sisältää kolme osaa.

  • Elementinlukija lukee syötteen yksi kerrallaan tietolähteestä, joka voi olla tietokanta, tasotiedosto, lokitiedosto jne.
  • Prosessori käsittelee tiedot yksi kerrallaan määritellyn liiketoimintalogiikan perusteella.
  • Kirjoittaja kirjoittaa tiedot kappaleittain. Kappaleen koko on ennalta määritetty ja se on konfiguroitavissa

Kappaleiden vaiheissa on tarkistuspisteitä, jotka antavat kehykselle tietoa kappaleiden valmistumisesta. Jos kappalekäsittelyn aikana tapahtuu virhe, prosessi voidaan käynnistää uudelleen viimeisimmän tarkistuspisteen perusteella.

1.3.2 Tehtäväkeskeiset askeleet

Se suorittaa muita tehtäviä kuin tietolähteen kohteiden käsittelyä. Joka sisältää hakemistojen luomisen tai poistamisen, tiedostojen siirtämisen, tietokantataulujen luomisen tai poistamisen jne. Tehtäväkeskeiset vaiheet eivät yleensä ole pitkäkestoisia verrattuna kappalekohtaisiin vaiheisiin.

Normaalissa skenaariossa tehtäväkeskeisiä vaiheita käytetään kappalekohtaisten vaiheiden jälkeen, kun tarvitaan siivousta. Esimerkiksi sovelluksen tuloksena saadaan lokitiedostoja. Chunk-askeleita käytetään tietojen käsittelyyn ja lokitiedostoista saadaan mielekästä tietoa.

Tehtäväaskelia käytetään sen jälkeen vanhojen lokitiedostojen siivoamiseen, joita ei enää tarvita.

1.3.3 Rinnakkaiskäsittely

Batch-työt suorittavat usein kalliita laskentaoperaatioita ja käsittelevät suuria tietomääriä. Eräsovellukset voivat hyötyä rinnakkaiskäsittelystä kahdessa skenaariossa.

  • Vaiheet, jotka ovat luonteeltaan riippumattomia, voidaan suorittaa eri säikeissä.
  • Kokonaisuussuuntautuneet vaiheet, joissa kunkin kohteen käsittely on riippumaton edellisten kohteiden käsittelyn tuloksista, voidaan suorittaa useammassa kuin yhdessä säikeessä.

Sarjakäsittely auttaa saamaan tehtävät valmiiksi ja suorittamaan operaatiot nopeammin valtaville tiedoille.

Työkalut ja teknologiat

Katsotaanpa vielä ohjelman rakentamisessa käytettyjä teknologioita ja työkaluja.

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

Projektin rakenne

Projektin rakenne näyttää alla olevan kuvan mukaisesti.

Projektirakenne Java Batchille

Yllä olevassa projektirakenteessa käytetään Gradlea. Tämä projekti voidaan luoda myös käyttämällä mavenia ja build.gralde korvataan pom.xml-tiedostolla. Projektin rakenne siirtyy hieman, kun käytetään Mavenia rakentamiseen.

Ohjelman tavoite

Ohjelman osana yritämme luoda yksinkertaisen java-eräajosovelluksen spring bootin avulla. Tämä sovellus suorittaa seuraavat tehtävät.

  1. Lue: – Lue työntekijätiedot CSV-tiedostosta.
  2. Käsittele tiedot: – Muunna työntekijätiedot suuraakkosiksi.
  3. Kirjoita: – Kirjoita käsitellyt työntekijätiedot takaisin tietokantaan.

4.1 Gradle build

Käytämme Gradlea rakentamiseen osana ohjelmaa. Tiedosto build.gradle näyttää alla olevan kuvan mukaiselta.

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

Yllä olevassa build.gradle tiedostossa apply plugin: 'java' kertoo meille pluginin, joka pitää asettaa. Meille se on Java-lisäosa.
repositories{} kertoo arkiston, josta riippuvuus tulee vetää. Me olemme valinneet mavenCentral vetääksemme riippuvuuspurkit. Voimme käyttää myös jcenter vastaavien riippuvuuspurkkien vetämiseen.

dependencies {}-tunnistetta käytetään tarvittavien jar-tiedostojen yksityiskohtien antamiseen, jotka pitäisi vetää projektia varten. apply plugin: 'org.springframework.boot' tätä laajennusta käytetään spring-boot-projektin määrittämiseen. boot jar{} määritetään sen jarin ominaisuudet, joka generoidaan buildista.

4.2 Esimerkkidatatiedosto

Lukuvaiheen tietojen antamiseksi käytämme CSV-tiedostoa, joka sisältää työntekijän tiedot.

Tiedosto näyttää alla esitetyltä.

Esimerkki-CSV-tiedosto

John,FosterJoe,ToyJustin,TaylorJane,ClarkJohn,Steve

Esimerkkidatatiedostossa on työntekijän etu- ja sukunimi. Käytämme samaa dataa käsittelyyn ja sen jälkeen tietokantaan lisäämiseen.

4.3 SQL-skriptit

Käytämme HSQL-tietokantaa, joka on muistipohjainen tietokanta. Skripti näyttää alla esitetyltä.

SQL-skripti

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äynnistyy schema-@@platform@@.sql automaattisesti, kun se käynnistyy. -all on oletusarvo kaikilla alustoilla. Taulukon luominen tapahtuu siis itsestään, kun sovellus käynnistyy ja se on käytettävissä, kunnes sovellus on käynnissä.

4.4 Model Class

Luomme Employee.java-luokan malliluokaksi. Luokka tulee näyttämään alla olevan kuvan mukaiselta.

Malliluokka ohjelmalle

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 käytetään toString()-metodin toString() oletustoteutuksen ohittamiseen.

4.5 Konfiguraatioluokka

Luomme BatchConfiguration.java-luokan, joka on konfiguraatioluokka eräkäsittelylle. Java-tiedostosta tulee alla esitetyn näköinen.

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 annotaatiota käytetään eräkäsittelyn mahdollistamiseen.
JobBuilderFactory on tehdas, jota käytetään työn rakentamiseen.
StepBuilderFactory käytetään askeleen luomiseen.
Metodilla step1() on ominaisuus chunk(). Tätä ominaisuutta käytetään syötteen pilkkomiseen määriteltyyn kokoon. Meillä koko on 10.

4.6 Elementtiprosessori

Itemeprosessori on rajapinta, joka vastaa tietojen käsittelystä. Toteutamme rajapinnan EmployeeItemProcessor.java. Java-luokka näyttää alla olevan kuvan mukaiselta.

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

Metodissa process() saamme datan ja muutamme sen isolla alkukirjaimella kirjoitetuksi nimeksi.

4.7 JobExecutionSupportListener-luokka

JobExecutionListenerSupport on rajapinta, joka ilmoittaa, kun työ on valmis. Osana rajapintaa on afterJob-metodi. Tätä metodia käytetään työn valmistumisesta ilmoittamiseen.

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

Tässä metodissa haemme tiedot tietokannasta työn valmistumisen jälkeen ja tulostamme tuloksen konsoliin todentaaksemme tietojen käsittelyn, joka suoritettiin.

4.8 Sovellusluokka

Luomme sovellusluokan, joka sisältää päämetodin, joka on vastuussa java-erätaajuuslaskentaohjelman käynnistämisestä. Luokasta tulee alla esitetyn näköinen.

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 on annotaatio, jota käytetään ohjelman määrittelyyn spring boot -ohjelmaksi.

Output

Toteutetaan sovellus Java-sovelluksena. Saamme konsoliin seuraavan tulosteen.

JavaBatch-ohjelman tuloste

Batch-ohjelman työnkulku on hyvin selvästi nähtävissä tulosteessa. Job alkaa importUserJob, jonka jälkeen alkaa askel-1:n suoritus, jossa luetut tiedot muunnetaan isoksi.

Vaiheen käsittelyn jälkeen näemme konsolissa isolla kirjoitetun tuloksen.

Yhteenveto

Tässä opetusohjelmassa opimme seuraavat asiat:

  1. JavaBatch sisältää Jobeja (työtehtäviä), jotka voivat sisältää useita vaiheita.
  2. Jokainen vaihe on yhdistelmä lukemista, käsittelyä ja kirjoittamista.
  3. Voidaan pilkkoa data erikokoiseksi käsittelyä varten.

7. Lataa Eclipse-projekti

Tämä oli tutoriaali JavaBatchista SpringBootilla.