使用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
相关产品推荐
相关产品推荐

