Boot Musim Semi Dengan Batch Musim Semi

1. Ikhtisar

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

Dalam tutorial ini, kita akan membangun yang sebelumnya dan mempelajari cara mengatur dan membuat aplikasi berbasis batch dasar menggunakan Spring Boot.

2. Ketergantungan Maven

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

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

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

 org.hsqldb hsqldb 2.5.1 runtime 

3. Mendefinisikan Pekerjaan Kelompok Musim Semi Sederhana

Kita akan membuat pekerjaan yang mengimpor daftar kopi dari file CSV, mengubahnya menggunakan prosesor khusus, dan menyimpan hasil akhir dalam database dalam memori .

3.1. Mulai

Mari kita mulai dengan mendefinisikan titik masuk aplikasi kita:

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

Seperti yang bisa kita lihat, ini adalah aplikasi Spring Boot standar. Karena kami ingin menggunakan nilai konfigurasi default jika memungkinkan, kami akan menggunakan sekumpulan properti konfigurasi aplikasi yang sangat ringan.

Kami akan menentukan properti ini di file src / main / resources / application.properties kami :

file.input=coffee-list.csv

Properti ini berisi lokasi daftar kopi masukan kami. Setiap baris berisi merek, asal, dan beberapa ciri khas kopi kami:

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

Seperti yang akan kita lihat, ini adalah file CSV datar, yang berarti Spring dapat menanganinya tanpa penyesuaian khusus.

Selanjutnya, kami akan menambahkan skrip SQL schema-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 dengan mudah akan menjalankan skrip ini secara otomatis selama startup .

3.2. Kelas Domain Kopi

Selanjutnya, kita memerlukan kelas domain sederhana untuk menampung item kopi kita:

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 disebutkan sebelumnya, objek Coffee kami berisi tiga properti:

  • Sebuah merek
  • Asal
  • Beberapa karakteristik tambahan

4. Konfigurasi Pekerjaan

Sekarang, ke komponen kunci, konfigurasi pekerjaan kita. Kami akan melanjutkan langkah demi langkah, membangun konfigurasi kami dan menjelaskan setiap bagian di sepanjang jalan:

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

Pertama, kita mulai dengan kelas Spring @Configuration standar . Selanjutnya, kami menambahkan anotasi @EnableBatchProcessing ke kelas kami. Khususnya, ini memberi kami akses ke banyak kacang bermanfaat yang mendukung pekerjaan dan akan menghemat banyak pekerjaan kaki.

Selain itu, menggunakan anotasi ini juga memberi kami akses ke dua pabrik berguna yang akan kami gunakan nanti saat membuat konfigurasi pekerjaan dan langkah-langkah pekerjaan kami.

Untuk bagian terakhir dari konfigurasi awal kami, kami menyertakan referensi ke properti file.input yang kami nyatakan sebelumnya.

4.1. Pembaca dan Penulis untuk Pekerjaan Kami

Sekarang, kita dapat melanjutkan dan mendefinisikan 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(); }

Singkatnya, kacang pembaca kami yang didefinisikan di atas mencari file bernama coffee-list.csv dan mem-parsing setiap item baris menjadi objek Coffee .

Demikian juga, kami mendefinisikan 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 database kami, didorong oleh properti biji Java dari objek Coffee kami . Dengan mudah, dataSource dibuat secara otomatis oleh anotasi @EnableBatchProcessing .

4.2. Menyatukan Pekerjaan Kita

Terakhir, kita perlu menambahkan langkah-langkah dan konfigurasi pekerjaan aktual:

@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 bisa kita lihat, pekerjaan kita relatif sederhana dan terdiri dari satu langkah yang ditentukan dalam metode step1 .

Mari kita lihat apa yang dilakukan langkah ini:

  • Pertama, kami mengonfigurasi langkah kami sehingga itu akan menulis hingga sepuluh rekaman sekaligus menggunakan deklarasi chunk (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, kode sumber lengkap artikel tersedia di GitHub.