Spring Boot Dengan Spring Batch

1. Gambaran keseluruhan

Spring Batch adalah kerangka yang kuat untuk mengembangkan aplikasi kumpulan yang kuat. Dalam tutorial sebelumnya, kami memperkenalkan Spring Batch.

Dalam tutorial ini, kita akan membina yang sebelumnya dan belajar bagaimana mengatur dan membuat aplikasi asas berdasarkan kumpulan menggunakan Spring Boot.

2. Pergantungan Maven

Pertama, mari tambahkan spring-boot-starter-batch ke pom.xml kami :

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

Kami juga akan menambahkan kebergantungan org.hsqldb , yang tersedia dari Maven Central juga:

 org.hsqldb hsqldb 2.5.1 runtime 

3. Mendefinisikan Pekerjaan Spring Batch yang Sederhana

Kami akan membina pekerjaan yang mengimport senarai kopi dari fail CSV, mengubahnya menggunakan pemproses khusus, dan menyimpan hasil akhir dalam pangkalan data dalam memori .

3.1. Bermula

Mari mulakan dengan menentukan titik masuk aplikasi kami:

@SpringBootApplication public class SpringBootBatchProcessingApplication { public static void main(String[] args) { SpringApplication.run(SpringBootBatchProcessingApplication.class, args); } }

Seperti yang kita lihat, ini adalah aplikasi Spring Boot standard. Oleh kerana kami mahu menggunakan nilai konfigurasi lalai di mana mungkin, kami akan menggunakan set sifat konfigurasi aplikasi yang sangat ringan.

Kami akan menentukan sifat-sifat ini dalam fail src / main / resources / application.properties kami :

file.input=coffee-list.csv

Properti ini mengandungi lokasi senarai kopi input kami. Setiap baris mengandungi jenama, asal, dan beberapa ciri kopi kami:

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

Seperti yang akan kita lihat, ini adalah fail CSV rata, yang bermaksud Spring dapat mengatasinya tanpa penyesuaian khas.

Seterusnya, kami akan menambahkan skema skrip SQL -all.sql untuk membuat meja kopi kami untuk menyimpan data:

DROP TABLE coffee IF EXISTS; CREATE TABLE coffee ( coffee_id BIGINT IDENTITY NOT NULL PRIMARY KEY, brand VARCHAR(20), origin VARCHAR(20), characteristics VARCHAR(30) );

Spring Boot akan menjalankan skrip ini secara automatik semasa permulaan .

3.2. Kelas Domain Kopi

Selepas itu, kami memerlukan kelas domain sederhana untuk menyimpan barang kopi kami:

public class Coffee { private String brand; private String origin; private String characteristics; public Coffee(String brand, String origin, String characteristics) { this.brand = brand; this.origin = origin; this.characteristics = characteristics; } // getters and setters }

Seperti yang telah disebutkan sebelumnya, objek Kopi kami mengandungi tiga sifat:

  • Jenama
  • Asal usul
  • Beberapa ciri tambahan

4. Konfigurasi Pekerjaan

Sekarang, kepada komponen utama, konfigurasi tugas kita. Kami akan pergi selangkah demi selangkah, membina konfigurasi kami dan menerangkan setiap bahagian di sepanjang jalan:

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

Pertama, kita mulakan dengan kelas Spring @Configuration standard . Seterusnya, kami menambahkan anotasi @EnableBatchProcessing ke kelas kami. Terutama, ini memberi kita akses kepada banyak kacang berguna yang menyokong pekerjaan dan akan menjimatkan banyak kerja kaki.

Selain itu, menggunakan anotasi ini juga memberi kita akses kepada dua kilang berguna yang akan kita gunakan kemudian ketika membina konfigurasi pekerjaan dan langkah-langkah pekerjaan kita.

Untuk bahagian terakhir konfigurasi awal kami, kami memasukkan rujukan ke properti file.input yang kami nyatakan sebelumnya.

4.1. Pembaca dan Penulis untuk Tugas Kami

Sekarang, kita boleh terus maju dan menentukan kacang pembaca dalam konfigurasi kita:

@Bean public FlatFileItemReader reader() { return new FlatFileItemReaderBuilder().name("coffeeItemReader") .resource(new ClassPathResource(fileInput)) .delimited() .names(new String[] { "brand", "origin", "characteristics" }) .fieldSetMapper(new BeanWrapperFieldSetMapper() {{ setTargetType(Coffee.class); }}) .build(); }

Ringkasnya, kacang pembaca kami yang ditentukan di atas mencari fail yang disebut coffee-list.csv dan menguraikan setiap item baris ke dalam objek Kopi .

Begitu juga, kita menentukan kacang penulis:

@Bean public JdbcBatchItemWriter writer(DataSource dataSource) { return new JdbcBatchItemWriterBuilder() .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider()) .sql("INSERT INTO coffee (brand, origin, characteristics) VALUES (:brand, :origin, :characteristics)") .dataSource(dataSource) .build(); }

Kali ini, kami menyertakan pernyataan SQL yang diperlukan untuk memasukkan satu item kopi ke dalam pangkalan data kami, didorong oleh sifat kacang Jawa dari objek Kopi kami . Dengan mudah dataSource dibuat secara automatik oleh anotasi @EnableBatchProcessing .

4.2. Menyatukan Tugas Kita Bersama

Terakhir, kita perlu menambahkan langkah dan konfigurasi pekerjaan yang sebenarnya:

@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") . chunk(10) .reader(reader()) .processor(processor()) .writer(writer) .build(); } @Bean public CoffeeItemProcessor processor() { return new CoffeeItemProcessor(); }

Seperti yang kita lihat, tugas kita agak sederhana dan terdiri daripada satu langkah yang ditentukan dalam kaedah step1 .

Mari lihat apa yang dilakukan oleh langkah ini:

  • Pertama, kita mengkonfigurasi langkah kita sehingga akan menulis hingga sepuluh catatan sekaligus menggunakan deklarasi potongan (10)
  • Then, we read in the coffee data using our reader bean, which we set using the reader method
  • Next, we pass each of our coffee items to a custom processor where we apply some custom business logic
  • Finally, we write each coffee item to the database using the writer we saw previously

On the other hand, our importUserJob contains our job definition, which contains an id using the build-in RunIdIncrementer class. We also set a JobCompletionNotificationListener, which we use to get notified when the job completes.

To complete our job configuration, we list each step (though this job has only one step). We now have a perfectly configured job!

5. A Custom Coffee Processor

Let's take a look in detail at the custom processor we defined previously in our job configuration:

public class CoffeeItemProcessor implements ItemProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(CoffeeItemProcessor.class); @Override public Coffee process(final Coffee coffee) throws Exception { String brand = coffee.getBrand().toUpperCase(); String origin = coffee.getOrigin().toUpperCase(); String chracteristics = coffee.getCharacteristics().toUpperCase(); Coffee transformedCoffee = new Coffee(brand, origin, chracteristics); LOGGER.info("Converting ( {} ) into ( {} )", coffee, transformedCoffee); return transformedCoffee; } }

Of particular interest, the ItemProcessor interface provides us with a mechanism to apply some specific business logic during our job execution.

To keep things simple, we define our CoffeeItemProcessor, which takes an input Coffee object and transforms each of the properties to uppercase.

6. Job Completion

Additionally, we're also going to write a JobCompletionNotificationListener to provide some feedback when our job finishes:

@Override public void afterJob(JobExecution jobExecution) { if (jobExecution.getStatus() == BatchStatus.COMPLETED) { LOGGER.info("!!! JOB FINISHED! Time to verify the results"); String query = "SELECT brand, origin, characteristics FROM coffee"; jdbcTemplate.query(query, (rs, row) -> new Coffee(rs.getString(1), rs.getString(2), rs.getString(3))) .forEach(coffee -> LOGGER.info("Found  in the database.", coffee)); } }

In the above example, we override the afterJob method and check the job completed successfully. Moreover, we run a trivial query to check that each coffee item was stored in the database successfully.

7. Running Our Job

Now that we have everything in place to run our job, here comes the fun part. Let's go ahead and run our job:

... 17:41:16.336 [main] INFO c.b.b.JobCompletionNotificationListener - !!! JOB FINISHED! Time to verify the results 17:41:16.336 [main] INFO c.b.b.JobCompletionNotificationListener - Found  in the database. 17:41:16.337 [main] INFO c.b.b.JobCompletionNotificationListener - Found  in the database. 17:41:16.337 [main] INFO c.b.b.JobCompletionNotificationListener - Found  in the database. ... 

As we can see, our job ran successfully, and each coffee item was stored in the database as expected.

8. Conclusion

In this article, we've learned how to create a simple Spring Batch job using Spring Boot. First, we started by defining some basic configuration.

Then, we saw how to add a file reader and database writer. Finally, we took a look at how to apply some custom processing and check our job was executed successfully.

Seperti biasa, kod sumber penuh artikel terdapat di GitHub.