1. Overview Spring Batch reads data through the ItemReader interface. The framework ships with readers for most common data sources (full list ). This article covers the five most commonly used readers:
FlatFileItemReader: reads data from flat text files;
JdbcPagingItemReader: reads data from a database with pagination;
StaxEventItemReader: reads data from XML files;
JsonItemReader: reads data from JSON files;
MultiResourceItemReader: reads data from multiple text files;
2. Usage Examples The entity class Ticket.class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import lombok.Data;import java.math.BigDecimal;@Data public class Ticket { private String departureStation; private String arrivalStation; private BigDecimal price; @Override public String toString () { return String.format("Departure: %s; Arrival: %s; Price: %s" , departureStation, arrivalStation, price); } }
2.1 FlatFileItemReader — Reading Flat Files The ticket.csv file:
1 2 3 4 5 Hefei,Bengbu,60.00 Nanjing,Bengbu,70.00 Shanghai,Bengbu,220.00 Shanghai,Hangzhou,75.20 Shanghai,Kunshan,19.00
Each line in the file represents one Ticket object, with fields separated by commas. FlatFileItemReader parses the file line by line and maps each row to a POJO.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @Bean public Job testFlatItemFileReaderJob () { return jobBuilderFactory.get("testFlatItemFileReaderJob" ) .incrementer(new RunIdIncrementer()) .flow(testFlatFileItemReaderStep) .end() .build(); } @Bean ("testFlatFileItemReaderStep" )public Step testFlatFileItemReaderStep (PlatformTransactionManager transactionManager) { return stepBuilderFactory.get("testFlatFileItemReaderStep" ) .transactionManager(transactionManager) .<Ticket, Ticket>chunk(10 ) .reader(ticketFileItemReader) .writer(list -> list.forEach(System.out::println)) .build(); } @Bean ("ticketFileItemReader" )public FlatFileItemReader<Ticket> ticketFileItemReader () { return new FlatFileItemReaderBuilder<Ticket>() .name("ticketFileItemReader" ) .resource(new ClassPathResource("ticket.csv" )) .delimited() .names(new String[]{"departureStation" , "arrivalStation" , "price" }) .fieldSetMapper(new BeanWrapperFieldSetMapper<Ticket>() {{ setTargetType(Ticket.class ) ; }}) .build(); }
Start the application. Console output:
1 2 3 4 5 6 7 8 2022 -06 -02 13 :50 :23.538 INFO 77808 --- [restartedMain] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=testFlatItemFileReaderJob]] launched with the following parameters: [{run.id=2 }]2022 -06 -02 13 :50 :23.599 INFO 77808 --- [restartedMain] o.s.batch.core.job.SimpleStepHandler : Executing step: [testFlatFileItemReaderStep]Departure: Hefei; Arrival: Bengbu; Price: 60.00 Departure: Nanjing; Arrival: Bengbu; Price: 70.00 Departure: Shanghai; Arrival: Bengbu; Price: 220.00 Departure: Shanghai; Arrival: Hangzhou; Price: 75.20 Departure: Shanghai; Arrival: Kunshan; Price: 19.00 2022 -06 -02 13 :50 :23.680 INFO 77808 --- [restartedMain] o.s.batch.core.step.AbstractStep : Step: [testFlatFileItemReaderStep] executed in 79 ms
2.2 JdbcPagingItemReader — Reading from a Database Read records from the student table in MySQL with pagination and print them out.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 @Bean public Job testDatabaseItemReaderJob () { return jobBuilderFactory.get("testDatabaseItemReaderJob" ) .incrementer(new RunIdIncrementer()) .flow(testDatabaseItemReaderStep) .end() .build(); } @Bean ("testDatabaseItemReaderStep" )public Step testDatabaseItemReaderStep (PlatformTransactionManager transactionManager) { return stepBuilderFactory.get("testDatabaseItemReaderStep" ) .transactionManager(transactionManager) .<Student, Student>chunk(10 ) .reader(studentItemReader) .writer(list -> list.forEach(System.out::println)) .build(); } @Bean ("studentItemReader" )public JdbcPagingItemReader<Student> studentItemReader () { MySqlPagingQueryProvider queryProvider = new MySqlPagingQueryProvider(); queryProvider.setSelectClause("student_id, name, address" ); queryProvider.setFromClause("from student_source" ); Map<String, Order> sortKeys = new HashMap<>(1 ); sortKeys.put("student_id" , Order.ASCENDING); queryProvider.setSortKeys(sortKeys); return new JdbcPagingItemReaderBuilder<Student>() .name("studentItemReader" ) .dataSource(batchDemoDB) .fetchSize(1000 ) .rowMapper(new StudentRowMapper()) .queryProvider(queryProvider) .build(); } public class StudentRowMapper implements RowMapper <Student > { @Override public Student mapRow (ResultSet rs, int rowNum) throws SQLException { Student student = new Student(); student.setStudentId(rs.getLong("student_id" )); student.setName(rs.getString("name" )); student.setAddress(rs.getString("address" )); return student; } } @Primary @Bean (name = "batchDemoDB" )@ConfigurationProperties (prefix = "spring.datasource.batch-demo" )public DataSource druidDataSource () { return DataSourceBuilder.create().type(HikariDataSource.class ).build () ; }
Start the application. Console output:
1 2 3 4 5 6 7 8 9 10 11 12 13 2022 -06 -02 14 :00 :19.010 INFO 67748 --- [restartedMain] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=testDatabaseItemReaderJob]] launched with the following parameters: [{run.id=2 }]2022 -06 -02 14 :00 :19.107 INFO 67748 --- [restartedMain] o.s.batch.core.job.SimpleStepHandler : Executing step: [testDatabaseItemReaderStep]name: Zhang San1, address: Shanghai1 name: Zhang San2, address: Shanghai2 name: Zhang San3, address: Shanghai3 name: Zhang San4, address: Shanghai4 name: Zhang San5, address: Shanghai5 name: Zhang San6, address: Shanghai6 name: Zhang San7, address: Shanghai7 name: Zhang San8, address: Shanghai8 name: Zhang San9, address: Shanghai9 name: Zhang San10, address: Shanghai10 2022 -06 -02 14 :00 :19.284 INFO 67748 --- [restartedMain] o.s.batch.core.step.AbstractStep : Step: [testDatabaseItemReaderStep] executed in 176 ms
2.3 StaxEventItemReader — Reading XML Files The ticket.xml file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?xml version="1.0" encoding="UTF-8"?> <tickets > <ticket > <departureStation > Hefei</departureStation > <arrivalStation > Bengbu</arrivalStation > <price > 60.00</price > </ticket > <ticket > <departureStation > Nanjing</departureStation > <arrivalStation > Bengbu</arrivalStation > <price > 70.00</price > </ticket > <ticket > <departureStation > Shanghai</departureStation > <arrivalStation > Bengbu</arrivalStation > <price > 220.00</price > </ticket > <ticket > <departureStation > Shanghai</departureStation > <arrivalStation > Hangzhou</arrivalStation > <price > 75.20</price > </ticket > <ticket > <departureStation > Shanghai</departureStation > <arrivalStation > Kunshan</arrivalStation > <price > 19.00</price > </ticket > </tickets >
The file contains multiple <ticket> elements, each representing one Ticket object. Each <ticket> element has three child elements corresponding to the three properties.
Mapping XML to objects requires OXM (Object/XML Mapping). The recommended approach is spring-oxm. Add these dependencies:
1 2 3 4 5 6 7 8 9 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.11.1</version> </dependency>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 @Bean public Job testXmlItemReaderJob () { return jobBuilderFactory.get("testXmlItemReaderJob" ) .incrementer(new RunIdIncrementer()) .flow(testXmlItemReaderStep) .end() .build(); } @Bean ("testXmlItemReaderStep" )public Step testXmlItemReaderStep (PlatformTransactionManager transactionManager) { return stepBuilderFactory.get("testXmlItemReaderStep" ) .transactionManager(transactionManager) .<Ticket, Ticket>chunk(10 ) .reader(ticketXmlItemReader) .writer(list -> list.forEach(System.out::println)) .build(); } @Bean ("ticketXmlItemReader" )public StaxEventItemReader<Ticket> itemReader () { return new StaxEventItemReaderBuilder<Ticket>() .name("ticketXmlItemReader" ) .resource(new ClassPathResource("ticket.xml" )) .addFragmentRootElements("ticket" ) .unmarshaller(ticketMarshaller) .build(); } @Bean ("ticketMarshaller" )public XStreamMarshaller ticketMarshaller () { Map<String, Class<Ticket>> aliases = new HashMap<>(1 ); aliases.put("ticket" , Ticket.class ) ; XStreamMarshaller marshaller = new XStreamMarshaller(); marshaller.setAliases(aliases); return marshaller; }
Start the application. Console output:
1 2 3 4 5 6 7 8 2022 -06 -02 14 :15 :48.444 INFO 87024 --- [restartedMain] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=testXmlItemReaderJob]] launched with the following parameters: [{run.id=3 }]2022 -06 -02 14 :15 :48.503 INFO 87024 --- [restartedMain] o.s.batch.core.job.SimpleStepHandler : Executing step: [testXmlItemReaderStep]Departure: Hefei; Arrival: Bengbu; Price: 60.00 Departure: Nanjing; Arrival: Bengbu; Price: 70.00 Departure: Shanghai; Arrival: Bengbu; Price: 220.00 Departure: Shanghai; Arrival: Hangzhou; Price: 75.20 Departure: Shanghai; Arrival: Kunshan; Price: 19.00 2022 -06 -02 14 :15 :48.710 INFO 87024 --- [restartedMain] o.s.batch.core.step.AbstractStep : Step: [testXmlItemReaderStep] executed in 205 ms
2.4 JsonItemReader — Reading JSON Files The ticket.json file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [ { "departureStation" : "Hefei" , "arrivalStation" : "Bengbu" , "price" : "60.00" }, { "departureStation" : "Nanjing" , "arrivalStation" : "Bengbu" , "price" : "70.00" }, { "departureStation" : "Shanghai" , "arrivalStation" : "Bengbu" , "price" : "220.00" }, { "departureStation" : "Shanghai" , "arrivalStation" : "Hangzhou" , "price" : "75.20" }, { "departureStation" : "Shanghai" , "arrivalStation" : "Kunshan" , "price" : "19.00" } ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Bean public Job testJsonItemReaderJob () { return jobBuilderFactory.get("testJsonItemReaderJob" ) .incrementer(new RunIdIncrementer()) .flow(testJsonItemReaderStep) .end() .build(); } @Bean ("testJsonItemReaderStep" )public Step testJsonItemReaderStep (PlatformTransactionManager transactionManager) { return stepBuilderFactory.get("testJsonItemReaderStep" ) .transactionManager(transactionManager) .<Ticket, Ticket>chunk(10 ) .reader(ticketJsonItemReader) .writer(list -> list.forEach(System.out::println)) .build(); } @Bean ("ticketJsonItemReader" )public JsonItemReader<Ticket> ticketJsonItemReader () { return new JsonItemReaderBuilder<Ticket>() .name("ticketJsonItemReader" ) .jsonObjectReader(new JacksonJsonObjectReader<>(Ticket.class )) .resource(new ClassPathResource("ticket.json")) .build(); }
Start the application. Console output:
1 2 3 4 5 6 7 8 2022 -06 -02 14 :25 :38.142 INFO 76544 --- [restartedMain] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=testJsonItemReaderJob]] launched with the following parameters: [{run.id=2 }]2022 -06 -02 14 :25 :38.211 INFO 76544 --- [restartedMain] o.s.batch.core.job.SimpleStepHandler : Executing step: [testJsonItemReaderStep]Departure: Hefei; Arrival: Bengbu; Price: 60.00 Departure: Nanjing; Arrival: Bengbu; Price: 70.00 Departure: Shanghai; Arrival: Bengbu; Price: 220.00 Departure: Shanghai; Arrival: Hangzhou; Price: 75.20 Departure: Shanghai; Arrival: Kunshan; Price: 19.00 2022 -06 -02 14 :25 :38.328 INFO 76544 --- [restartedMain] o.s.batch.core.step.AbstractStep : Step: [testJsonItemReaderStep] executed in 116 ms
2.5 MultiResourceItemReader — Reading Multiple Files Reading from multiple files works on the same principle as reading a single flat file. MultiResourceItemReader adds a delegation layer on top of FlatFileItemReader. The requirement is that all files share the same data structure. Here is an example reading from ticket-1.csv and ticket-2.csv:
1 2 3 Hefei,Bengbu,60.00 Nanjing,Bengbu,70.00 Shanghai,Bengbu,220.00
1 2 Shanghai,Hangzhou,75.20 Shanghai,Kunshan,19.00
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 @Bean public Job testMultiFileItemReaderJob () { return jobBuilderFactory.get("testMultiFileItemReaderJob" ) .incrementer(new RunIdIncrementer()) .flow(testMultiFileItemReaderStep) .end() .build(); } @Bean ("testMultiFileItemReaderStep" )public Step testMultiFileItemReaderStep1 (PlatformTransactionManager transactionManager) { return stepBuilderFactory.get("testMultiFileItemReaderStep" ) .transactionManager(transactionManager) .<Ticket, Ticket>chunk(10 ) .reader(ticketMultiFileItemReader) .writer(list -> list.forEach(System.out::println)) .build(); } @Bean ("ticketMultiFileItemReader" )public MultiResourceItemReader<Ticket> ticketMultiFileItemReader () { Resource[] resources = new Resource[]{ new ClassPathResource("ticket-1.csv" ), new ClassPathResource("ticket-2.csv" )}; return new MultiResourceItemReaderBuilder<Ticket>() .name("ticketMultiFileItemReader" ) .delegate(commonTicketFileItemReader()) .resources(resources) .build(); } @Bean ("commonTicketFileItemReader" )public FlatFileItemReader<Ticket> commonTicketFileItemReader () { return new FlatFileItemReaderBuilder<Ticket>() .name("commonTicketFileItemReader" ) .delimited() .names(new String[]{"departureStation" , "arrivalStation" , "price" }) .fieldSetMapper(new BeanWrapperFieldSetMapper<Ticket>() {{ setTargetType(Ticket.class ) ; }}) .build(); }
Start the application. Console output:
1 2 3 4 5 6 7 8 2022 -06 -02 14 :37 :49.693 INFO 86124 --- [restartedMain] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=testMultiFileItemReaderJob]] launched with the following parameters: [{run.id=2 }]2022 -06 -02 14 :37 :49.785 INFO 86124 --- [restartedMain] o.s.batch.core.job.SimpleStepHandler : Executing step: [testMultiFileItemReaderStep]Departure: Hefei; Arrival: Bengbu; Price: 60.00 Departure: Nanjing; Arrival: Bengbu; Price: 70.00 Departure: Shanghai; Arrival: Bengbu; Price: 220.00 Departure: Shanghai; Arrival: Hangzhou; Price: 75.20 Departure: Shanghai; Arrival: Kunshan; Price: 19.00 2022 -06 -02 14 :37 :49.944 INFO 86124 --- [restartedMain] o.s.batch.core.step.AbstractStep : Step: [testMultiFileItemReaderStep] executed in 157 ms
Sample code: spring-batch-demo