如何在Spring Batch+Spring Boot的BatchConfiguration中执行清库SQL脚本?
嘿,其实你完全可以实现这个需求的,只是没找对Spring Batch的扩展点而已!我给你两个最常用且可靠的方案,都是在实际项目中验证过的:
方案一:通过JobExecutionListener在Job启动前执行清空脚本
Spring Batch提供了JobExecutionListener接口,允许你在Job启动前(beforeJob)和结束后(afterJob)自定义逻辑,正好适合你的场景——在迁移Job启动前执行清空脚本。
步骤1:自定义前置清理监听器
@Component public class PreMigrationCleanupListener implements JobExecutionListener { private final JdbcTemplate targetJdbcTemplate; // 注意:这里要注入**目标数据库**的数据源,避免误删源数据! public PreMigrationCleanupListener(@Qualifier("targetDataSource") DataSource targetDataSource) { this.targetJdbcTemplate = new JdbcTemplate(targetDataSource); } @Override public void beforeJob(JobExecution jobExecution) { try { // 执行你已经测试好的删除SQL,单条或多条都可以 targetJdbcTemplate.execute("DELETE FROM target_table_1"); targetJdbcTemplate.execute("DELETE FROM target_table_2"); // 如果你的脚本是外部文件(比如classpath下的cleanup.sql),可以用ScriptUtils执行 // ScriptUtils.executeSqlScript( // targetJdbcTemplate.getDataSource().getConnection(), // new ClassPathResource("cleanup.sql") // ); System.out.println("✅ 目标库数据清空完成"); } catch (Exception e) { // 清空失败时抛出异常,终止整个迁移Job,避免脏数据 throw new JobExecutionException("❌ 清空目标库数据失败", e); } } @Override public void afterJob(JobExecution jobExecution) { // 不需要后置逻辑的话留空即可 } }
步骤2:在BatchConfiguration中注册监听器
把上面的监听器绑定到你的迁移Job上:
@Configuration public class BatchConfiguration { @Autowired private PreMigrationCleanupListener cleanupListener; @Bean public Job migrationJob(JobBuilderFactory jobBuilderFactory, Step migrationStep) { return jobBuilderFactory.get("migrationJob") .listener(cleanupListener) // 绑定前置清理监听器 .start(migrationStep) .build(); } // 这里是你原有的迁移Step配置(读取、处理、写入逻辑) @Bean public Step migrationStep(StepBuilderFactory stepBuilderFactory, ItemReader<SourceEntity> reader, ItemProcessor<SourceEntity, TargetEntity> processor, ItemWriter<TargetEntity> writer) { return stepBuilderFactory.get("migrationStep") .<SourceEntity, TargetEntity>chunk(100) .reader(reader) .processor(processor) .writer(writer) .build(); } }
方案二:用Tasklet Step作为Job的第一个执行步骤
Spring Batch的Job由多个Step组成,且Step默认按顺序执行。你可以把清空操作做成一个独立的Tasklet Step,放在迁移Step的前面,保证先清空再执行数据迁移。
步骤1:自定义清理Tasklet
@Component public class CleanupTasklet implements Tasklet { private final JdbcTemplate targetJdbcTemplate; public CleanupTasklet(@Qualifier("targetDataSource") DataSource targetDataSource) { this.targetJdbcTemplate = new JdbcTemplate(targetDataSource); } @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { // 执行清空逻辑,和监听器里的一样 targetJdbcTemplate.execute("DELETE FROM target_table_1"); targetJdbcTemplate.execute("DELETE FROM target_table_2"); // 或者执行外部脚本文件 // ScriptUtils.executeSqlScript( // targetJdbcTemplate.getDataSource().getConnection(), // new ClassPathResource("cleanup.sql") // ); System.out.println("✅ 目标库数据清空完成"); return RepeatStatus.FINISHED; // 标记该Tasklet执行完成 } }
步骤2:在BatchConfiguration中配置Step顺序
@Configuration public class BatchConfiguration { @Autowired private CleanupTasklet cleanupTasklet; @Bean public Job migrationJob(JobBuilderFactory jobBuilderFactory, Step cleanupStep, Step migrationStep) { return jobBuilderFactory.get("migrationJob") .start(cleanupStep) // 第一步:清空目标库 .next(migrationStep) // 第二步:执行数据迁移 .build(); } @Bean public Step cleanupStep(StepBuilderFactory stepBuilderFactory) { return stepBuilderFactory.get("cleanupStep") .tasklet(cleanupTasklet) .build(); } // 原有的迁移Step配置 @Bean public Step migrationStep(StepBuilderFactory stepBuilderFactory, ItemReader<SourceEntity> reader, ItemProcessor<SourceEntity, TargetEntity> processor, ItemWriter<TargetEntity> writer) { return stepBuilderFactory.get("migrationStep") .<SourceEntity, TargetEntity>chunk(100) .reader(reader) .processor(processor) .writer(writer) .build(); } }
注意事项
- 数据源一定要选对:务必注入目标数据库的数据源,绝对不能用源库的,否则会误删业务数据!
- 事务控制:如果需要保证清空操作的原子性,可以在
Tasklet或Listener的方法上添加@Transactional注解,并指定目标库的事务管理器(@Transactional("targetTransactionManager"))。 - 脚本执行方式:如果SQL脚本包含多条语句,用
ScriptUtils比逐条执行更简洁,它会自动处理语句分隔符(默认是;)。
内容的提问来源于stack exchange,提问作者IvanNickSim
相关产品推荐
相关产品推荐

