Learn how to build a Spring Batch job that reads Excel files with Apache POI, processes data in chunks, and writes results to a database.
Updated August 2026 — full tutorial restored for this URL.
TL;DR
-
Spring Batch and Apache POI can be combined to read structured Excel data in batch jobs.
-
Build a custom ItemReader to read product rows from an Excel worksheet.
-
Use an ItemProcessor to validate, filter, and transform records before writing them.
-
Chunk processing lets Spring Batch process and write records in manageable groups.
-
For large Excel files, use streaming approaches instead of loading the entire workbook into memory.
In this tutorial we build a Spring Batch job that reads Excel (Apache POI) and writes processed rows in chunks — the same pattern you use for imports in production.
- Dependencies
- Domain model and Excel layout
- ItemReader with POI
- Processor and ItemWriter
- Job and Step configuration
- Run and verify
1. Dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
Enable batch schema: spring.batch.jdbc.initialize-schema=always for local demos.
2. Domain model and Excel layout
Assume sheet products with columns: sku, name, price.
public record ProductRow(String sku, String name, BigDecimal price) {}
3. ItemReader with POI
public class ExcelProductReader implements ItemReader<ProductRow> {
private Iterator<Row> rows;
public ExcelProductReader(Resource resource) throws Exception {
Workbook wb = WorkbookFactory.create(resource.getInputStream());
Sheet sheet = wb.getSheet("products");
rows = sheet.iterator();
if (rows.hasNext()) rows.next(); // skip header
}
@Override
public ProductRow read() {
if (rows == null || !rows.hasNext()) return null;
Row r = rows.next();
return new ProductRow(
r.getCell(0).getStringCellValue(),
r.getCell(1).getStringCellValue(),
BigDecimal.valueOf(r.getCell(2).getNumericCellValue()));
}
}
For large files, prefer streaming (SXSSF / event API) instead of loading the whole workbook.
4. Processor and ItemWriter
@Bean
ItemProcessor<ProductRow, ProductRow> processor() {
return item -> {
if (item.price().signum() < 0) return null; // filter
return new ProductRow(item.sku().trim().toUpperCase(), item.name().trim(), item.price());
};
}
@Bean
JdbcBatchItemWriter<ProductRow> writer(DataSource ds) {
return new JdbcBatchItemWriterBuilder<ProductRow>()
.dataSource(ds)
.sql("INSERT INTO product(sku,name,price) VALUES (:sku,:name,:price)")
.beanMapped()
.build();
}
5. Job and Step configuration
@Bean
Job importJob(JobRepository jobs, Step step1) {
return new JobBuilder("importExcelJob", jobs).start(step1).build();
}
@Bean
Step step1(JobRepository jobs, PlatformTransactionManager tx,
ExcelProductReader reader,
ItemProcessor<ProductRow, ProductRow> processor,
JdbcBatchItemWriter<ProductRow> writer) {
return new StepBuilder("excelToDb", jobs)
.<ProductRow, ProductRow>chunk(100, tx)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
6. Run and verify
Trigger with JobLauncher or spring.batch.job.enabled=true. Check BATCH_JOB_EXECUTION and your product table. Next step: write an ItemWriter that exports Excel for reports (mirror the reader with XSSFWorkbook).
Final Thoughts
Spring Batch provides a reliable structure for turning Excel imports into repeatable processing jobs. By separating reading, processing, and writing into dedicated components, you can keep the workflow easier to test, maintain, and extend.
The example here uses Apache POI to read Excel rows, applies simple validation and transformation, and writes the results to a database in chunks. For production workloads, file size and memory usage should guide your choice of Excel processing approach, especially when dealing with large workbooks.
Once the basic import works, the same Spring Batch pattern can be extended to more complex validation, error handling, database operations, reporting, and Excel exports.