CompositeItemWriter 详解
本章定位:掌握 Spring Batch 中的多目标写入策略——CompositeItemWriter 和 ClassifierCompositeItemWriter 的完整用法。
定义与作用
CompositeItemWriter 将一个 Chunk 同时写入多个 Writer,实现"一次处理、多处落盘"。ClassifierCompositeItemWriter 根据数据特征将不同记录路由到不同 Writer。
飞翔科技运营部杨英的典型需求——同一批订单既要存数据库又要导出 CSV,小崔用 CompositeItemWriter 一行配置解决:
"CompositeItemWriter 就像在你家的水龙头上装了一个三通——水一开,厨房和洗衣机同时有水。"
核心原理
Composite vs Classifier
| 模式 | 行为 | 适用场景 |
|---|---|---|
CompositeItemWriter | 每个 Chunk 写入所有 Writer | 数据库 + 文件双写、主库 + 审计库 |
ClassifierCompositeItemWriter | 每条记录路由到一个 Writer | 按类型分文件、按状态分目标 |
完整示例
场景一:数据库 + 文件双写
@Bean
public CompositeItemWriter<StudentEntity> dualWriteWriter(
JdbcBatchItemWriter<StudentEntity> dbWriter,
FlatFileItemWriter<StudentEntity> fileWriter) {
CompositeItemWriter<StudentEntity> writer = new CompositeItemWriter<>();
writer.setDelegates(List.of(dbWriter, fileWriter));
return writer;
}
场景二:按状态路由到不同文件
@Bean
public ClassifierCompositeItemWriter<Order> orderClassifierWriter() {
ClassifierCompositeItemWriter<Order> writer = new ClassifierCompositeItemWriter<>();
// 创建两个文件 Writer
FlatFileItemWriter<Order> activeWriter = new FlatFileItemWriterBuilder<Order>()
.name("activeWriter")
.resource(new FileSystemResource("orders_active.csv"))
.delimited().names("orderId", "customerName").build();
FlatFileItemWriter<Order> cancelledWriter = new FlatFileItemWriterBuilder<Order>()
.name("cancelledWriter")
.resource(new FileSystemResource("orders_cancelled.csv"))
.delimited().names("orderId", "customerName").build();
writer.setClassifier(order -> {
if ("CANCELLED".equals(order.getStatus())) {
return cancelledWriter;
}
return activeWriter;
});
return writer;
}
运行结果:
Chunk 50 items: 42 ACTIVE + 8 CANCELLED
→ orders_active.csv: 追加 42 行
→ orders_cancelled.csv: 追加 8 行
易错场景与避坑
反例一:CompositeItemWriter 中一个 Writer 失败影响全部
// ❌ DB Writer 失败 → 整个 Chunk 回滚 → File Writer 也被回滚
// CompositeItemWriter 内所有 Writer 在同一个事务中
正确做法:如果某个 Writer 的失败不应影响其他 Writer,将该 Writer 移到单独的 Step 中,或配置为 non-transactional。
反例二:Classifier 忘记处理所有情况
// ❌ 未匹配的情况没有默认 Writer → NPE
writer.setClassifier(order -> {
if ("ACTIVE".equals(order.getStatus())) return activeWriter;
// CANCELLED 等其他状态 → 返回 null → NullPointerException
});
面试高频考点
Q1:CompositeItemWriter 的事务性如何保证?
所有委托的 Writer 共享 Step 的事务。任何一个 Writer 失败,整个 Chunk 回滚。如果需要独立的事务,必须将 Writer 拆分到不同的 Step。
Q2:ClassifierCompositeItemWriter 和 CompositeItemWriter 的区别?
CompositeItemWriter 将整个 Chunk 写入每个 Writer。ClassifierCompositeItemWriter 将每条记录路由到恰好一个 Writer。前者用于多目标复制,后者用于按规则分流。