Spring Batch中的ItemReader:数据搬运工的"吸星大法" 📚
副标题:从文件到数据库,如何优雅地"吸"数据?
一、ItemReader是谁?------数据搬运界的"吸尘器"
ItemReader 是Spring Batch中专门负责数据读取 的核心接口,它的使命只有一个:从各种数据源(文件、数据库、消息队列等)中"吸"出数据,交给后续的ItemProcessor
和ItemWriter
处理。它的read()
方法就像吸尘器的开关,每次调用"吸"一条数据,直到返回null
(表示数据吸完了)。
接口定义与核心逻辑:
java
public interface ItemReader<T> {
T read() throws Exception; // 吸一条,吸不动了返回null
}
适用场景:
- 从CSV文件读取百万条订单数据
- 从数据库分页查询用户信息
- 从消息队列消费待处理消息
二、ItemReader的"十八般武艺"------主要实现类型
Spring Batch为不同数据源提供了丰富的内置实现,开发者无需重复造轮子。
1. 文件读取类
-
FlatFileItemReader :专治CSV、TXT等平面文件,支持分隔符、固定长度等格式。
java// 示例:读取CSV文件并映射为Person对象 @Bean public FlatFileItemReader<Person> reader() { return new FlatFileItemReaderBuilder<Person>() .resource(new ClassPathResource("data.csv")) .delimited().names("name", "age") .targetType(Person.class) .build(); }
-
StaxEventItemReader:解析XML文件,支持XSD验证。
2. 数据库读取类
- JdbcCursorItemReader:基于游标逐行读取,适合大数据量(但需注意内存占用)。
- JdbcPagingItemReader:分页查询,避免一次性加载所有数据(内存友好)。
- JpaPagingItemReader:基于JPA的分页读取,集成ORM框架。
3. 消息队列与NoSQL
- JmsItemReader:从JMS队列消费消息。
- MongoItemReader:读取MongoDB数据,支持分页和排序。
4. 自定义场景
- ListItemReader:直接读取内存中的List(调试神器)。
- 自定义ItemReader :实现
ItemReader
接口,适配特殊数据源(如API接口)。
三、ItemReader的"记忆大师"------状态管理与重启机制
Spring Batch通过ItemStream接口实现状态管理,确保任务崩溃后能"断点续吸"。
ItemStream三剑客:
- open():初始化读取位置(如从数据库游标或文件行号开始)。
- update() :将当前读取状态(如行号)保存到
ExecutionContext
。 - close():释放资源(如关闭文件句柄或数据库连接)。
示例:
- 若任务在读取到第1000行时崩溃,重启后
open()
会根据保存的状态跳过前1000行。
四、ItemReader的"容错秘籍"------异常处理
读取数据时难免遇到脏数据或网络抖动,Spring Batch提供灵活的错误处理策略:
- 跳过脏数据 :配置
SkipPolicy
,如跳过格式错误的CSV行。 - 重试机制:对可重试异常(如网络超时)设置重试次数。
- 限流控制 :设置
skipLimit(10)
,最多跳过10条错误数据。
代码示例:
java
public Step step() {
return stepBuilderFactory.get("step")
.<Person, Person>chunk(100)
.reader(reader())
.faultTolerant()
.skipLimit(10)
.skip(FlatFileParseException.class) // 跳过解析异常
.retryLimit(3)
.retry(DeadlockLoserDataAccessException.class) // 重试数据库死锁
.build();
}
五、实战对比:游标 vs. 分页------数据库读取的"左右互搏"
1. JdbcCursorItemReader(游标)
- 原理:通过JDBC游标逐行读取,保持数据库连接直到读完。
- 优点:适合大数据量,内存占用低。
- 缺点:长事务可能锁表,连接占用时间长。
2. JdbcPagingItemReader(分页)
- 原理:分页查询(如每页1000条),多次短事务完成读取。
- 优点:避免长事务,内存更可控。
- 缺点:分页SQL性能依赖数据库优化(如索引)。
选择建议:
- 数据量极大且无需实时性 → 游标
- 高并发或需避免锁表 → 分页
六、自定义ItemReader开发------手把手打造"专属吸管"
若内置实现不满足需求,可自定义ItemReader:
示例:从API分页读取数据
java
public class ApiItemReader implements ItemReader<Data>, ItemStream {
private int page = 0;
private Iterator<Data> currentPage;
@Override
public Data read() {
if (currentPage == null || !currentPage.hasNext()) {
currentPage = fetchNextPage(page++).iterator(); // 调用API获取下一页
}
return currentPage.hasNext() ? currentPage.next() : null;
}
@Override
public void open(ExecutionContext context) {
page = context.getInt("page", 0); // 从上下文恢复页码
}
@Override
public void update(ExecutionContext context) {
context.putInt("page", page); // 保存当前页码
}
}
七、最佳实践------老司机的"吸数据"心得
-
合理配置Chunk大小:
- 太大 → 内存溢出(如10000条一次)
- 太小 → 事务开销高(如10条一次)
- 建议:根据数据量和内存测试,一般100~1000条。
-
监控与日志:
- 通过
StepExecutionListener
记录读取速度、错误率。 - 集成Spring Boot Actuator查看批处理指标。
- 通过
-
资源释放:
- 确保
ItemStream.close()
正确关闭资源(如文件句柄、数据库连接)。
- 确保
总结
ItemReader是Spring Batch数据处理的"第一公里",无论是文件解析、数据库查询还是自定义数据源,它都能以高效、可靠、可重启的方式完成任务。掌握其核心机制与最佳实践,你的批处理任务将如虎添翼!