
本文详解如何在 spring batch 中配置 itemwriter,通过设置 chunk size 为 1 实现“每处理一项即提交一次”的原子性写入,支持混合执行数据库更新与插入,并明确 jdbcbatchitemwriter 与 repositoryitemwriter 的选型依据及自定义业务逻辑集成方式。
本文详解如何在 spring batch 中配置 itemwriter,通过设置 chunk size 为 1 实现“每处理一项即提交一次”的原子性写入,支持混合执行数据库更新与插入,并明确 jdbcbatchitemwriter 与 repositoryitemwriter 的选型依据及自定义业务逻辑集成方式。
在 Spring Batch 中,ItemWriter 的设计天然面向批量写入——其 write(List extends T> items) 方法接收一个列表,这是为了配合 chunk-oriented processing(基于块的处理)提升 I/O 效率。但当业务要求每个条目独立提交、失败隔离、不可重复处理(例如:某条记录成功落库后,即使后续条目失败也不应回滚),最直接且推荐的做法是将 chunk size 设为 1:
@Bean
public Step processStep() {
return stepBuilderFactory.get("processStep")
.<inputitem outputitem>chunk(1) // 关键:每项成一块
.reader(itemReader())
.processor(itemProcessor()) // 可返回 null 跳过该条目
.writer(itemWriter())
.build();
}</inputitem>
此时,尽管 ItemWriter.write() 签名仍接收 List
✅ 推荐写法:使用 RepositoryItemWriter(JPA 场景)
适用于已建模实体、需复用 JPA 事务语义及关联逻辑的场景:
@Bean
public ItemWriter<outputitem> itemWriter() {
RepositoryItemWriter<outputitem> writer = new RepositoryItemWriter();
writer.setRepository(outputItemRepository); // Spring Data JPA Repository
writer.setMethodName("save"); // 或自定义方法如 "upsert"
return writer;
}</outputitem></outputitem>
⚠️ 注意:RepositoryItemWriter 默认调用 save(),对已有 ID 的实体自动触发 UPDATE,无 ID 则 INSERT。若需更精确控制(如先查后更、多表联写),建议封装为自定义 Service 方法并在 writer 中调用。
✅ 替代方案:使用 JdbcBatchItemWriter(纯 JDBC / 高性能场景)
虽名为“Batch”,但 chunk size=1 时仍完全可用,适合需原生 SQL 控制、避免 JPA 开销或跨多表复杂 DML 的情况:
@Bean
public ItemWriter<outputitem> itemWriter() {
JdbcBatchItemWriter<outputitem> writer = new JdbcBatchItemWriter();
writer.setDataSource(dataSource);
writer.setSql("MERGE INTO target_table USING (VALUES (?, ?, ?)) AS vals(id, name, status) " +
"ON target_table.id = vals.id " +
"WHEN MATCHED THEN UPDATE SET name = vals.name, status = vals.status " +
"WHEN NOT MATCHED THEN INSERT (id, name, status) VALUES vals.id, vals.name, vals.status");
writer.setItemPreparedStatementSetter((item, ps) -> {
ps.setLong(1, item.getId());
ps.setString(2, item.getName());
ps.setString(3, item.getStatus());
});
return writer;
}</outputitem></outputitem>
✅ 进阶:自定义 ItemWriter 调用业务服务(最灵活)
当更新/插入涉及多个表、条件分支、外部 API 调用或复杂事务边界时,强烈建议封装为独立 Service 并在 Writer 中调用:
@Component
public class CustomItemWriter implements ItemWriter<outputitem> {
private final ItemProcessingService processingService; // 注入你的业务服务
public CustomItemWriter(ItemProcessingService processingService) {
this.processingService = processingService;
}
@Override
public void write(List extends OutputItem> items) throws Exception {
if (items.isEmpty()) return;
// chunk size = 1 → 安全取首项
OutputItem item = items.get(0);
processingService.upsertAndLog(item); // 内部含 update + insert + audit log 等
// 事务由 Spring Batch 的 PlatformTransactionManager 自动管理,commit 在此处完成
}
}</outputitem>
关键注意事项总结:
- ✅ Processor 返回 null → 该项被过滤,Writer.write() 不会被调用,天然支持条件跳过;
- ✅ 所有 writer 均运行在 Spring Batch 管理的事务中,chunk size = 1 即意味着每个 write() 调用后立即 commit;
- ❌ 避免在 writer 中手动开启新事务(如 @Transactional(propagation = REQUIRES_NEW)),会破坏 Batch 的一致性保障;
- ✅ 若需幂等重跑,务必在 Processor 或 Writer 中校验数据状态(如检查目标记录是否已存在),而非依赖 chunk 提交机制;
- ? 日志建议:在 writer 或 service 中记录 item.getId() 和操作结果,便于故障定位与补偿。
综上,无需回避 List










