
spring batch 的 tasklet 步骤默认已在 spring 管理的事务中执行,手动开启/提交/回滚事务不仅多余,还极易引发连接失效、重复提交或回滚失败等异常。
spring batch 的 tasklet 步骤默认已在 spring 管理的事务中执行,手动开启/提交/回滚事务不仅多余,还极易引发连接失效、重复提交或回滚失败等异常。
在 Spring Batch 中,TaskletStep 的执行生命周期由框架严格管控:整个 Tasklet#execute() 方法调用天然运行在一个由 Spring Batch 自动开启、传播并最终提交或回滚的事务上下文中。这一机制在官方文档的 Tasklet Step 章节中有明确说明——TaskletStep 默认使用 TransactionTemplate 包裹 Tasklet.execute(),确保其原子性。
这意味着:你无需、也不应手动调用 PlatformTransactionManager.getTransaction() 或 commit()/rollback()。一旦自行介入事务控制(如问题中示例),将导致以下典型问题:
? 为什么原代码会抛出 Connection is null 异常?
观察第一段代码:
while((testDto = dbReader01.read()) != null) {
if (status == null || status.isCompleted()) {
status = transactionManager.getTransaction(definition); // ✅ 手动开启事务
}
// ... 业务逻辑
if (someCheck) {
return RepeatStatus.FINISHED; // ⚠️ 提前返回!status 未 commit 也未 rollback
}
if (status != null && !status.isCompleted()) {
transactionManager.commit(status); // ✅ 显式提交
}
}当 someCheck 为 true 时,方法提前返回,但此时手动创建的 TransactionStatus 对象仍处于 active 状态,而 Spring Batch 框架在 TaskletStep 执行结束后,会尝试对当前线程绑定的事务(即它自己开启的那个)执行 processCommit()。然而,由于你的手动事务已占用并可能污染了 TransactionSynchronizationManager 的资源(如 DataSourceTransactionManager 内部的 ConnectionHolder),框架在后续回滚(因任务非正常结束触发清理)时发现底层 Connection 已被释放或置为 null,从而抛出:
java.sql.SQLException: Connection is null.
✅ 正确做法:完全交由 Spring Batch 管理事务
删除所有手动事务代码,让 Tasklet 保持“无状态”和“无事务侵入”:
@Component("sampleDemo")
@Scope("step")
public class SampleDemo implements Tasklet {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext context) throws Exception {
TestDto testDto;
while ((testDto = dbReader01.read()) != null) {
// ✅ 仅专注业务逻辑:读取、转换、写入、校验...
if (someCheck(testDto)) {
// 提前退出:Spring Batch 会优雅终止 step,自动回滚当前事务
return RepeatStatus.FINISHED;
}
// 其他处理...
}
return RepeatStatus.FINISHED;
}
}? 关键点:RepeatStatus.FINISHED 表示该 Tasklet 主动完成,Spring Batch 将按配置的 TransactionAttribute(默认 PROPAGATION_REQUIRED)正常提交外层事务;若抛出未捕获异常,则自动回滚。
⚠️ 注意事项与最佳实践
- 禁止混用事务管理器:不要在 Tasklet 中同时依赖框架事务 + 手动 PlatformTransactionManager。
- 资源清理交给框架:ItemReader/ItemWriter 的 open()/close() 由 Step 生命周期自动调用,勿在 finally 中重复关闭(如 dbReader01.close() 可能干扰框架管理)。
- 如需细粒度事务控制:应改用 ChunkOrientedTasklet(即基于 ItemReader → ItemProcessor → ItemWriter 的 chunk 处理模式),并通过 chunk() 配置 commit-interval 和 transaction-attributes,而非在 Tasklet 中硬编码。
- 调试建议:启用 org.springframework.transaction 日志级别为 DEBUG,可清晰看到事务的 begin/commit/rollback 追踪链路。
✅ 总结
Spring Batch 的设计哲学是“约定优于配置”。TaskletStep 的事务边界由框架定义,开发者只需聚焦业务逻辑。手动接管事务不仅违背框架意图,更会引入难以排查的资源竞争与状态不一致问题。牢记:在 Tasklet 中,写业务,不碰事务;交由 Step,静待结果。










