升级Liquibase至4.21.1后出现ChangeSet ID重复问题求助
Liquibase升级后重复变更集验证失败的解决方案
问题背景
把使用Liquibase 4.9.1的Spring Boot 2.7.x项目升级到包含Liquibase 4.21.1的最新Spring Boot版本后,启动系统抛出以下异常:
Caused by: liquibase.exception.ValidationFailedException: Validation was unsuccessful:
840 change sets had duplicate identifiers
问题源于多模块项目中Liquibase XML文件互相引用的结构,示例如下:
<!-- db/migration/changes/second.changelog.xml --> ... <include file="/db/migration/changes/first.changelog.xml" /> ...
升级前运行正常,升级后出现上述问题,已尝试以下方案:
- 跳过重复变更集验证:通过断点修改
liquibase.changelog.visitor.ValidatingVisitor::validationPassed()返回true,结果脚本执行两次,出现表已存在的SQL错误 - 移除引用:可解决重复问题,但Liquibase无法按正确顺序执行文件,集成测试因脚本依赖缺失失败
- 移除引用并在
db.changelog-master.yaml中显式列出变更日志:方案可行,但无法通过Spring Profiles激活/停用单个变更日志,不适用于集成测试场景
原因分析:Liquibase的重复项处理逻辑变更
Liquibase从4.10版本开始,对变更集的重复验证逻辑做了严格强化:
- 旧版本(4.9及以下)允许变更集被多次
include,只要变更集的执行状态已记录在数据库的DATABASECHANGELOG表中,就会跳过重复执行,且不会触发验证报错 - 新版本(4.10+)默认会扫描整个变更日志树,检测到同一个变更集(通过
id+author+filepath唯一标识)被多次引用时,直接抛出ValidationFailedException,阻止系统启动
解决方案
方案1:使用includeAll配合上下文(Contexts)替代互相引用
既能保留多模块的依赖逻辑,又能避免重复引用问题:
- 为每个模块的变更日志添加专属的Spring Profile/Context标识,比如在变更集中设置:
或者在changelog根节点统一配置:<changeSet id="init-user-table" author="dev" context="user-module"> <!-- 变更内容 --> </changeSet><databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.21.xsd" context="order-module"> <!-- 模块下的变更集 --> </databaseChangeLog> - 在
db.changelog-master.yaml中使用includeAll按目录加载变更日志,同时通过Spring Profiles控制激活的Context:databaseChangeLog: - includeAll: path: classpath:/db/migration/changes/ contextFilter: ${spring.profiles.active} - 启动时通过
spring.profiles.active指定要激活的模块,Liquibase会自动加载对应Context的变更日志,不会出现重复引用的问题
方案2:使用preConditions控制引用的执行时机
在被引用的changelog中添加前置条件,确保只有当目标变更集未执行过时才加载:
<!-- first.changelog.xml --> <preConditions onFail="MARK_RAN"> <not> <changeSetExecuted id="init-base-table" author="your-name" /> </not> </preConditions>
这样即使被多次include,只要目标变更集已执行过,就会跳过加载,避免重复验证报错
方案3:启用宽松验证模式(不推荐)
通过配置参数关闭重复变更集的验证,在application.yml中添加:
liquibase: parameters: liquibase.validate.duplicateChangeSets: false
注意:此方案会退回到旧版本的行为,可能导致变更集重复执行的风险,仅适合临时应急使用
内容的提问来源于stack exchange,提问作者RiVogel
相关产品推荐
相关产品推荐

