The previous parts of my Spring Batch tutorial provided an introduction to Spring Batch and described how you can get the required dependencies by using either Maven or Gradle.
After you have downloaded the required dependencies, you can start writing Spring Batch jobs. The first thing that you have to do is to provide the input data for your batch job. This blog post helps you to read the input data a from CSV file.
After you have read this blog post, you:
- Can read the input data of your batch job from a CSV file.
- Understand how you can transform a line read from a CSV file into a domain object.
Let’s start by taking a quick look at the example application.
This blog post assumes that:
- You are familiar with Spring Batch
- You can get the required dependencies with Maven or Gradle
Introduction to the Example Application
During this blog post you will read the input data of your batch job from a CSV file which contains the student information of an online course. To be more specific, the CSV file contains a student list that provides the following information to your batch job:
- The name of the student.
- The email address of the student.
- The name of the purchased package.
The content of your input file looks as follows:
1NAME;EMAIL_ADDRESS;PACKAGE
2Tony Tester;tony.tester@gmail.com;master
3Nick Newbie;nick.newbie@gmail.com;starter
4Ian Intermediate;ian.intermediate@gmail.com;intermediate
The ItemReader which reads the student list from a CSV file must return StudentDTO objects. The StudentDTO class contains the information of a single student, 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}
Next, you will find out how you can read the input data of your batch job from a CSV file.
Reading the Input Data From a CSV File
You can provide the input data for your batch job by configuring an ItemReader bean. Because you must read the student information from a CSV file, you have to configure this 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 write a private method that returns a LineMapper<StudentDTO> object. This object transforms a String object read from the source CSV file into a domain object. You can write this method by following these steps:
- Create a new
DefaultLineMapper<StudentDTO>object. - Create a new
DelimitedLineTokenizerobject. Ensure that the created object splits the student information line into tokens by using semicolon (;) as a delimiter character and configure the names of each token. The names of these tokens must match with the field names of the target class (StudentDTO). - Ensure that the
DefaultLineMapper<StudentDTO>object splits each row into tokens by using the createdDelimitedLineTokenizerobject. - Create a new
BeanWrapperFieldSetMapper<StudentDTO>object which maps the tokenized input data into a domain object by using bean property paths. Remember to ensure that the created object creates newStudentDTOobjects. - Ensure that the
DefaultLineMapper<StudentDTO>object creates newStudentDTOobjects by using the createdBeanWrapperFieldSetMapper<StudentDTO>object. - Return the created
DefaultLineMapper<StudentDTO>object.
After you have written this method, the source code of your configuration class looks as follows:
1import org.springframework.batch.item.file.LineMapper;
2import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
3import org.springframework.batch.item.file.mapping.DefaultLineMapper;
4import org.springframework.batch.item.file.mapping.FieldSetMapper;
5import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
6import org.springframework.batch.item.file.transform.LineTokenizer;
7import org.springframework.context.annotation.Configuration;
8
9@Configuration
10public class SpringBatchExampleJobConfig {
11
12 private LineMapper<StudentDTO> createStudentLineMapper() {
13 DefaultLineMapper<StudentDTO> studentLineMapper = new DefaultLineMapper<>();
14
15 LineTokenizer studentLineTokenizer = createStudentLineTokenizer();
16 studentLineMapper.setLineTokenizer(studentLineTokenizer);
17
18 FieldSetMapper<StudentDTO> studentInformationMapper =
19 createStudentInformationMapper();
20 studentLineMapper.setFieldSetMapper(studentInformationMapper);
21
22 return studentLineMapper;
23 }
24
25 private LineTokenizer createStudentLineTokenizer() {
26 DelimitedLineTokenizer studentLineTokenizer = new DelimitedLineTokenizer();
27 studentLineTokenizer.setDelimiter(";");
28 studentLineTokenizer.setNames(new String[]{
29 "name",
30 "emailAddress",
31 "purchasedPackage"
32 });
33 return studentLineTokenizer;
34 }
35
36 private FieldSetMapper<StudentDTO> createStudentInformationMapper() {
37 BeanWrapperFieldSetMapper<StudentDTO> studentInformationMapper =
38 new BeanWrapperFieldSetMapper<>();
39 studentInformationMapper.setTargetType(StudentDTO.class);
40 return studentInformationMapper;
41 }
42}
Additional Reading:
Third, you have to create a method that configures your ItemReader bean and ensure that this method returns an ItemReader<StudentDTO> object. After you have created this method, you have to implement it by following these steps:
- Create a new
FlatItemReaderBuilder<StudentDTO>object. This builder createsFlatItemReader<StudentDTO>objects which read lines from the specifiedResource. - Configure the name of the created
ItemReader. - Configure the location of the CSV file which contains the input data of your batch job. Because I wanted to create an example application that’s as easy to run as possible, I ensured that the input file (data/students.csv) of your batch job is found from the classpath
- Ignore the header line of the CSV file.
- Configure the used
LineMapper<StudentDTO>object which transforms aStringobject read from the CSV file into a domain object (StudentDTO). - Create a new
FlatItemReader<StudentDTO>object and return the created object.
After you have configured your ItemReader bean, the source code of your configuration class looks as follows:
1import org.springframework.batch.item.ItemReader;
2import org.springframework.batch.item.file.LineMapper;
3import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
4import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
5import org.springframework.batch.item.file.mapping.DefaultLineMapper;
6import org.springframework.batch.item.file.mapping.FieldSetMapper;
7import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
8import org.springframework.batch.item.file.transform.LineTokenizer;
9import org.springframework.context.annotation.Bean;
10import org.springframework.context.annotation.Configuration;
11import org.springframework.core.io.ClassPathResource;
12
13@Configuration
14public class SpringBatchExampleJobConfig {
15
16 @Bean
17 public ItemReader<StudentDTO> itemReader() {
18 LineMapper<StudentDTO> studentLineMapper = createStudentLineMapper();
19
20 return new FlatFileItemReaderBuilder<StudentDTO>()
21 .name("studentReader")
22 .resource(new ClassPathResource("data/students.csv"))
23 .linesToSkip(1)
24 .lineMapper(studentLineMapper)
25 .build();
26 }
27
28 private LineMapper<StudentDTO> createStudentLineMapper() {
29 DefaultLineMapper<StudentDTO> studentLineMapper = new DefaultLineMapper<>();
30
31 LineTokenizer studentLineTokenizer = createStudentLineTokenizer();
32 studentLineMapper.setLineTokenizer(studentLineTokenizer);
33
34 FieldSetMapper<StudentDTO> studentInformationMapper =
35 createStudentInformationMapper();
36 studentLineMapper.setFieldSetMapper(studentInformationMapper);
37
38 return studentLineMapper;
39 }
40
41 private LineTokenizer createStudentLineTokenizer() {
42 DelimitedLineTokenizer studentLineTokenizer = new DelimitedLineTokenizer();
43 studentLineTokenizer.setDelimiter(";");
44 studentLineTokenizer.setNames(new String[]{
45 "name",
46 "emailAddress",
47 "purchasedPackage"
48 });
49 return studentLineTokenizer;
50 }
51
52 private FieldSetMapper<StudentDTO> createStudentInformationMapper() {
53 BeanWrapperFieldSetMapper<StudentDTO> studentInformationMapper =
54 new BeanWrapperFieldSetMapper<>();
55 studentInformationMapper.setTargetType(StudentDTO.class);
56 return studentInformationMapper;
57 }
58}
You must configure the location of your input file by creating a new ClassPathResource object because I wanted to create an example application that’s as easy to run as possible. Typically, the input file of your batch job is found from the file system. This means that you can configure its location by creating a new FileSystemResource object.
Additional Reading:
You can now read the input data of your batch job from a CSV file. Let’s summarize what you learned from this blog post.
Summary
This blog post has taught you four things:
- You can read data from a CSV file by using the
FlatItemReader<T>class. - The
FlatItemReader<T>class transforms lines read from the input file into domain objects by using aLineMapper<T>object. - The
DelimitedLineTokenizerclass can split the input data into tokens by using the specified delimiter character. Also, this class allows you to configure the field names which are used to populate the fields of the created domain object. - The
BeanWrapperFieldSetMapper<T>class can transform the tokenized input data into a domain object by using bean property paths.
The next part of this tutorial describes how you can read the input data of your batch job from an XML file.
P.S. You can get the example application from Github.