You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 13:07:47