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 属性的决策路径,帮助开发者在实际项目中快速确定配置:
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 属性,你可以构建出既灵活又安全的数据库迁移流程,满足从开发到生产的全生命周期管理需求。