4. ChangeSet 定义与属性:id、author、命名规范、runAlways、runOnChange、context、labels

ChangeSet 是 Liquibase 迁移的最小原子单元,每个 ChangeSet 对应一次数据库变更。正确理解 ChangeSet 的属性与命名规范,是构建可追溯、可协作的数据库版本控制体系的基础。本章将系统讲解 ChangeSet 的核心属性及其最佳实践。

4.1 ChangeSet 基本结构

在 SQL 格式化文件中,ChangeSet 通过注释标记定义,基本语法如下:

-- liquibase formatted sql

-- changeset author:id
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(100)
);
-- rollback DROP TABLE users;

每个 ChangeSet 必须包含 author 和 id 两个必填属性,它们共同构成 ChangeSet 的唯一标识。Liquibase 通过该标识追踪变更是否已执行。

4.2 核心属性详解

属性 必填 说明 示例
id 是 ChangeSet 的唯一标识符,建议使用数字序列或 JIRA 工单号 id: 001 或 id: JIRA-1234
author 是 变更作者,通常使用用户名或邮箱前缀 author: zhang.san
runAlways 否 每次执行迁移时都重新运行该 ChangeSet,即使已执行过 runAlways: true
runOnChange 否 仅当 ChangeSet 内容发生变化时才重新运行 runOnChange: true
context 否 按运行环境过滤,支持多值逗号分隔 context: dev,test
labels 否 按标签过滤,支持多值逗号分隔 labels: schema,init

4.3 id 与 author 的命名规范

良好的命名规范能显著提升团队协作效率。以下是推荐的命名策略:

  • id 使用数字序列:如 001、002,确保变更顺序明确。避免使用时间戳,因为多人并行开发时容易冲突。
  • id 使用 JIRA 工单号:如 JIRA-1234,便于追溯需求来源。适合工单驱动开发流程。
  • author 使用统一标识:建议使用公司邮箱前缀或 LDAP 用户名,如 zhang.san,避免使用昵称或中文名。
  • 避免重复:同一 author 下不能出现重复 id,否则 Liquibase 会报错。
最佳实践:推荐使用 author: 工号/邮箱前缀 + id: 四位数字序列 的组合,如 -- changeset zhang.san:0001。该方案在多人协作时冲突概率最低。

4.4 runAlways 与 runOnChange 的区别

这两个属性用于控制 ChangeSet 的重复执行策略,理解其差异至关重要:

属性 触发条件 典型场景 注意事项
runAlways 每次执行 update 时都运行 初始化配置表、加载静态字典数据、创建视图 确保变更幂等,否则会重复插入数据
runOnChange 仅当 ChangeSet 的 SQL 内容或 checksum 发生变化时运行 修改存储过程、更新函数定义、调整视图逻辑 变更后需重新计算 checksum,否则不会触发
警告:使用 runAlways: true 时,必须确保 SQL 语句是幂等的。例如,使用 MERGE 或 INSERT ... ON CONFLICT DO NOTHING 代替普通 INSERT,否则每次执行都会产生重复数据。

4.5 context 与 labels 的过滤机制

context 和 labels 都用于按条件过滤 ChangeSet,但设计意图不同:

  • context:按运行环境过滤,如 dev、test、prod。适合区分不同环境的初始化数据或测试数据。
  • labels:按功能标签过滤,如 schema、data、index。适合按变更类型分组执行。

在 SQL 格式化文件中,通过注释属性指定:

-- changeset zhang.san:0003 context:dev,test labels:schema,init
CREATE TABLE temp_data (
    id INT PRIMARY KEY,
    value VARCHAR(100)
);

执行时通过命令行参数过滤:

# 仅执行 context 包含 dev 的 ChangeSet
liquibase update -Dcontext=dev

# 仅执行 labels 包含 schema 的 ChangeSet
liquibase update -Dlabels=schema

# 同时过滤 context 和 labels
liquibase update -Dcontext=dev -Dlabels=schema,init

4.6 ChangeSet 属性决策流程

下图展示了选择 ChangeSet 属性的决策路径,帮助开发者在实际项目中快速确定配置:

ChangeSet 属性决策流程 定义 ChangeSet 是否需要重复执行? 否 是 默认 ChangeSet 选择执行策略 仅执行一次 适合 DDL 变更 runAlways: true runOnChange: true 每次执行都运行 适合配置表、视图 内容变化时运行 适合存储过程、函数 最终:添加 context / labels 过滤

4.7 完整示例:综合使用多个属性

以下示例展示了如何在一个 SQL 格式化文件中综合使用多个 ChangeSet 属性:

-- liquibase formatted sql

-- changeset zhang.san:0001
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255)
);
-- rollback DROP TABLE users;

-- changeset zhang.san:0002 context:dev,test labels:seed-data
INSERT INTO users (id, name, email) VALUES
(1, 'Admin', 'admin@example.com'),
(2, 'Test', 'test@example.com');
-- rollback DELETE FROM users WHERE id IN (1, 2);

-- changeset zhang.san:0003 runAlways:true
CREATE OR REPLACE VIEW active_users AS
SELECT * FROM users WHERE status = 'ACTIVE';
-- rollback DROP VIEW active_users;

-- changeset zhang.san:0004 runOnChange:true labels:function
CREATE OR REPLACE FUNCTION get_user_count()
RETURNS INT AS $$
BEGIN
    RETURN (SELECT COUNT(*) FROM users);
END;
$$ LANGUAGE plpgsql;
-- rollback DROP FUNCTION get_user_count();
提示:每个 ChangeSet 都应包含对应的 -- rollback 语句。虽然 rollback 不是必填属性,但良好的回滚策略是生产级迁移的必备要素。在开发环境中,rollback 能帮助你快速重置数据库状态。

4.8 属性组合策略

在实际项目中,建议按以下策略组合使用 ChangeSet 属性:

变更类型 推荐属性组合 说明
DDL(建表、加索引) 默认(无特殊属性) 只执行一次,不可重复
DML(初始化数据) context: dev,test 仅开发测试环境执行,生产环境跳过
视图/物化视图 runAlways: true 每次部署都重新创建,确保定义最新
存储过程/函数 runOnChange: true 仅当代码变化时更新,避免不必要的执行
测试数据 context: dev labels: test-data 双重过滤,精确控制执行范围

通过合理配置 ChangeSet 属性,你可以构建出既灵活又安全的数据库迁移流程,满足从开发到生产的全生命周期管理需求。