SQL Liquibase 的 precondition 失败的回滚策略与日志记录

1次阅读

precondition失败时liquibase默认跳过changeset并标记为skipped,不执行也不回滚;需配置onfail=”halt”或合并操作至单个changeset才能实现中止与回滚。

SQL Liquibase 的 precondition 失败的回滚策略与日志记录

Liquibase 的 precondition 失败时默认不执行变更(changeSet),也不会触发回滚——因为它根本没进入执行阶段。理解这一点是处理失败前提条件的关键。

precondition 失败的本质:跳过而非中断

Liquibase 在执行每个 changeSet 前,会依次校验其 preConditions。一旦任一条件不满足(如表不存在、列已存在、sql 查询返回空/非零等),Liquibase 就直接跳过该 changeSet,并记录为 “SKIPPED”,不会抛出异常(除非配置了 onFail="HALT")。

  • 默认行为是继续执行后续 changeSet,整个 changelog 可能部分成功
  • 数据库状态保持不变——没有 INSERT/UPDATE/ALTER 发生,自然无需回滚
  • 失败的 precondition 不影响 DATABASECHANGELOG 表的写入:该 changeSet 的记录不会插入

如何让 precondition 失败触发中止与显式回滚

若需“失败即终止+回滚之前已执行的 changeSet”,必须主动设计控制流,Liquibase 本身不提供跨 changeSet 的自动回滚能力:

  • 在 changelog 开头使用 <pre class="brush:php;toolbar:false;" conditions>&lt;onfail&gt;HALT&lt;/onfail&gt;</pre>,使首次失败立即停止执行
  • 将逻辑上强依赖的多个操作合并到单个 changeSet中(用 <sql></sql> 或自定义 Change),并在其中手动编写带事务边界的 SQL(如 postgresqlBEGIN / EXCEPTION 块)
  • 借助外部脚本(Shell/Python)调用 Liquibase,捕获命令退出码(liquibase updateHALT 会返回非 0 状态),再调用回滚专用 changelog(如 rollbackCount 1

日志记录的关键位置与排查建议

Liquibase 的 precondition 日志分散在不同层级,需结合多处定位问题:

  • 控制台输出:启用 --logLevel=debug,可看到类似 Precondition failed: table 'users' does not exist 的明细
  • liquibase.log 文件(若配置了 fileHandler):包含完整 precondition 校验上下文和(尤其自定义 precondition)
  • DATABASECHANGELOG 表:检查 EXECTYPE 字段,SKIPPED 表示 precondition 失败,EXECUTED 表示成功运行
  • sqlCheck 类型 precondition,建议在 SQL 中添加注释或用 select 'DEBUG: condition X' AS reason 辅助判断实际返回值

避免 precondition 不可靠的设计习惯

很多 precondition 失败源于环境假设偏差,可通过以下方式增强健壮性:

  • 慎用 dbms="h2" 等硬编码类型判断,改用 <not><dbms type="oracle"></dbms></not> 组合
  • 检查表/列存在时,优先用 <tableexists></tableexists><columnexists></columnexists>,而非手写 SQL(它们适配多数据库元数据查询)
  • 对版本类判断(如 <runningas></runningas> 或自定义 SQL 返回值),在测试环境预跑并比对 DATABASECHANGELOG 记录的实际执行顺序
  • 在 CI/CD 流程中,对关键 changelog 加入 liquibase status + liquibase futureRollbackSQL 预检步骤
text=ZqhQzanResources