From 7e313e5b4c3b50e2e72f04020164d08058a1933d Mon Sep 17 00:00:00 2001 From: Mahmoud Ben Hassine Date: Tue, 17 Oct 2023 18:47:56 +0200 Subject: [PATCH] Update guide - Use Java records for domain types - Update Spring dependency injection style - Update Spring Boot version to 3.1.4 - Update README.adoc - Clean up code --- README.adoc | 34 ++++++++----------- complete/build.gradle | 2 +- complete/pom.xml | 2 +- .../batchprocessing/BatchConfiguration.java | 30 ++++++---------- .../BatchProcessingApplication.java | 2 +- .../JobCompletionNotificationListener.java | 12 +++---- .../com/example/batchprocessing/Person.java | 34 +------------------ .../batchprocessing/PersonItemProcessor.java | 6 ++-- initial/build.gradle | 2 +- initial/pom.xml | 2 +- 10 files changed, 40 insertions(+), 86 deletions(-) diff --git a/README.adoc b/README.adoc index fd26e12..99b7a45 100644 --- a/README.adoc +++ b/README.adoc @@ -79,9 +79,7 @@ include::complete/src/main/java/com/example/batchprocessing/Person.java[] ---- ==== -You can instantiate the `Person` class either with first and last name through a -constructor or by setting the properties. - +You can instantiate the `Person` record with first name and last name through the constructor. == Create an Intermediate Processor @@ -131,9 +129,8 @@ parses each line item with enough information to turn it into a `Person`. * `processor()` creates an instance of the `PersonItemProcessor` that you defined earlier, meant to convert the data to upper case. * `writer(DataSource)` creates an `ItemWriter`. This one is aimed at a JDBC destination and -automatically gets a copy of the dataSource created by `@EnableBatchProcessing`. It -includes the SQL statement needed to insert a single `Person`, driven by Java bean -properties. +automatically gets a copy of the dataSource created by Spring Boot. It +includes the SQL statement needed to insert a single `Person`, driven by Java record components. The last chunk (from `src/main/java/com/example/batchprocessing/BatchConfiguration.java`) shows the actual job configuration: @@ -148,12 +145,11 @@ include::complete/src/main/java/com/example/batchprocessing/BatchConfiguration.j The first method defines the job, and the second one defines a single step. Jobs are built from steps, where each step can involve a reader, a processor, and a writer. -In this job definition, you need an incrementer, because jobs use a database to maintain -execution state. You then list each step, (though this job has only one step). The job +You then list each step, (though this job has only one step). The job ends, and the Java API produces a perfectly configured job. In the step definition, you define how much data to write at a time. In this case, it -writes up to ten records at a time. Next, you configure the reader, processor, and writer +writes up to three records at a time. Next, you configure the reader, processor, and writer by using the beans injected earlier. NOTE: `chunk()` is prefixed `` because it is a generic method. This @@ -210,16 +206,16 @@ output: ==== [source,text] ---- -Converting (firstName: Jill, lastName: Doe) into (firstName: JILL, lastName: DOE) -Converting (firstName: Joe, lastName: Doe) into (firstName: JOE, lastName: DOE) -Converting (firstName: Justin, lastName: Doe) into (firstName: JUSTIN, lastName: DOE) -Converting (firstName: Jane, lastName: Doe) into (firstName: JANE, lastName: DOE) -Converting (firstName: John, lastName: Doe) into (firstName: JOHN, lastName: DOE) -Found in the database. -Found in the database. -Found in the database. -Found in the database. -Found in the database. +Converting (Person[firstName=Jill, lastName=Doe]) into (Person[firstName=JILL, lastName=DOE]) +Converting (Person[firstName=Joe, lastName=Doe]) into (Person[firstName=JOE, lastName=DOE]) +Converting (Person[firstName=Justin, lastName=Doe]) into (Person[firstName=JUSTIN, lastName=DOE]) +Converting (Person[firstName=Jane, lastName=Doe]) into (Person[firstName=JANE, lastName=DOE]) +Converting (Person[firstName=John, lastName=Doe]) into (Person[firstName=JOHN, lastName=DOE]) +Found <{Person[firstName=JILL, lastName=DOE]}> in the database. +Found <{Person[firstName=JOE, lastName=DOE]}> in the database. +Found <{Person[firstName=JUSTIN, lastName=DOE]}> in the database. +Found <{Person[firstName=JANE, lastName=DOE]}> in the database. +Found <{Person[firstName=JOHN, lastName=DOE]}> in the database. ---- ==== diff --git a/complete/build.gradle b/complete/build.gradle index 59554b1..c5e019c 100644 --- a/complete/build.gradle +++ b/complete/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '3.1.2' + id 'org.springframework.boot' version '3.1.4' id 'io.spring.dependency-management' version '1.1.2' id 'java' } diff --git a/complete/pom.xml b/complete/pom.xml index 7e34fa3..f9582ce 100644 --- a/complete/pom.xml +++ b/complete/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.1.2 + 3.1.4 com.example diff --git a/complete/src/main/java/com/example/batchprocessing/BatchConfiguration.java b/complete/src/main/java/com/example/batchprocessing/BatchConfiguration.java index cef16d5..e8f21ea 100644 --- a/complete/src/main/java/com/example/batchprocessing/BatchConfiguration.java +++ b/complete/src/main/java/com/example/batchprocessing/BatchConfiguration.java @@ -5,19 +5,16 @@ import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.job.builder.JobBuilder; -import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; -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.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; -import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; @Configuration public class BatchConfiguration { @@ -29,10 +26,8 @@ public FlatFileItemReader reader() { .name("personItemReader") .resource(new ClassPathResource("sample-data.csv")) .delimited() - .names(new String[]{"firstName", "lastName"}) - .fieldSetMapper(new BeanWrapperFieldSetMapper() {{ - setTargetType(Person.class); - }}) + .names("firstName", "lastName") + .targetType(Person.class) .build(); } @@ -44,32 +39,29 @@ public PersonItemProcessor processor() { @Bean public JdbcBatchItemWriter writer(DataSource dataSource) { return new JdbcBatchItemWriterBuilder() - .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>()) .sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)") .dataSource(dataSource) + .beanMapped() .build(); } // end::readerwriterprocessor[] // tag::jobstep[] @Bean - public Job importUserJob(JobRepository jobRepository, - JobCompletionNotificationListener listener, Step step1) { + public Job importUserJob(JobRepository jobRepository,Step step1, JobCompletionNotificationListener listener) { return new JobBuilder("importUserJob", jobRepository) - .incrementer(new RunIdIncrementer()) .listener(listener) - .flow(step1) - .end() + .start(step1) .build(); } @Bean - public Step step1(JobRepository jobRepository, - PlatformTransactionManager transactionManager, JdbcBatchItemWriter writer) { + public Step step1(JobRepository jobRepository, DataSourceTransactionManager transactionManager, + FlatFileItemReader reader, PersonItemProcessor processor, JdbcBatchItemWriter writer) { return new StepBuilder("step1", jobRepository) - . chunk(10, transactionManager) - .reader(reader()) - .processor(processor()) + . chunk(3, transactionManager) + .reader(reader) + .processor(processor) .writer(writer) .build(); } diff --git a/complete/src/main/java/com/example/batchprocessing/BatchProcessingApplication.java b/complete/src/main/java/com/example/batchprocessing/BatchProcessingApplication.java index 1aa5328..139a532 100644 --- a/complete/src/main/java/com/example/batchprocessing/BatchProcessingApplication.java +++ b/complete/src/main/java/com/example/batchprocessing/BatchProcessingApplication.java @@ -6,7 +6,7 @@ @SpringBootApplication public class BatchProcessingApplication { - public static void main(String[] args) throws Exception { + public static void main(String[] args) { System.exit(SpringApplication.exit(SpringApplication.run(BatchProcessingApplication.class, args))); } } diff --git a/complete/src/main/java/com/example/batchprocessing/JobCompletionNotificationListener.java b/complete/src/main/java/com/example/batchprocessing/JobCompletionNotificationListener.java index 7f24b4d..c50a642 100644 --- a/complete/src/main/java/com/example/batchprocessing/JobCompletionNotificationListener.java +++ b/complete/src/main/java/com/example/batchprocessing/JobCompletionNotificationListener.java @@ -2,10 +2,11 @@ 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.JobExecutionListener; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.DataClassRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; @@ -16,7 +17,6 @@ public class JobCompletionNotificationListener implements JobExecutionListener { private final JdbcTemplate jdbcTemplate; - @Autowired public JobCompletionNotificationListener(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @@ -26,11 +26,9 @@ public void afterJob(JobExecution jobExecution) { if(jobExecution.getStatus() == BatchStatus.COMPLETED) { log.info("!!! JOB FINISHED! Time to verify the results"); - jdbcTemplate.query("SELECT first_name, last_name FROM people", - (rs, row) -> new Person( - rs.getString(1), - rs.getString(2)) - ).forEach(person -> log.info("Found <{{}}> in the database.", person)); + jdbcTemplate + .query("SELECT first_name, last_name FROM people", new DataClassRowMapper<>(Person.class)) + .forEach(person -> log.info("Found <{{}}> in the database.", person)); } } } diff --git a/complete/src/main/java/com/example/batchprocessing/Person.java b/complete/src/main/java/com/example/batchprocessing/Person.java index 23db880..cc7b700 100644 --- a/complete/src/main/java/com/example/batchprocessing/Person.java +++ b/complete/src/main/java/com/example/batchprocessing/Person.java @@ -1,37 +1,5 @@ package com.example.batchprocessing; -public class Person { - - private String lastName; - private String firstName; - - public Person() { - } - - public Person(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; - } +public record Person(String firstName, String lastName) { } diff --git a/complete/src/main/java/com/example/batchprocessing/PersonItemProcessor.java b/complete/src/main/java/com/example/batchprocessing/PersonItemProcessor.java index 8c911cb..6bfb146 100644 --- a/complete/src/main/java/com/example/batchprocessing/PersonItemProcessor.java +++ b/complete/src/main/java/com/example/batchprocessing/PersonItemProcessor.java @@ -10,9 +10,9 @@ public class PersonItemProcessor implements ItemProcessor { private static final Logger log = LoggerFactory.getLogger(PersonItemProcessor.class); @Override - public Person process(final Person person) throws Exception { - final String firstName = person.getFirstName().toUpperCase(); - final String lastName = person.getLastName().toUpperCase(); + public Person process(final Person person) { + final String firstName = person.firstName().toUpperCase(); + final String lastName = person.lastName().toUpperCase(); final Person transformedPerson = new Person(firstName, lastName); diff --git a/initial/build.gradle b/initial/build.gradle index 59554b1..c5e019c 100644 --- a/initial/build.gradle +++ b/initial/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '3.1.2' + id 'org.springframework.boot' version '3.1.4' id 'io.spring.dependency-management' version '1.1.2' id 'java' } diff --git a/initial/pom.xml b/initial/pom.xml index 84c5b1e..e1e065c 100644 --- a/initial/pom.xml +++ b/initial/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.1.2 + 3.1.4 com.example