The previous parts of this tutorial have taught you to read the input data of your batch job from different data sources. However, your batch job isn’t very useful because you don’t know how you can save the output data of your batch job.
This time you will learn to write the output data of your Spring Batch job to a CSV file. After you have read this blog post, you:
- Know how you can add a header line to a CSV file.
- Understand how you can transform your domain object into a line that’s written to a CSV file.
- Can write the output data of your batch job to a CSV file.
Let’s start by taking a quick look at your batch job.
This blog post assumes that:
- You are familiar with Spring Batch
- You can get the required dependencies with Maven or Gradle
Introduction to Your Batch Job
The example batch job of this blog post processes the student information of an online course. The input data of this batch job is read from a data source and transformed into 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}
During this blog post you will write the output data of your batch job to a CSV file. To be more specific, this CSV file must must fulfill these requirements:
- It must use semicolon (‘;’) as a separator character.
- Each row must contain the following information: the name of the student, the email address of the student, and the name of the purchased package.
- The created CSV file must have a header row that contains the following the string: ‘NAME;EMAIL_ADDRESS;PACKAGE’.
In other words, the CSV file that contains the processed student information must look 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
Next, you will learn to add a header line to the created CSV file.
Adding a Header Line to the Created CSV File
When you want to add a header line to the created CSV file, you have to write a custom class which implements the FlatFileHeaderCallback interface. You can create this class by following these steps:
- Create a class that implements the
FlatFileHeaderCallbackinterface. - Add a
privateandfinalfield called theheaderto the created class. This field contains the header that’s written to the first line of the created CSV file. - Create a constructor that injects the value of the
headerfield by using constructor injection. - Override the
writeHeader(Writer writer)method of theFlatFileHeaderCallbackinterface. - Write the header to the created CSV file by using the
Writerobject that’s given to thewriteHeader()method as a method parameter.
After you have implemented the FlatFileHeaderCallback interface, the source code of the StringHeaderWriter class looks as follows:
1import org.springframework.batch.item.file.FlatFileHeaderCallback;
2
3import java.io.IOException;
4import java.io.Writer;
5
6public class StringHeaderWriter implements FlatFileHeaderCallback {
7
8 private final String header;
9
10 StringHeaderWriter(String header) {
11 this.header = header;
12 }
13
14 @Override
15 public void writeHeader(Writer writer) throws IOException {
16 writer.write(header);
17 }
18}
You don’t have to:
- Add a newline character to the header because it’s added automatically by Spring Batch.
- Flush the
Writer.
Additional Reading:
Let’s move on and find out how you can write the output data of your batch job a CSV file.
Writing Information to a CSV File
When you want to process the output data of your batch job, you have to configure an ItemWriter bean. Because you have to write the output data to 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 workflow of your Spring 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 a FieldExtractor<T> object that extracts the field values from the object that is written to a CSV file. When you create a new FieldExtractor<T> object, you have to provide one type parameter which specifies the type of object that contains the input data of your ItemWriter.
You can create this object by following these steps:
- Add a
privatemethod to your configuration class and ensure that this method returns aFieldExtractor<StudentDTO>object. - Ensure that this method returns a
BeanWrapperFieldExtractor<StudentDTO>object. When you create a newBeanWrapperFieldExtractor<StudentDTO>object, you must specify the property names of the properties which are written to the created CSV file.
After you have implemented the method which returns a new FieldExtractor<T> object, the source code of your configuration class looks as follows:
1import org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor;
2import org.springframework.batch.item.file.transform.FieldExtractor;
3import org.springframework.context.annotation.Configuration;
4
5@Configuration
6public class SpringBatchExampleJobConfig {
7
8 private FieldExtractor<StudentDTO> createStudentFieldExtractor() {
9 BeanWrapperFieldExtractor<StudentDTO> extractor =
10 new BeanWrapperFieldExtractor<>();
11 extractor.setNames(new String[] {
12 "name",
13 "emailAddress",
14 "purchasedPackage"
15 });
16 return extractor;
17 }
18}
Additional Reading:
Third, you have to create a new LineAggregator<T> object that creates the line which is written to the target file. When you create a new LineAggregator<T> object, you have to provide one type parameter that specifies the type of object that contains the input data of your ItemWriter.
When you want to create this object, you should add a new private method to your configuration class and ensure that this method returns a LineAggregator<StudentDTO> object. After you have added this method to your configuration class, you should implement it by following these steps:
- Create a new
DelimitedLineAggregator<StudentDTO>object. This object transforms the input object into aStringobject that contains the property values provided the configuredFieldExtractor<StudentDTO>object. These property values are separated by the specified delimiter character. - Configure the used delimiter character (’;’).
- Ensure that the created
DelimitedLineAggregator<StudentDTO>object object uses theFieldExtractor<StudentDTO>object which is returned by thecreateStudentFieldExtractor()method. - Return the created
DelimitedLineAggregator<StudentDTO>object.
After you have written the method which creates a new LineAggregator<StudentDTO> object, the source code of your configuration class looks as follows:
1import org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor;
2import org.springframework.batch.item.file.transform.DelimitedLineAggregator;
3import org.springframework.batch.item.file.transform.FieldExtractor;
4import org.springframework.batch.item.file.transform.LineAggregator;
5import org.springframework.context.annotation.Configuration;
6
7@Configuration
8public class SpringBatchExampleJobConfig {
9
10 private LineAggregator<StudentDTO> createStudentLineAggregator() {
11 DelimitedLineAggregator<StudentDTO> lineAggregator =
12 new DelimitedLineAggregator<>();
13 lineAggregator.setDelimiter(";");
14
15 FieldExtractor<StudentDTO> fieldExtractor = createStudentFieldExtractor();
16 lineAggregator.setFieldExtractor(fieldExtractor);
17
18 return lineAggregator;
19 }
20
21 private FieldExtractor<StudentDTO> createStudentFieldExtractor() {
22 BeanWrapperFieldExtractor<StudentDTO> extractor =
23 new BeanWrapperFieldExtractor<>();
24 extractor.setNames(new String[] {
25 "name",
26 "emailAddress",
27 "purchasedPackage"
28 });
29 return extractor;
30 }
31}
Additional Reading:
Fourth, you have to create the method that configures your ItemWriter bean. Ensure that the created method takes an Environment object as a method parameter and returns an ItemWriter<StudentDTO> object.
After you have added this method to your configuration class, its source code looks as follows:
1import org.springframework.batch.item.ItemWriter;
2import org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor;
3import org.springframework.batch.item.file.transform.DelimitedLineAggregator;
4import org.springframework.batch.item.file.transform.FieldExtractor;
5import org.springframework.batch.item.file.transform.LineAggregator;
6import org.springframework.context.annotation.Bean;
7import org.springframework.context.annotation.Configuration;
8import org.springframework.core.env.Environment;
9
10@Configuration
11public class SpringBatchExampleJobConfig {
12
13 @Bean
14 public ItemWriter<StudentDTO> itemWriter(Environment environment) {
15
16 }
17
18 private LineAggregator<StudentDTO> createStudentLineAggregator() {
19 DelimitedLineAggregator<StudentDTO> lineAggregator =
20 new DelimitedLineAggregator<>();
21 lineAggregator.setDelimiter(";");
22
23 FieldExtractor<StudentDTO> fieldExtractor = createStudentFieldExtractor();
24 lineAggregator.setFieldExtractor(fieldExtractor);
25
26 return lineAggregator;
27 }
28
29 private FieldExtractor<StudentDTO> createStudentFieldExtractor() {
30 BeanWrapperFieldExtractor<StudentDTO> extractor
31 = new BeanWrapperFieldExtractor<>();
32 extractor.setNames(new String[] {
33 "name",
34 "emailAddress",
35 "purchasedPackage"
36 });
37 return extractor;
38 }
39}
Fifth, you have to implement the itemWriter() method by following these steps:
- Create a new
FlatFileItemWriterBuilder<StudentDTO>object. This object createsFlatFileItemWriter<StudentDTO>objects which can write the output data of your batch job to a CSV file. - Configure the name of the
ItemWriter. - Ensure that the created
FlatFileItemWriter<StudentDTO>object writes a header line to the created CSV file by using theStringHeaderWriterclass. You can read the header line from a properties file by using theEnvironmentobject given as a method parameter. - Configure the
LineAggregatorobject which constructs the lines which are written to the created CSV file. - Configure the file path of the created CSV file. You can read this information from a properties file by using the
Environmentobject given as a method parameter. - Create a new
FlatFileItemWriter<StudentDTO>object and return the created object.
After you have implemented the itemWriter() method, the source code of your configuration class looks as follows:
1import org.springframework.batch.item.ItemWriter;
2import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
3import org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor;
4import org.springframework.batch.item.file.transform.DelimitedLineAggregator;
5import org.springframework.batch.item.file.transform.FieldExtractor;
6import org.springframework.batch.item.file.transform.LineAggregator;
7import org.springframework.context.annotation.Bean;
8import org.springframework.context.annotation.Configuration;
9import org.springframework.core.env.Environment;
10import org.springframework.core.io.FileSystemResource;
11import org.springframework.core.io.Resource;
12
13@Configuration
14public class SpringBatchExampleJobConfig {
15
16 @Bean
17 public ItemWriter<StudentDTO> itemWriter(Environment environment) {
18 String exportFilePath = environment
19 .getRequiredProperty("batch.job.export.file.path");
20 Resource exportFileResource = new FileSystemResource(exportFilePath);
21
22 String exportFileHeader =
23 environment.getRequiredProperty("batch.job.export.file.header");
24 StringHeaderWriter headerWriter = new StringHeaderWriter(exportFileHeader);
25
26 LineAggregator<StudentDTO> lineAggregator = createStudentLineAggregator();
27
28 return new FlatFileItemWriterBuilder<StudentDTO>()
29 .name("studentWriter")
30 .headerCallback(headerWriter)
31 .lineAggregator(lineAggregator)
32 .resource(exportFileResource)
33 .build();
34 }
35
36 private LineAggregator<StudentDTO> createStudentLineAggregator() {
37 DelimitedLineAggregator<StudentDTO> lineAggregator
38 = new DelimitedLineAggregator<>();
39 lineAggregator.setDelimiter(";");
40
41 FieldExtractor<StudentDTO> fieldExtractor
42 = createStudentFieldExtractor();
43 lineAggregator.setFieldExtractor(fieldExtractor);
44
45 return lineAggregator;
46 }
47
48 private FieldExtractor<StudentDTO> createStudentFieldExtractor() {
49 BeanWrapperFieldExtractor<StudentDTO> extractor =
50 new BeanWrapperFieldExtractor<>();
51 extractor.setNames(new String[] {
52 "name",
53 "emailAddress",
54 "purchasedPackage"
55 });
56 return extractor;
57 }
58}
Additional Reading:
You can now configure an ItemWriter bean which writes the output data of your batch job to a CSV file. Let’s summarize what you learned from this blog post.
Summary
This blog post has taught you four things:
- If you have to write the output data of your batch job to a CSV file, you must use the
FlatFileItemWriter<T>class. - If you have to add a header line to the created CSV file, you have to implement the
FlatFileHeaderCallbackinterface. - The
FlatFileItemWriter<T>class transforms the input objects into lines which are written to the CSV file by using aLineAggregator<T>object. - The
DelimitedLineAggregator<T>class extracts the property values from the processed object by using aFieldExtractor<T>object.
The next part of this tutorial describes how you can write the output data of your batch job to an XML file.
P.S. You can get the example application of this blog post from Github.