4. 依赖关系声明方式:在 XML 中声明 dependsOn、在 YAML 中声明、在 JSON 中声明、在 SQL 中声明

Liquibase 支持四种变更集描述格式:XML、YAML、JSON 和 SQL。每种格式都提供了声明依赖关系(dependsOn)的语法。理解这些声明方式,是进行冒烟验证和故障排查的基础——错误的格式或遗漏的依赖声明,是导致变更集执行顺序混乱的常见根源。

核心原则:无论使用哪种格式,dependsOn 都指向一个或多个已存在的 changeset,通过其 id、author 和 filePath(可选)进行唯一标识。冒烟验证的第一步,就是确认这些引用在目标数据库中真实存在。

4.1 在 XML 中声明 dependsOn

XML 是 Liquibase 最传统、最成熟的格式。依赖关系通过 <preConditions> 内部的 <changesetExecuted> 元素,或直接在 <changeset> 上使用 dependsOn 属性来声明。推荐使用 dependsOn 属性,因为它更直观、更简洁。

<?xml version="1.0" encoding="UTF-8"?>
<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.20.xsd">

    <changeSet id="1" author="alice">
        <createTable tableName="users">
            <column name="id" type="int">
                <constraints primaryKey="true"/>
            </column>
            <column name="name" type="varchar(255)"/>
        </createTable>
    </changeSet>

    <changeSet id="2" author="bob" dependsOn="1">
        <comment>依赖 changeset id=1, author=alice</comment>
        <addColumn tableName="users">
            <column name="email" type="varchar(255)"/>
        </addColumn>
    </changeSet>

    <!-- 多依赖:使用逗号分隔 -->
    <changeSet id="3" author="carol" dependsOn="1, 2">
        <createIndex indexName="idx_users_email" tableName="users">
            <column name="email"/>
        </createIndex>
    </changeSet>

</databaseChangeLog>
XML 声明要点:
  • dependsOn 属性值是一个或多个 changeset 的 id(字符串),多个 id 用逗号分隔。
  • 默认情况下,Liquibase 会在同一个 databaseChangeLog 文件中查找同 author 的变更集。如果依赖跨文件或跨 author,需要使用 changesetExecuted 前置条件。
  • 故障排查时,检查 dependsOn 引用的 id 是否与目标变更集的 id 完全匹配(包括大小写和空格)。

4.2 在 YAML 中声明 dependsOn

YAML 格式以其简洁和可读性受到许多团队的青睐。依赖关系通过 dependsOn 键来声明,其值可以是一个字符串(单个依赖)或一个列表(多个依赖)。

databaseChangeLog:
  - changeSet:
      id: 1
      author: alice
      changes:
        - createTable:
            tableName: orders
            columns:
              - column:
                  name: id
                  type: int
                  constraints:
                    primaryKey: true
              - column:
                  name: product
                  type: varchar(255)

  - changeSet:
      id: 2
      author: bob
      dependsOn: "1"  # 单依赖,字符串形式
      changes:
        - addColumn:
            tableName: orders
            columns:
              - column:
                  name: quantity
                  type: int

  - changeSet:
      id: 3
      author: carol
      dependsOn:      # 多依赖,列表形式
        - "1"
        - "2"
      changes:
        - createIndex:
            indexName: idx_orders_product
            tableName: orders
            columns:
              - column:
                  name: product
YAML 声明要点:
  • YAML 对缩进敏感,dependsOn 必须与 id、author 在同一缩进层级。
  • 多依赖时,使用列表(以 - 开头)比逗号分隔字符串更清晰,也更容易排查遗漏。
  • 冒烟验证时,检查 YAML 解析是否报错——错误的缩进会导致 dependsOn 被忽略,从而破坏依赖链。

4.3 在 JSON 中声明 dependsOn

JSON 格式在自动化生成和工具集成场景中很常见。依赖关系通过 "dependsOn" 键来声明,其值可以是字符串或数组。

{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "1",
        "author": "alice",
        "changes": [
          {
            "createTable": {
              "tableName": "products",
              "columns": [
                { "column": { "name": "id", "type": "int", "constraints": { "primaryKey": true } } },
                { "column": { "name": "name", "type": "varchar(255)" } }
              ]
            }
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "2",
        "author": "bob",
        "dependsOn": "1",
        "changes": [
          {
            "addColumn": {
              "tableName": "products",
              "columns": [
                { "column": { "name": "price", "type": "decimal(10,2)" } }
              ]
            }
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "3",
        "author": "carol",
        "dependsOn": ["1", "2"],
        "changes": [
          {
            "createIndex": {
              "indexName": "idx_products_name",
              "tableName": "products",
              "columns": [
                { "column": { "name": "name" } }
              ]
            }
          }
        ]
      }
    }
  ]
}
JSON 声明要点:
  • JSON 要求严格的键名和字符串引号。dependsOn 必须使用双引号,且值如果是字符串也必须用双引号。
  • 多依赖时,使用数组 ["1", "2"] 形式。注意数组元素是字符串,不是对象。
  • 故障排查时,使用 JSON 验证工具检查文件格式。一个常见的错误是遗漏逗号或多余逗号,导致整个 databaseChangeLog 解析失败,依赖关系自然无法生效。

4.4 在 SQL 中声明 dependsOn

SQL 格式与其他三种格式有本质区别:它没有结构化的 dependsOn 属性。依赖关系通过 SQL 注释中的 Liquibase 指令来声明,或者通过 preconditions 注释块来实现。

-- liquibase formatted sql

-- changeset alice:1
CREATE TABLE customers (
    id INT PRIMARY KEY,
    name VARCHAR(255)
);
-- rollback DROP TABLE customers;

-- changeset bob:2 dependsOn:1
-- 依赖 changeset alice:1
ALTER TABLE customers ADD COLUMN phone VARCHAR(20);
-- rollback ALTER TABLE customers DROP COLUMN phone;

-- changeset carol:3 dependsOn:1,2
-- 多依赖:依赖 alice:1 和 bob:2
CREATE INDEX idx_customers_name ON customers(name);
-- rollback DROP INDEX idx_customers_name;
SQL 声明要点:
  • dependsOn 指令必须写在 -- changeset 注释行的末尾,格式为 dependsOn:id 或 dependsOn:id1,id2。
  • SQL 格式中,dependsOn 只能引用同一个 author 的变更集。如果需要跨 author 依赖,必须使用 -- preconditions 注释块。
  • 冒烟验证时,检查注释行是否被正确解析。注释中的拼写错误(如 dependsOn 写成 dependOn)会导致依赖被静默忽略。

4.5 四种声明方式对比

特性 XML YAML JSON SQL
声明位置 <changeSet> 属性 changeSet 键值对 "changeSet" 键值对 -- changeset 注释行
单依赖语法 dependsOn="1" dependsOn: "1" "dependsOn": "1" dependsOn:1
多依赖语法 dependsOn="1, 2" dependsOn: ["1", "2"] "dependsOn": ["1", "2"] dependsOn:1,2
跨 author 支持 需 changesetExecuted 需 changesetExecuted 需 changesetExecuted 需 -- preconditions
常见故障点 属性拼写、逗号空格 缩进错误、列表格式 引号遗漏、逗号错误 注释拼写、author 不匹配

4.6 依赖声明方式的结构对比

下面的 SVG 图展示了四种格式在声明依赖时的结构差异,帮助你在冒烟验证时快速定位声明位置。

四种格式的 dependsOn 声明结构对比 XML 格式 <changeSet id="2" author="bob" dependsOn="1"> <addColumn .../> </changeSet> 属性形式,逗号分隔多依赖 YAML 格式 - changeSet: id: 2 author: bob dependsOn: "1" changes: - addColumn: ... 键值对,列表支持多依赖 JSON 格式 { "changeSet": { "id": "2", "author": "bob", "dependsOn": "1" } } 键名和值必须双引号 SQL 格式 -- liquibase formatted sql -- changeset bob:2 -- dependsOn:1 ALTER TABLE ... -- rollback ... 注释行声明,易被忽略 冒烟验证关键检查点 1. 语法正确性:XML 属性拼写、YAML 缩进、JSON 引号、SQL 注释格式。 2. 引用存在性:dependsOn 指向的 id 必须在同一个 changelog 或已执行的历史中存在。 3. author 匹配:SQL 格式默认只匹配同 author;其他格式需注意跨 author 场景。 4. 多依赖顺序:列表中的依赖顺序不影响执行顺序,但影响故障排查时的阅读顺序。 5. 文件编码:确保 changelog 文件使用 UTF-8 编码,避免特殊字符导致解析失败。 6. 工具验证:使用 liquibase validate 命令检查整个 changelog 的语法和依赖完整性。

4.7 故障排查:依赖声明常见错误

在冒烟验证阶段,以下错误是导致依赖关系失效的高频原因。建议在验证脚本中逐一检查。

错误类型 XML 示例 YAML 示例 JSON 示例 SQL 示例
拼写错误 dependsOn="1" 写成 dependOn="1" dependsOn: 写成 dependOn: "dependsOn" 写成 "dependOn" dependsOn:1 写成 dependOn:1
id 不匹配 引用的 id 包含空格或大小写不一致 引用的 id 与目标 id 类型不一致(如 "1" vs 1) 引用的 id 与目标 id 字符串不精确匹配 引用的 id 与 changeset 注释中的 id 不一致
author 缺失 跨 author 依赖时未使用 changesetExecuted 跨 author 依赖时未使用 changesetExecuted 跨 author 依赖时未使用 changesetExecuted SQL 格式默认只匹配同 author,跨 author 需 -- preconditions
格式错误 多依赖逗号后有多余空格 列表缩进错误导致解析失败 数组元素未加引号 注释行被 SQL 解析器忽略
实战建议:在 CI/CD 流水线中,建议对每种格式的 changelog 文件执行 liquibase validate 命令。该命令会检查所有 dependsOn 引用的变更集是否在 changelog 中定义,并报告任何格式错误。这是冒烟验证中最直接、最有效的自动化手段。