Spring Batch has a good support for reading the input data of a batch job from different data sources such as files (CSV, XML) and databases.
However, it’s quite common that you have to read the input data from a data source that’s not supported out of the box. This means that you have to implement a component that reads the input data from your data source.
This blog post helps you to solve that problem. After you have read this blog post, you:
- Understand how you can implement a custom
ItemReader. - Know how you can configure the
ItemReaderbean which provides the input data for your batch job.
Let’s begin.
This blog post assumes that:
- You are familiar with Spring Batch
- You can get the required dependencies with Maven or Gradle
Creating a Custom ItemReader
You can create a custom ItemReader by following these steps:
First, you have to create a class that implements the ItemReader<T> interface and provide the type of the returned object as a type parameter.
Second, you have to implement the T read() method of the ItemReader<T> interface by following these rules:
- The
read()method returns an object that contains the information of the next item. - If the next item isn’t found, the
read()method must returnnull.
Additional Reading:
Let’s create a custom ItemReader that returns the student information of an online testing course as StudentDTO objects which are read from the memory.
The StudentDTO class is a simple data transfer object, and its source code looks as follows:
1public class StudentDTO {
2
3 private String emailAddress;
4 private String name;
5 private String purchasedPackage;
6
7 public StudentDTO() {}
8
9 public String getEmailAddress() {
10 return emailAddress;
11 }
12
13 public String getName() {
14 return name;
15 }
16
17 public String getPurchasedPackage() {
18 return purchasedPackage;
19 }
20
21 public void setEmailAddress(String emailAddress) {
22 this.emailAddress = emailAddress;
23 }
24
25 public void setName(String name) {
26 this.name = name;
27 }
28
29 public void setPurchasedPackage(String purchasedPackage) {
30 this.purchasedPackage = purchasedPackage;
31 }
32}
You can implement your ItemReader by following these steps:
First, you have to create a class that implements the ItemReader<T> interface and specify the type of the object which is returned by the T read() method. After you have created this class, its source code looks as follows:
1import org.springframework.batch.item.ItemReader;
2
3public class InMemoryStudentReader implements ItemReader<StudentDTO> {
4}
Second, you have to initialize the input data that’s returned by your ItemReader. You can initialize your input data by following these steps:
- Add a
List<Student>field to yourItemReaderclass. This field contains the student information of the course. - Add an
intfield callednextStudentIndexto yourItemReaderclass. This field contains the index of the nextStudentDTOobject that’s returned by yourItemReader. - Add a private
initialize()method to yourItemReaderclass. This method creates the student data and sets the index of the next student to 0. - Create a constructor that invokes the
initialize()method.
After you have initialized your input data, the source code of your ItemReader class looks as follows:
1import org.springframework.batch.item.ItemReader;
2
3import java.util.Arrays;
4import java.util.Collections;
5import java.util.List;
6
7public class InMemoryStudentReader implements ItemReader<StudentDTO> {
8
9 private int nextStudentIndex;
10 private List<StudentDTO> studentData;
11
12 InMemoryStudentReader() {
13 initialize();
14 }
15
16 private void initialize() {
17 StudentDTO tony = new StudentDTO();
18 tony.setEmailAddress("tony.tester@gmail.com");
19 tony.setName("Tony Tester");
20 tony.setPurchasedPackage("master");
21
22 StudentDTO nick = new StudentDTO();
23 nick.setEmailAddress("nick.newbie@gmail.com");
24 nick.setName("Nick Newbie");
25 nick.setPurchasedPackage("starter");
26
27 StudentDTO ian = new StudentDTO();
28 ian.setEmailAddress("ian.intermediate@gmail.com");
29 ian.setName("Ian Intermediate");
30 ian.setPurchasedPackage("intermediate");
31
32 studentData = Collections.unmodifiableList(Arrays.asList(tony, nick, ian));
33 nextStudentIndex = 0;
34 }
35}
Third, you have to implement the read() method of the ItemReader interface by following these rules:
- If the next student is found, return the found
StudentDTOobject and increase the value of thenextStudentIndexfield by 1. - If the next student isn’t found, set the value of the
nextStudentIndexfield to 0. - If the next student isn’t found, return
null.
After you have implemented the read() method, the source code of your ItemReader class looks as follows:
1import org.springframework.batch.item.ItemReader;
2
3import java.util.Arrays;
4import java.util.Collections;
5import java.util.List;
6
7public class InMemoryStudentReader implements ItemReader<StudentDTO> {
8
9 private int nextStudentIndex;
10 private List<StudentDTO> studentData;
11
12 InMemoryStudentReader() {
13 initialize();
14 }
15
16 private void initialize() {
17 StudentDTO tony = new StudentDTO();
18 tony.setEmailAddress("tony.tester@gmail.com");
19 tony.setName("Tony Tester");
20 tony.setPurchasedPackage("master");
21
22 StudentDTO nick = new StudentDTO();
23 nick.setEmailAddress("nick.newbie@gmail.com");
24 nick.setName("Nick Newbie");
25 nick.setPurchasedPackage("starter");
26
27 StudentDTO ian = new StudentDTO();
28 ian.setEmailAddress("ian.intermediate@gmail.com");
29 ian.setName("Ian Intermediate");
30 ian.setPurchasedPackage("intermediate");
31
32 studentData = Collections.unmodifiableList(Arrays.asList(tony, nick, ian));
33 nextStudentIndex = 0;
34 }
35
36 @Override
37 public StudentDTO read() throws Exception {
38 StudentDTO nextStudent = null;
39
40 if (nextStudentIndex < studentData.size()) {
41 nextStudent = studentData.get(nextStudentIndex);
42 nextStudentIndex++;
43 }
44 else {
45 nextStudentIndex = 0;
46 }
47
48 return nextStudent;
49 }
50}
After you have created your custom ItemReader class, you have to configure the ItemReader bean that provides the input data for your Spring Batch job. Next, you will find out how you can configure this bean.
Configuring the ItemReader Bean
You can configure your ItemReader bean by following these steps:
First, you have to create the configuration class that contains the beans which describe the flow of your batch job. The source code of your configuration class looks as follows:
1import org.springframework.context.annotation.Configuration;
2
3@Configuration
4public class SpringBatchExampleJobConfig {
5}
Second, you have to create the method that configures your ItemReader bean. This method must return an ItemReader<StudentDTO> object. After you have created this method, the source code of your configuration class looks as follows:
1import org.springframework.batch.item.ItemReader;
2import org.springframework.context.annotation.Bean;
3import org.springframework.context.annotation.Configuration;
4
5@Configuration
6public class SpringBatchExampleJobConfig {
7
8 @Bean
9 public ItemReader<StudentDTO> itemReader() {
10
11 }
12}
Third, you have to ensure that the ItemReader() method returns a new InMemoryStudentReader object. After you have implemented the ItemReader() method, the source code of your configuration class looks as follows:
1import org.springframework.batch.item.ItemReader;
2import org.springframework.context.annotation.Bean;
3import org.springframework.context.annotation.Configuration;
4
5@Configuration
6public class SpringBatchExampleJobConfig {
7
8 @Bean
9 public ItemReader<StudentDTO> itemReader() {
10 return new InMemoryStudentReader();
11 }
12}
You can now create a custom ItemReader class and you understand how you can configure an ItemReader bean which provides the input data for your batch job. Let’s summarize what you learned from this blog post.
Summary
This blog post has taught you four things:
- You can create a custom
ItemReaderby implementing theItemReader<T>interface. - When you implement the
ItemReader<T>interface, you must provide the type of the returned object as a type parameter. - The
T read()method of theItemReader<T>interface must return the nextTobject. If the next object isn’t found, it must returnnull. - After you have created your custom
ItemReaderclass, you have to configure theItemReaderbean that provides the input data for your Spring Batch job.
The next part of this tutorial describes how you can create a custom ItemReader that reads the input data of your batch job by using an external REST API.
P.S. You can get the example applications of this blog post from Github.