Spring Boot Met Spring Batch

1. Overzicht

Spring Batch is een krachtig raamwerk voor het ontwikkelen van robuuste batchtoepassingen. In onze vorige tutorial hebben we Spring Batch geïntroduceerd.

In deze zelfstudie bouwen we voort op de vorige en leren we hoe u met Spring Boot een eenvoudige batchgestuurde applicatie instelt en maakt.

2. Maven afhankelijkheden

Laten we eerst de spring-boot-starter-batch naar onze pom.xml:

 org.springframework.boot spring-boot-starter-batch 2.4.0.RELEASE 

We zullen ook de org.hsqldb afhankelijkheid, die ook beschikbaar is bij Maven Central:

 org.hsqldb hsqldb 2.5.1 runtime 

3. Een eenvoudige Spring Batch Job definiëren

We gaan een taak bouwen die een koffielijst uit een CSV-bestand importeert, deze omzet met een aangepaste processor en de uiteindelijke resultaten opslaat in een in-memory database.

3.1. Beginnen

Laten we beginnen met het definiëren van ons toegangspunt voor toepassingen:

@SpringBootApplication openbare klasse SpringBootBatchProcessingApplication {openbare statische leegte hoofd (String [] args) {SpringApplication.run (SpringBootBatchProcessingApplication.class, args); }}

Zoals we kunnen zien, is dit een standaard Spring Boot-applicatie. Omdat we waar mogelijk standaardconfiguratiewaarden willen gebruiken, gaan we een zeer lichte set van applicatieconfiguratie-eigenschappen gebruiken.

We zullen deze eigenschappen definiëren in onze src / main / resources / application.properties het dossier:

file.input = koffielijst.csv

Deze eigenschap bevat de locatie van onze invoer koffielijst. Elke regel bevat het merk, de oorsprong en enkele kenmerken van onze koffie:

Blue Mountain, Jamaica, Fruitige Lavazza, Colombia, Strong Folgers, America, Smokey

Zoals we zullen zien, is dit een plat CSV-bestand, wat betekent dat Spring het aankan zonder speciale aanpassingen.

Vervolgens voegen we een SQL-script toe schema-all.sql om onze koffie tabel om de gegevens op te slaan:

DROP TABLE koffie INDIEN BESTAAT; MAAK TAFEL koffie (coffee_id BIGINT IDENTITY NOT NULL PRIMAIRE SLEUTEL, merk VARCHAR (20), oorsprong VARCHAR (20), kenmerken VARCHAR (30));

Handig is dat Spring Boot dit script automatisch uitvoert tijdens het opstarten.

3.2. Koffiedomeinklasse

Vervolgens hebben we een eenvoudige domeinklasse nodig om onze koffieartikelen te bewaren:

openbare klasse Coffee {private String-merk; private String oorsprong; private String-kenmerken; public Coffee (String-merk, String-oorsprong, String-kenmerken) {this.brand = brand; this.origin = oorsprong; this.characteristics = kenmerken; } // getters en setters}

Zoals eerder vermeld, onze Koffie object bevat drie eigenschappen:

  • Een merk
  • Een oorsprong
  • Enkele aanvullende kenmerken

4. Taakconfiguratie

Nu, op het belangrijkste onderdeel, onze taakconfiguratie. We zullen stap voor stap gaan, onze configuratie opbouwen en elk onderdeel onderweg uitleggen:

@Configuration @EnableBatchProcessing openbare klasse BatchConfiguration {@Autowired openbare JobBuilderFactory jobBuilderFactory; @Autowired openbare StepBuilderFactory stepBuilderFactory; @Value ("$ {file.input}") private String fileInput; // ...}

Ten eerste beginnen we met een standaard veer @Configuratie klasse. Vervolgens voegen we een @EnableBatchProcessing annotatie voor onze klas. Dit geeft ons met name toegang tot veel nuttige bonen die banen ondersteunen en ons veel beenwerk zullen besparen.

Bovendien geeft het gebruik van deze annotatie ons ook toegang tot twee handige fabrieken die we later zullen gebruiken bij het bouwen van onze taakconfiguratie en taakstappen.

Voor het laatste deel van onze initiële configuratie nemen we een verwijzing op naar het bestand.input eigendom dat we eerder hebben aangegeven.

4.1. Een lezer en schrijver voor ons werk

Nu kunnen we doorgaan en een reader-bean in onze configuratie definiëren:

@Bean openbare FlatFileItemReader-lezer () {retourneer nieuwe FlatFileItemReaderBuilder (). Naam ("coffeeItemReader") .resource (nieuwe ClassPathResource (fileInput)) .delimited () .names (nieuwe String [] {"brand", "origin", " kenmerken "}) .fieldSetMapper (nieuwe BeanWrapperFieldSetMapper () {{setTargetType (Coffee.class);}}) .build (); }

Kortom, onze reader bean die hierboven is gedefinieerd, zoekt naar een bestand met de naam koffielijst.csv en parseert elk regelitem in een Koffie voorwerp.

Evenzo definiëren we een schrijverboon:

@Bean openbare JdbcBatchItemWriter-schrijver (DataSource dataSource) {retourneer nieuwe JdbcBatchItemWriterBuilder () .itemSqlParameterSourceProvider (nieuwe BeanPropertyItemSqlParameterSourceProvider ()) .sql ("):, oorsprong, koffie-eigenschappen .dataSource (dataSource) .build (); }

Deze keer nemen we de SQL-instructie op die nodig is om een ​​enkel koffie-item in onze database in te voegen, aangestuurd door de Java-bean-eigenschappen van onze Koffie voorwerp. Handig is het databron wordt automatisch aangemaakt door @EnableBatchProcessing annotatie.

4.2. Onze taak samenbrengen

Ten slotte moeten we de feitelijke taakstappen en configuratie toevoegen:

@Bean public Job importUserJob (JobCompletionNotificationListener listener, Step step1) {return jobBuilderFactory.get ("importUserJob") .incrementer (nieuwe RunIdIncrementer ()) .listener (listener) .flow (step1) .end () .build (); } @Bean public Step step1 (JdbcBatchItemWriter-schrijver) {return stepBuilderFactory.get ("step1"). chunk (10) .reader (reader ()) .processor (processor ()) .writer (schrijver) .build (); } @Bean openbare CoffeeItemProcessor processor () {retourneer nieuwe CoffeeItemProcessor (); }

Zoals we kunnen zien, is ons werk relatief eenvoudig en bestaat het uit één stap die is gedefinieerd in de stap 1 methode.

Laten we eens kijken wat deze stap doet:

  • Eerst configureren we onze stap zodat deze maximaal tien records tegelijk kan schrijven met behulp van de brok (10) verklaring
  • Vervolgens lezen we de koffiegegevens in met behulp van onze leesboon, die we hebben ingesteld met de lezer methode
  • Vervolgens geven we elk van onze koffieartikelen door aan een aangepaste processor waar we een aantal aangepaste bedrijfslogica toepassen
  • Ten slotte schrijven we elk koffie-item naar de database met behulp van de schrijver die we eerder hebben gezien

Aan de andere kant, onze importUserJob bevat onze jobdefinitie, die een id bevat met behulp van de ingebouwde RunIdIncrementer klasse. We hebben ook een JobCompletionNotificationListener, die we gebruiken om een ​​melding te krijgen wanneer de taak is voltooid.

Om onze taakconfiguratie te voltooien, vermelden we elke stap (hoewel deze taak maar één stap heeft). We hebben nu een perfect geconfigureerde job!

5. Een op maat gemaakte koffieprocessor

Laten we eens in detail kijken naar de aangepaste processor die we eerder in onze taakconfiguratie hebben gedefinieerd:

openbare klasse CoffeeItemProcessor implementeert ItemProcessor {privé statische laatste Logger LOGGER = LoggerFactory.getLogger (CoffeeItemProcessor.class); @Override openbaar Koffieproces (laatste koffiekoffie) gooit uitzondering {String brand = coffee.getBrand (). ToUpperCase (); String oorsprong = coffee.getOrigin (). ToUpperCase (); String chracteristics = coffee.getCharacteristics (). ToUpperCase (); Coffee transformedCoffee = nieuwe koffie (merk, oorsprong, kenmerken); LOGGER.info ("Converteren ({}) naar ({})", koffie, transformedCoffee); retourneer transformedCoffee; }}

Van bijzonder belang is de ItemProcessor interface biedt ons een mechanisme om een ​​aantal specifieke bedrijfslogica toe te passen tijdens onze taakuitvoering.

Om het simpel te houden, we definiëren onze KoffieItemProcessor, waarvoor een input nodig is Koffie object en zet elk van de eigenschappen om in hoofdletters.

6. Voltooiing van de taak

Daarnaast gaan we ook een JobCompletionNotificationListener om wat feedback te geven wanneer onze klus is afgelopen:

@Override public void afterJob (JobExecution jobExecution) {if (jobExecution.getStatus () == BatchStatus.COMPLETED) {LOGGER.info ("!!! JOB FINISHED! Tijd om de resultaten te verifiëren"); String query = "SELECTEER merk, oorsprong, kenmerken VAN koffie"; jdbcTemplate.query (query, (rs, row) -> new Coffee (rs.getString (1), rs.getString (2), rs.getString (3))) .forEach (coffee -> LOGGER.info ("Gevonden in de database. ", koffie)); }}

In het bovenstaande voorbeeld overschrijven we de na Job methode en controleer of de taak met succes is voltooid. Bovendien voeren we een triviale zoekopdracht uit om te controleren of elk koffieartikel met succes in de database is opgeslagen.

7. Onze taak uitvoeren

Nu we alles hebben om ons werk te doen, komt hier het leuke gedeelte. Laten we doorgaan en ons werk doen:

... 17: 41: 16.336 [hoofd] INFO c.b.b.JobCompletionNotificationListener - !!! BAAN KLAAR! Tijd om de resultaten te verifiëren 17: 41: 16.336 [main] INFO c.b.b.JobCompletionNotificationListener - Gevonden in de database. 17: 41: 16.337 [main] INFO c.b.b.JobCompletionNotificationListener - Gevonden in de database. 17: 41: 16.337 [main] INFO c.b.b.JobCompletionNotificationListener - Gevonden in de database. ... 

Zoals we kunnen zien, is onze taak succesvol verlopen en is elk koffie-item zoals verwacht in de database opgeslagen.

8. Conclusie

In dit artikel hebben we geleerd hoe u een eenvoudige Spring Batch-taak kunt maken met Spring Boot. Ten eerste zijn we begonnen met het definiëren van een aantal basisconfiguraties.

Vervolgens hebben we gezien hoe we een bestandslezer en databaseschrijver kunnen toevoegen. Ten slotte hebben we gekeken hoe we wat aangepaste verwerking kunnen toepassen en hebben we gecontroleerd of onze taak met succes is uitgevoerd.

Zoals altijd is de volledige broncode van het artikel beschikbaar op GitHub.


$config[zx-auto] not found$config[zx-overlay] not found