diff --git a/pom.xml b/pom.xml
index 3fca452552..17e4fe1584 100644
--- a/pom.xml
+++ b/pom.xml
@@ -617,7 +617,8 @@
spring-aop
spring-apache-camel
- spring-batch
+ spring-batch
+ spring-batch-2
spring-bom
spring-boot-modules
spring-boot-rest
diff --git a/spring-batch-2/pom.xml b/spring-batch-2/pom.xml
new file mode 100644
index 0000000000..54df6d43e8
--- /dev/null
+++ b/spring-batch-2/pom.xml
@@ -0,0 +1,57 @@
+
+
+ 4.0.0
+ spring-batch-2
+ 0.1-SNAPSHOT
+ spring-batch-2
+ jar
+ http://maven.apache.org
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-batch
+ 2.3.6.RELEASE
+
+
+ org.hsqldb
+ hsqldb
+ ${hsqldb.version}
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring.boot.batch.version}
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+ org.springframework.batch
+ spring-batch-test
+ ${spring.batch.test.version}
+ test
+
+
+
+
+ 2.3.6.RELEASE
+ 4.2.4.RELEASE
+ 2.5.1
+
+
+
diff --git a/spring-batch-2/src/main/java/com/baeldung/batch/BatchConfiguration.java b/spring-batch-2/src/main/java/com/baeldung/batch/BatchConfiguration.java
new file mode 100644
index 0000000000..0c053dd86c
--- /dev/null
+++ b/spring-batch-2/src/main/java/com/baeldung/batch/BatchConfiguration.java
@@ -0,0 +1,81 @@
+package com.baeldung.batch;
+
+import javax.sql.DataSource;
+
+import org.springframework.batch.core.Job;
+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.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ClassPathResource;
+
+@Configuration
+@EnableBatchProcessing
+public class BatchConfiguration {
+
+ @Autowired
+ public JobBuilderFactory jobBuilderFactory;
+
+ @Autowired
+ public StepBuilderFactory stepBuilderFactory;
+
+ @Value("${file.input}")
+ private String fileInput;
+
+ @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();
+ }
+
+ @Bean
+ public CoffeeItemProcessor processor() {
+ return new CoffeeItemProcessor();
+ }
+
+ @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();
+ }
+
+ @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();
+ }
+
+}
diff --git a/spring-batch-2/src/main/java/com/baeldung/batch/Coffee.java b/spring-batch-2/src/main/java/com/baeldung/batch/Coffee.java
new file mode 100644
index 0000000000..4dfcd9959c
--- /dev/null
+++ b/spring-batch-2/src/main/java/com/baeldung/batch/Coffee.java
@@ -0,0 +1,47 @@
+package com.baeldung.batch;
+
+public class Coffee {
+
+ private String brand;
+ private String origin;
+ private String characteristics;
+
+ public Coffee() {
+ }
+
+ public Coffee(String brand, String origin, String characteristics) {
+ this.brand = brand;
+ this.origin = origin;
+ this.characteristics = characteristics;
+ }
+
+ public String getBrand() {
+ return brand;
+ }
+
+ public void setBrand(String brand) {
+ this.brand = brand;
+ }
+
+ public String getOrigin() {
+ return origin;
+ }
+
+ public void setOrigin(String origin) {
+ this.origin = origin;
+ }
+
+ public String getCharacteristics() {
+ return characteristics;
+ }
+
+ public void setCharacteristics(String characteristics) {
+ this.characteristics = characteristics;
+ }
+
+ @Override
+ public String toString() {
+ return "Coffee [brand=" + getBrand() + ", origin=" + getOrigin() + ", characteristics=" + getCharacteristics() + "]";
+ }
+
+}
diff --git a/spring-batch-2/src/main/java/com/baeldung/batch/CoffeeItemProcessor.java b/spring-batch-2/src/main/java/com/baeldung/batch/CoffeeItemProcessor.java
new file mode 100644
index 0000000000..b154b80453
--- /dev/null
+++ b/spring-batch-2/src/main/java/com/baeldung/batch/CoffeeItemProcessor.java
@@ -0,0 +1,24 @@
+package com.baeldung.batch;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.batch.item.ItemProcessor;
+
+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;
+ }
+
+}
diff --git a/spring-batch-2/src/main/java/com/baeldung/batch/JobCompletionNotificationListener.java b/spring-batch-2/src/main/java/com/baeldung/batch/JobCompletionNotificationListener.java
new file mode 100644
index 0000000000..ca1de40aea
--- /dev/null
+++ b/spring-batch-2/src/main/java/com/baeldung/batch/JobCompletionNotificationListener.java
@@ -0,0 +1,34 @@
+package com.baeldung.batch;
+
+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.stereotype.Component;
+
+@Component
+public class JobCompletionNotificationListener extends JobExecutionListenerSupport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JobCompletionNotificationListener.class);
+
+ private final JdbcTemplate jdbcTemplate;
+
+ @Autowired
+ public JobCompletionNotificationListener(JdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = jdbcTemplate;
+ }
+
+ @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));
+ }
+ }
+}
diff --git a/spring-batch-2/src/main/java/com/baeldung/batch/SpringBootBatchProcessingApplication.java b/spring-batch-2/src/main/java/com/baeldung/batch/SpringBootBatchProcessingApplication.java
new file mode 100644
index 0000000000..7682124b8d
--- /dev/null
+++ b/spring-batch-2/src/main/java/com/baeldung/batch/SpringBootBatchProcessingApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.batch;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringBootBatchProcessingApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBootBatchProcessingApplication.class, args);
+ }
+
+}
diff --git a/spring-batch-2/src/main/resources/application.properties b/spring-batch-2/src/main/resources/application.properties
new file mode 100644
index 0000000000..0b8c56d3f8
--- /dev/null
+++ b/spring-batch-2/src/main/resources/application.properties
@@ -0,0 +1 @@
+file.input=coffee-list.csv
\ No newline at end of file
diff --git a/spring-batch-2/src/main/resources/coffee-list.csv b/spring-batch-2/src/main/resources/coffee-list.csv
new file mode 100644
index 0000000000..6ceef00556
--- /dev/null
+++ b/spring-batch-2/src/main/resources/coffee-list.csv
@@ -0,0 +1,3 @@
+Blue Mountain,Jamaica,Fruity
+Lavazza,Colombia,Strong
+Folgers,America,Smokey
\ No newline at end of file
diff --git a/spring-batch-2/src/main/resources/logback.xml b/spring-batch-2/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7d900d8ea8
--- /dev/null
+++ b/spring-batch-2/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-batch-2/src/main/resources/schema-all.sql b/spring-batch-2/src/main/resources/schema-all.sql
new file mode 100644
index 0000000000..9f698f7d81
--- /dev/null
+++ b/spring-batch-2/src/main/resources/schema-all.sql
@@ -0,0 +1,8 @@
+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)
+);
\ No newline at end of file
diff --git a/spring-batch-2/src/test/java/com/baeldung/batch/SpringBootBatchIntegrationTest.java b/spring-batch-2/src/test/java/com/baeldung/batch/SpringBootBatchIntegrationTest.java
new file mode 100644
index 0000000000..ba2b8a6a13
--- /dev/null
+++ b/spring-batch-2/src/test/java/com/baeldung/batch/SpringBootBatchIntegrationTest.java
@@ -0,0 +1,49 @@
+package com.baeldung.batch;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.batch.core.ExitStatus;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobInstance;
+import org.springframework.batch.test.JobLauncherTestUtils;
+import org.springframework.batch.test.JobRepositoryTestUtils;
+import org.springframework.batch.test.context.SpringBatchTest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@SpringBatchTest
+@SpringBootTest
+@DirtiesContext
+@PropertySource("classpath:application.properties")
+@RunWith(SpringRunner.class)
+public class SpringBootBatchIntegrationTest {
+
+ @Autowired
+ private JobLauncherTestUtils jobLauncherTestUtils;
+
+ @Autowired
+ private JobRepositoryTestUtils jobRepositoryTestUtils;
+
+ @After
+ public void cleanUp() {
+ jobRepositoryTestUtils.removeJobExecutions();
+ }
+
+ @Test
+ public void givenCoffeeList_whenJobExecuted_thenSuccess() throws Exception {
+ JobExecution jobExecution = jobLauncherTestUtils.launchJob();
+ JobInstance jobInstance = jobExecution.getJobInstance();
+ ExitStatus jobExitStatus = jobExecution.getExitStatus();
+
+ assertThat(jobInstance.getJobName(), is("importUserJob"));
+ assertThat(jobExitStatus.getExitCode(), is("COMPLETED"));
+ }
+
+}