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

Spring Boot中如何确保Liquibase YAML脚本优先于SQL脚本执行?

解决方案

方法1:监听Liquibase执行完成事件

利用Liquibase发布的LiquibaseEndEvent事件,在Schema变更完全完成后手动执行数据脚本,这是最可靠的顺序控制方式。

步骤:

  1. 禁用默认数据源初始化:在application.properties中关闭Spring Boot自动执行spring.datasource.data指定的脚本,避免冲突:
spring.datasource.initialization-mode=never
  1. 编写事件监听器:创建Spring组件,监听Liquibase执行结束事件,通过Spring内置的ScriptUtils工具执行数据脚本:
@Component
public class PostLiquibaseDataLoader {

    private final JdbcTemplate jdbcTemplate;
    private final ResourceLoader resourceLoader;

    public PostLiquibaseDataLoader(JdbcTemplate jdbcTemplate, ResourceLoader resourceLoader) {
        this.jdbcTemplate = jdbcTemplate;
        this.resourceLoader = resourceLoader;
    }

    @EventListener
    public void runDataScriptsAfterLiquibase(LiquibaseEndEvent event) {
        // 按执行顺序定义数据脚本路径
        String[] dataScripts = {
            "classpath:data/user_init.sql",
            "classpath:data/product_init.sql"
        };

        for (String scriptPath : dataScripts) {
            try {
                Resource scriptResource = resourceLoader.getResource(scriptPath);
                // 自动处理SQL分隔符、连接管理和异常
                ScriptUtils.executeSqlScript(
                    jdbcTemplate.getDataSource().getConnection(),
                    scriptResource
                );
            } catch (Exception e) {
                throw new RuntimeException("Failed to execute data script: " + scriptPath, e);
            }
        }
    }
}

方法2:通过Bean依赖控制执行顺序

使用@DependsOn注解强制负责数据脚本执行的DataSourceInitializer Bean,在Liquibase Bean初始化完成后再启动。

步骤:

  1. 禁用默认初始化:
spring.datasource.initialization-mode=never
  1. 自定义DataSourceInitializer Bean:
@Configuration
public class DataLoaderConfig {

    @Bean
    @DependsOn("liquibase") // 明确依赖Liquibase Bean,确保Schema变更先完成
    public DataSourceInitializer dataSourceInitializer(DataSource dataSource) {
        DataSourceInitializer initializer = new DataSourceInitializer();
        initializer.setDataSource(dataSource);

        ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
        populator.addScripts(
            new ClassPathResource("data/user_init.sql"),
            new ClassPathResource("data/product_init.sql")
        );
        // 脚本执行失败时终止(可根据需求改为true)
        populator.setContinueOnError(false);
        initializer.setDatabasePopulator(populator);

        // 设置初始化模式:ALWAYS(每次启动执行)、EMBEDDED(仅嵌入式数据库)、NEVER
        initializer.setMode(DataSourceInitializationMode.ALWAYS);

        return initializer;
    }
}
最佳实践
  • 环境区分执行:通过Spring Profile控制数据脚本仅在开发/测试环境自动执行,生产环境手动触发或通过状态表判断:
    @Profile({"dev", "test"})
    @Component
    public class PostLiquibaseDataLoader { ... }
    
  • 防止重复执行:生产环境中创建data_load_status表,记录数据加载状态,执行脚本前先检查状态,避免重复插入数据:
    @EventListener
    public void runDataScriptsAfterLiquibase(LiquibaseEndEvent event) {
        Boolean isLoaded = jdbcTemplate.queryForObject(
            "SELECT EXISTS(SELECT 1 FROM data_load_status WHERE status = 'COMPLETED')",
            Boolean.class
        );
        if (Boolean.TRUE.equals(isLoaded)) {
            return;
        }
    
        // 执行脚本...
    
        jdbcTemplate.execute("INSERT INTO data_load_status (status, load_time) VALUES ('COMPLETED', NOW())");
    }
    
  • 脚本维护原则:保持数据脚本与最终Schema结构一致,避免依赖中间Schema版本,降低长期维护成本。

内容的提问来源于stack exchange,提问作者EmreAkkoc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 18:22:21