3. SQL格式化ChangeLog基础:文件结构、命名规范与文件头声明
SQL格式化ChangeLog是Liquibase中最直观、最贴近数据库开发者习惯的变更日志格式。它允许你使用纯SQL脚本描述数据库变更,同时通过文件头声明和命名规范,将版本控制与Liquibase的变更追踪机制无缝集成。本节将深入剖析其文件结构、命名规范及文件头声明的核心要素。
3.1 文件结构:从单一SQL到结构化变更集
一个标准的SQL格式化ChangeLog文件,其内部结构遵循“文件头声明 + 多个变更集”的层次模型。每个变更集对应一个原子性的数据库变更操作。
--changeset author:id 注释作为起始标记,并可选的 --rollback 注释定义回滚语句。文件末尾无需特殊结束标记。
--liquibase formatted sql -- 文件头声明:标识此文件为Liquibase SQL格式
--changeset zhangsan:create_user_table -- 变更集1:创建用户表
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
email VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
--rollback DROP TABLE IF EXISTS user; -- 变更集1的回滚语句
--changeset lisi:add_email_index -- 变更集2:添加索引
CREATE INDEX idx_user_email ON user(email);
--rollback DROP INDEX idx_user_email ON user;
--changeset wangwu:insert_default_admin context:"test,dev" -- 变更集3:带上下文标签
INSERT INTO user (username, email) VALUES ('admin', 'admin@example.com');
--rollback DELETE FROM user WHERE username = 'admin';
上述代码展示了三个典型的变更集。每个变更集由 --changeset 注释行开始,其后紧跟SQL语句,并以可选的 --rollback 注释行结束。这种结构使得每个变更集独立、可追溯、可回滚。
3.2 命名规范:构建可维护的变更日志体系
良好的命名规范是数据库迁移版本控制的基石。它不仅能帮助团队成员快速定位变更,还能避免文件名冲突和变更集ID重复。以下是一套经过实践验证的命名规范。
| 规范维度 | 推荐做法 | 示例 | 说明 |
|---|---|---|---|
| 文件名 | 使用 V{版本号}__{描述}.sql 格式 |
V1.0.0__create_user_table.sql |
版本号支持语义化版本,双下划线分隔描述,便于排序和识别 |
| 变更集ID | 使用 {功能模块}_{序号} 或 {JIRA编号} |
user_module_001 或 PROJ-1234 |
ID应简短且具有业务含义,避免使用纯数字 |
| 作者 | 使用公司邮箱前缀或统一花名 | zhangsan 或 zhang.san |
保持团队内部一致,便于追溯责任人 |
| 目录结构 | 按版本或发布周期组织目录 | changelog/v1.0.0/ 或 changelog/sprint-12/ |
避免将所有文件平铺在单一目录下 |
V2.1.0__add_order_module.sql 内部可以包含 --changeset lisi:order_table_001 和 --changeset lisi:order_index_002。
3.3 文件头声明:Liquibase的识别入口
文件头声明是SQL格式化ChangeLog文件的“身份证”。它必须位于文件的第一行,且不能包含任何前导空格或注释。Liquibase通过解析这一行来确认文件格式,并启用后续的变更集解析逻辑。
--liquibase formatted sql
这行声明告诉Liquibase:“这是一个使用SQL格式化的ChangeLog文件,请按照SQL格式化规则解析后续内容。” 缺少这一行,Liquibase将无法识别该文件,并可能抛出解析异常。
除了基础声明,文件头还可以包含全局属性定义,用于设置影响整个文件的参数。这些属性使用 --liquibase formatted sql 之后的 --property 注释行定义。
--liquibase formatted sql
--property name="schema.name" value="public" global="true"
--changeset zhangsan:create_table
CREATE TABLE ${schema.name}.my_table (
id INT PRIMARY KEY
);
上述示例中,--property 定义了一个全局属性 schema.name,其值为 public。在后续的SQL语句中,可以通过 ${schema.name} 占位符引用该属性。这为多环境部署提供了极大的灵活性。
3.4 文件结构全景图
下图展示了SQL格式化ChangeLog的完整文件结构,从文件头声明到多个变更集的层次关系。
3.5 变更集属性:精细控制迁移行为
除了基本的 author:id 标识,--changeset 注释行还支持多个可选属性,用于精细控制变更集的执行上下文、运行条件等。
| 属性 | 格式 | 作用 | 示例 |
|---|---|---|---|
| context | context:"值1,值2" |
指定变更集仅在特定上下文(如 dev、test、prod)中执行 | --changeset lisi:add_index context:"dev,test" |
| labels | labels:"标签1,标签2" |
为变更集打标签,用于运行时过滤(与context类似但语义不同) | --changeset wangwu:update_schema labels:"performance,index" |
| runAlways | runAlways:true |
即使变更集已被执行过,每次运行都会重新执行 | --changeset zhao:refresh_view runAlways:true |
| runOnChange | runOnChange:true |
仅当变更集的SQL内容发生变化时才重新执行 | --changeset qian:update_proc runOnChange:true |
| failOnError | failOnError:false |
当变更集执行失败时,是否继续执行后续变更集 | --changeset sun:optional_change failOnError:false |
runAlways 和 runOnChange 属性应谨慎使用。频繁使用 runAlways:true 可能导致生产环境中的非预期数据变更。建议仅在开发或测试环境中使用,或用于管理视图、存储过程等幂等对象。
3.6 文件头声明与变更集属性的协同关系
文件头声明定义了文件的全局上下文,而变更集属性则提供了局部控制。下图展示了它们之间的协同工作流程。
通过合理组合文件头声明、全局属性和变更集属性,你可以构建出既灵活又严谨的数据库迁移脚本,满足从本地开发到生产部署的全生命周期管理需求。