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

使用Flyway在H2中创建存储过程触发JdbcSQLSyntaxErrorException,MySQL正常

解决Flyway跨MySQL8与H2数据库的存储过程迁移兼容问题

核心问题是H2不支持MySQL的DELIMITER $$语法,且Flyway对跨数据库脚本的兼容需要针对性处理。最稳妥的方案是利用Flyway的数据库特定脚本功能,将迁移逻辑拆分为通用部分和数据库专属部分,避免强行写兼容两种数据库的复杂脚本。

具体实现步骤

1. 拆分迁移脚本

将整个流程拆分为3类脚本:

  • 通用脚本:处理跨数据库语法一致的操作(新增字段)
  • MySQL专属脚本:处理MySQL环境下的存储过程创建、数据填充、旧字段删除
  • H2专属脚本:用H2支持的语法完成数据填充和旧字段删除

2. 通用迁移脚本(V1__init_new_field.sql)

这部分脚本在所有数据库环境下执行,仅做新增字段操作:

-- 新增目标字段
ALTER TABLE your_target_table ADD COLUMN new_target_field VARCHAR(255);

3. MySQL专属脚本(V1__populate_and_clean_mysql.sql)

Flyway会自动识别带-mysql后缀的脚本,仅在MySQL环境执行:

DELIMITER $$
CREATE PROCEDURE populate_new_field()
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE old_val VARCHAR(255);
    DECLARE record_cursor CURSOR FOR SELECT old_source_field FROM your_target_table;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    OPEN record_cursor;
    data_loop: LOOP
        FETCH record_cursor INTO old_val;
        IF done THEN
            LEAVE data_loop;
        END IF;
        -- 替换为你的实际数据转换逻辑
        UPDATE your_target_table SET new_target_field = CONCAT('transformed_', old_val) WHERE old_source_field = old_val;
    END LOOP;
    CLOSE record_cursor;
END $$
DELIMITER ;

-- 执行存储过程
CALL populate_new_field();

-- 清理存储过程
DROP PROCEDURE populate_new_field;

-- 删除旧字段
ALTER TABLE your_target_table DROP COLUMN old_source_field;

4. H2专属脚本(V1__populate_and_clean_h2.sql)

Flyway会自动识别带-h2后缀的脚本,仅在H2环境执行。H2不支持MySQL风格的存储过程,根据逻辑复杂度选择两种方式:

方式一:简单逻辑直接用UPDATE(推荐)

如果数据转换逻辑可以用单条SQL完成,直接写UPDATE:

-- 替换为你的实际数据转换逻辑
UPDATE your_target_table SET new_target_field = CONCAT('transformed_', old_source_field);

-- 删除旧字段
ALTER TABLE your_target_table DROP COLUMN old_source_field;

方式二:复杂逻辑用H2的CREATE ALIAS

如果转换逻辑复杂,用H2的CREATE ALIAS实现类似存储过程的功能:

CREATE ALIAS populate_new_field AS $$
void processData(Connection conn) throws SQLException {
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT old_source_field FROM your_target_table");
    while(rs.next()) {
        String oldVal = rs.getString("old_source_field");
        PreparedStatement pstmt = conn.prepareStatement("UPDATE your_target_table SET new_target_field = ? WHERE old_source_field = ?");
        pstmt.setString(1, "transformed_" + oldVal);
        pstmt.setString(2, oldVal);
        pstmt.executeUpdate();
        pstmt.close();
    }
    rs.close();
    stmt.close();
} $$;

-- 调用自定义逻辑
CALL populate_new_field();

-- 清理别名
DROP ALIAS populate_new_field;

-- 删除旧字段
ALTER TABLE your_target_table DROP COLUMN old_source_field;

关键说明

Flyway的数据库特定脚本规则:脚本名称后缀添加-<database>(如-mysql、-h2),Flyway会根据当前连接的数据库类型自动匹配执行对应脚本。确保你的Spring Boot配置中正确指定了数据库类型,Flyway会自动识别。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 07:55:00