• 开发环境、测试环境、生产环境数据结构各异
  • 团队成员修改数据库后,其他人不知道最新变更
  • 出现问题需要回滚,却找不到之前的SQL脚本
  • 手动执行SQL脚本顺序错误,导致数据丢失

如果你经历过这些痛苦,那么今天介绍的 Liquibase 就是你的救星!它就像Git对于代码,让数据库变更变得可控、可追溯、可回滚。


Liquibase是什么?为什么它是数据库界的"Git"?

核心定位

Liquibase是一个开源的数据库版本控制工具,通过变更日志(Changelog)记录所有数据库变更,支持:

  • 多数据库支持:MySQL、Oracle、PostgreSQL、SQL Server等60+数据库
  • 版本控制:像Git一样管理数据库变更历史
  • 自动回滚:出问题一键回退到安全版本
  • 团队协作:多人修改不会互相干扰

与传统SQL脚本对比

对比维度传统SQL脚本Liquibase
执行顺序手动维护,易出错自动依赖,智能排序
回滚功能需额外编写rollback脚本内置回滚,一键搞定
变更追踪难以查看历史记录DATABASECHANGELOG表完整记录
多环境同步容易出现不一致同一changelog保证一致
开发效率重复工作多自动化程度高

适用场景:这些项目建议用Liquibase!

强烈推荐场景

  1. 团队协作项目:多人同时开发,数据库变更频繁
  2. 微服务架构:多个服务需要独立管理数据库 schema
  3. 持续集成/持续部署(CI/CD):自动化流水线需要可靠的数据库迁移
  4. 多环境部署:开发、测试、生产环境需要保持一致
  5. 遗留系统重构:需要逐步迁移和回滚保障

不建议使用场景

  • 小型项目(变更极少)
  • 一次性脚本
  • 对性能要求极致的场景(有少量开销)

快速上手:5分钟入门Liquibase

Step 1:添加Maven依赖

<dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
    <version>4.33.0</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
</dependency>

Step 2:Spring Boot配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_db
    username: root
    password: 123456
  liquibase:
    change-log: classpath:db/changelog/db.changelog-master.yaml
    enabled: true

Step 3:创建变更日志

主变更日志db.changelog-master.yaml):

databaseChangeLog:
  - include:
      file: db/changelog/v1.0/changelog-v1.0.yaml

具体变更集changelog-v1.0.yaml):

databaseChangeLog:
  - changeSet:
      id: 1
      author: dev_team
      changes:
        - createTable:
            tableName: user
            columns:
              - column:
                  name: id
                  type: bigint
                  autoIncrement: true
                  constraints:
                    primaryKey: true
              - column:
                  name: username
                  type: varchar(50)
                  constraints:
                    nullable: false

Step 4:启动应用

# 启动Spring Boot应用,Liquibase自动执行变更
mvn spring-boot:run

执行日志示例

2025-12-10 10:00:00.123  INFO --- [           main] liquibase.changelog                      
 : Running Changeset: classpath:/db/changelog/v1.0/changelog-v1.0.yaml::1::dev_team
2025-12-10 10:00:00.456  INFO --- [           main] liquibase.changelog                      
 : ChangeSet 1 ran successfully in 333ms
2025-12-10 10:00:00.789  INFO --- [           main] liquibase.lockservice                    
 : Successfully released change log lock

查看数据库,你会发现user表已创建,且DATABASECHANGELOG表中记录了所有变更历史。


同类工具对比:为什么选Liquibase?

Flyway vs Liquibase

特性FlywayLiquibase
脚本格式纯SQLSQL、XML、YAML、JSON
配置复杂度简单中等
回滚功能不支持(需手动)内置支持
多数据库需分别写SQL一套脚本适配多库
学习曲线平缓稍陡峭

结论

  • 小项目、纯SQL团队 → Flyway
  • 大型项目、多数据库、需要回滚 → Liquibase

其他工具

  • MyBatis Migrations:仅适用于MyBatis项目
  • Alembic:Python生态,Java项目不适用
  • 自定义脚本:维护成本高,不推荐

核心技能:如何快速编写变更集?

1. 常用变更类型

省略掉前面的:databaseChangeLog.changeSet.changes 标签

创建表

- createTable:
    tableName: order
    columns:
      - column:
          name: id
          type: bigint
          autoIncrement: true
          constraints:
            primaryKey: true
      - column:
          name: user_id
          type: bigint
          constraints:
            nullable: false
      - column:
          name: amount
          type: decimal(10,2)
          constraints:
            nullable: false

添加字段

- addColumn:
    tableName: user
    columns:
      - column:
          name: email
          type: varchar(100)

创建索引

- createIndex:
    tableName: user
    indexName: idx_user_username
    columns:
      - column:
          name: username

插入数据

- insert:
    tableName: payment_method
    columns:
      - column:
          name: id
          value: 1
      - column:
          name: code
          value: 'ALIPAY'
      - column:
          name: name
          value: '支付宝'
      - column:
          name: enabled
          valueBoolean: true
- insert:
    tableName: payment_method
    columns:
      - column:
          name: id
          value: 2
      - column:
          name: code
          value: 'WECHAT'
      - column:
          name: name
          value: '微信支付'
      - column:
          name: enabled
          valueBoolean: true

2. 回滚操作

- changeSet:
    id: 5
    author: dev_team
    changes:
      - dropTable:
          tableName: temp_data
    rollback:
      - createTable:
          tableName: temp_data
          columns:
            - column:
                name: id
                type: int
                constraints:
                  primaryKey: true

3. 上下文控制(环境区分)

- changeSet:
    id: 6
    author: dev_team
    context: dev,test  # 仅在dev和test环境执行
    changes:
      - sql: INSERT INTO config VALUES ('debug_mode','true')

4. 前置条件

- changeSet:
    id: 7
    author: dev_team
    preConditions:
      - not:
          columnExists:
            tableName: user
            columnName: age
    changes:
      - addColumn:
          tableName: user
          columns:
            - column:
                name: age
                type: int

转换工具:如何将现有数据库转为Liquibase?

方法1:使用generateChangeLog命令

步骤

  1. 创建一个空数据库
mysql -u root -p -e "CREATE DATABASE test_db_empty;"
  1. 导入现有数据库结构
mysqldump -u root -p --no-data existing_db > schema.sql
mysql -u root -p test_db_empty < schema.sql
  1. 生成变更日志
liquibase --changeLogFile=generated-changelog.xml \
          --driver=com.mysql.cj.jdbc.Driver \
          --url="jdbc:mysql://localhost:3306/test_db_empty" \
          --username=root \
          --password=123456 \
          generateChangeLog

生成的generated-changelog.xml包含完整的数据库结构,可作为初始版本使用 。

方法2:使用diffChangeLog(推荐)

比较现有数据库与期望状态,仅生成差异变更:

liquibase --changeLogFile=diff-changelog.xml \
          --driver=com.mysql.cj.jdbc.Driver \
          --url="jdbc:mysql://localhost:3308/db_v1" \
          --username=root \
          --password=123456 \
		  --reference-url="jdbc:mysql://localhost:3308/db_v2" \
		  --reference-username=root \
		  --reference-password=123456 \
		  --reference-driver=com.mysql.cj.jdbc.Driver \
          diffChangeLog

生成的 diff-changelog.xml,在部署之前,应检查由 diffChangeLog/generateChangeLog 生成的变更日志的正确性和完整性。有些数据库对象及其依赖项无法自动表示,可能需要手动更新以确保部署的准确性。
经过验证,表信息的变更(字段增删改、索引、外键、注释信息)都可以生成变更,这真的是一个极佳的数据库版本差异变更的神器啊

方法3:使用第三方工具

SQLDiff:比较两个数据库结构生成迁移SQL

DBeaver:数据库工具导出变更脚本

注意:生成的变更日志需要人工审核,确保 correctness。


企业级实战:解决常见难题

问题1:多数据库字段类型适配

场景:MySQL的TEXT类型在Oracle中应映射为CLOB,在PostgreSQL中应为TEXT

解决方案:使用Liquibase的数据库类型映射

databaseChangeLog:
  - changeSet:
      id: 1
      author: dba
      changes:
        - createTable:
            tableName: document
            columns:
              - column:
                  name: content
                  # Liquibase会根据目标数据库自动映射类型
                  type: LONGTEXT  # MySQL: TEXT, Oracle: CLOB, PostgreSQL: TEXT

高级用法:自定义类型映射

// 创建自定义类型映射器
public class CustomTypeMapping implements liquibase.database.type.TypeMapping {
    @Override
    public String getColumnType(String columnType) {
        if ("LONGTEXT".equals(columnType)) {
            String database = getDatabase().getDatabaseProductName();
            if ("Oracle".equals(database)) {
                return "CLOB";
            }
        }
        return columnType;
    }
}

问题2:处理大对象(BLOB/CLOB)

场景:需要初始化包含图片、文件等二进制数据、初始化数据比较多时。

解决方案:使用loadData标签

databaseChangeLog:
  - changeSet:
      id: 7-load-data
      author: backend_team
      # 将 contexts 属性简化为逗号分隔的字符串
      contexts: dev, test, production 
      comment: 使用loadData加载文件
      changes:
        - loadData:
            tableName: payment_method
            columns:
              - column:
                  name: id
                  type: int
              - column:
                  name: code
              - column:
                  name: name
              - column:
                  name: enabled
                  index: 4
                  type: boolean
            # 注释开头信息
            commentLineStartsWith: "//"
            encoding: UTF-8
            file: example/payment_method.csv
            # 字段分隔符
            separator: ","
            # 是否用引号引用起来
            quotchar: "\""
            # 是否使用相对路径
            relativeToChangelogFile: true
            # 插入参数优化
            usePreparedStatements: true
      rollback:
        - delete:
            tableName: payment_method

example/payment_method.csv 文件示例内容

id,code,name,enabled
// 我是文件注释
6,'Paypal6',"贝宝6", t
7,'Paypal7','贝宝7', true

执行结果

注释可以被忽略,ttrue 都可以正常转换为实际的格式插入,双引号不会插入,单引号可以插入,指定字段分隔符,如果非标准csv可以指定分隔符


注意事项

  • Liquibase 3.6.2+版本对CLOB处理有改进,如遇问题请升级版本
  • 大文件建议使用文件存储,数据库仅存URL

问题3:外键约束管理

场景:添加外键约束时需要确保父表已存在。

解决方案:使用依赖关系或分步执行

databaseChangeLog:
  # 先创建父表
  - changeSet:
      id: 1
      author: dev_team
      changes:
        - createTable:
            tableName: user
            columns:
              - column:
                  name: id
                  type: bigint
                  constraints:
                    primaryKey: true
  
  # 再创建子表(含外键)
  - changeSet:
      id: 2
      author: dev_team
      changes:
        - createTable:
            tableName: order
            columns:
              - column:
                  name: id
                  type: bigint
                  constraints:
                    primaryKey: true
              - column:
                  name: user_id
                  type: bigint
                  constraints:
                    nullable: false
                    # 外键约束
                    foreignKeyName: fk_order_user
                    references:
                      column: id
                      table: user

问题4:数据库注释处理

场景:需要为表和字段添加注释(MySQL的COMMENT)。

解决方案:使用remarks属性

databaseChangeLog:
  - changeSet:
      id: 3
      author: dev_team
      changes:
        - createTable:
            tableName: user
            remarks: "用户信息表"  # 表级注释
            columns:
              - column:
                  name: id
                  type: bigint
                  autoIncrement: true
                  constraints:
                    primaryKey: true
                  remarks: "用户主键ID"  # 列级注释
              - column:
                  name: username
                  type: varchar(50)
                  constraints:
                    nullable: false
                  remarks: "用户名,用于登录"

不同数据库的注释支持

  • MySQL:直接使用COMMENT属性
  • PostgreSQL:使用COMMENT ON TABLE/COLUMN
  • Oracle:使用COMMENT ON TABLE/COLUMN
  • SQL Server:使用sp_updateextendedproperty存储过程

Liquibase会根据目标数据库自动生成相应的注释语句,我们无需自行维护不同数据库的sql语句。

问题5:初始化数据管理

场景:系统需要基础配置数据、权限数据等。

解决方案:使用SQL文件或insert标签

方法1:使用SQL文件

databaseChangeLog:
  - changeSet:
      id: 4
      author: dev_team
      changes:
        - sqlFile:
            path: db/changelog/v1.0/init-data.sql

方法2:使用insert标签(适合少量数据)
数据较多时,建议使用xml格式

databaseChangeLog:
  - changeSet:
      id: 5
      author: dev_team
      changes:
        - insert:
            tableName: payment_method
            columns:
              - column:
                  name: id
                  value: 2
              - column:
                  name: code
                  value: 'WECHAT'
              - column:
                  name: name
                  value: '微信支付'
              - column:
                  name: enabled
                  valueBoolean: true
        - insert:
            tableName: payment_method
            columns:
              - column:
                  name: id
                  value: 3
              - column:
                  name: code
                  value: 'CARD'
              - column:
                  name: name
                  value: '银行卡'
              - column:
                  name: enabled
                  valueBoolean: true

方法3:使用loadData(适合大量数据)

databaseChangeLog:
  - changeSet:
      id: 6
      author: dev_team
      changes:
        - loadData:
            tableName: permission
            columns:
              - column:
                  name: id
              - column:
                  name: code
              - column:
                  name: name
            file: db/changelog/v1.0/permissions.csv

CSV文件格式:

id,code,name
1,user:view,查看用户
2,user:create,创建用户
3,user:edit,编辑用户

高级技巧:让Liquibase更好用

1. 标签管理(Tagging)

为重要版本打标签,便于回滚:

# 标记当前版本
liquibase tag v1.0.0

# 回滚到指定标签
liquibase rollback v1.0.0

2. 上下文环境隔离

# 仅在生产环境执行
- changeSet:
    id: 10
    author: dba
    context: production
    changes:
      - sql: "UPDATE config SET value='prod' WHERE key='env'"

# 排除某个环境
- changeSet:
    id: 11
    author: dev_team
    contexts:
      - exclude:
          - production
    changes:
      - sql: "INSERT INTO debug_logs VALUES (...)"

3. 变更集分组

使用include文件组织变更:

# 主日志
databaseChangeLog:
  - include:
      file: db/changelog/tables.yaml
  - include:
      file: db/changelog/indexes.yaml
  - include:
      file: db/changelog/data.yaml

4. Maven集成

<plugin>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-maven-plugin</artifactId>
    <version>4.27.0</version>
    <configuration>
        <changeLogFile>db/changelog/master.xml</changeLogFile>
        <driver>com.mysql.cj.jdbc.Driver</driver>
        <url>jdbc:mysql://localhost:3306/test_db</url>
        <username>root</username>
        <password>123456</password>
    </configuration>
</plugin>

# 执行更新
mvn liquibase:update

# 执行回滚
mvn liquibase:rollback

# 生成变更日志
mvn liquibase:generateChangeLog

5. CI/CD集成

Jenkins Pipeline示例

pipeline {
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean compile'
            }
        }
        stage('DB Migration') {
            steps {
                sh 'mvn liquibase:update'
            }
        }
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
        stage('Deploy') {
            steps {
                sh 'mvn package'
                // 部署到生产环境
            }
        }
    }
}

信创数据库支持

目前 Liquibase 官方暂不直接支持达梦、人大金仓等信创数据库,但社区提供了成熟的 第三方适配方案db-migration 扩展项目。通过这些方案,可以实现对这些国产数据库的版本控制,核心思路是利用适配层转接标准 JDBC 协议。

db-migration 开源项目

Flyway、Liquibase 扩展支持达梦(DM)、南大通用(GBase 8s)、OpenGauss 等国产数据库。

Github地址https://github.com/mengweijin/db-migration

优点:springboot项目 拿来即用

缺点:仅做了简单实现,适配不一定完全,采用源码覆盖方式,未使用官方推荐方式,下载jar包无法使用命令行方式进行适配

自定义扩展

Liquibase的代码提供了成熟的 SPI 扩展方式,可以自定义实现。

这里我提供一个优化后的 达梦适配版本(命令行、springboot项目):

适配包下载:https://pan.alianga.com/s/3ycz

  1. liquibase-dm-1.0.0.jar 文件放到 liquibase的 lib目录下
  2. 把达梦的 JDBC 驱动包,也放到 liquibase 的 lib目录
liquibase
lib
├── dm-jdbc8-8.1.2.192.jar
├── gsjdbc4-GaussDB-Kernel-505-5.0.0.4.2.jar
├── liquibase_autocomplete_mac.bash
├── liquibase_autocomplete.sh
├── liquibase_autocomplete.zsh
├── liquibase-dm-1.0.0.jar
├── mysql-connector-j-9.5.0.jar
├── ojdbc8-19.3.0.0.jar
└── README.txt
  1. 执行示例
liquibase update --changeLogFile=master.yaml --driver=dm.jdbc.driver.DmDriver --url="jdbc:dm://127.0.0.1:5236" --username=TEST_DB --password=TEST_DB --log-level=debug

最佳实践:避免这些坑!

推荐做法

  1. 版本号规范:采用语义化版本(v1.0.1),每次变更递增
  2. 变更集原子性:每个变更集只做一件事,便于回滚
  3. 避免DROP操作:优先使用ALTER修改,而非删除重建
  4. 开发环境启用spring.liquibase.enabled=true,快速迭代
  5. 生产环境验证:变更前在预生产环境测试
  6. 定期清理:归档旧变更日志,保持changelog清洁

避免做法

  1. 不要手动修改数据库:绕过Liquibase直接操作数据库
  2. 不要重复执行变更集:会导致报错和数据不一致
  3. 不要使用DROP CASCADE:可能导致数据丢失
  4. 不要忽略回滚脚本:变更前必须确认可回滚性
  5. 不要在生产环境调试:先用dev/test环境验证

常见错误及解决方案

错误1:ChangeSet already executed

ERROR: ChangeSet classpath:/db/changelog/v1.0/changelog-v1.0.yaml::1::dev_team has already been executed

解决:检查变更集ID和author是否唯一,或使用liquibase clearCheckSums清理(谨慎使用)

错误2:Could not find databaseChangeLog node

ERROR: Could not find databaseChangeLog node

解决:检查YAML文件格式是否正确,确保根元素为databaseChangeLog

错误3:Databasechangelog already exists

ERROR: relation databasechangelog already exists

解决:数据库已存在旧版本Liquibase表,需手动清理或升级数据库


性能优化:让迁移更快更稳

1. 批量执行

# 不好:多个变更集
- changeSet:
    id: 1
    changes:
      - addColumn: ...
- changeSet:
    id: 2
    changes:
      - addColumn: ...

# 推荐:单个变更集多列
- changeSet:
    id: 1
    changes:
      - addColumn:
          tableName: user
          columns:
            - column:
                name: email
                type: varchar(100)
            - column:
                name: phone
                type: varchar(20)

2. 索引延迟创建

# 先建表
- changeSet:
    id: 1
    author: dev_team
    changes:
      - createTable:
          tableName: order
          columns:
            - column:
                name: id
                type: bigint
                constraints:
                  primaryKey: true

# 后建索引(表数据量大时更高效)
- changeSet:
    id: 2
    author: dev_team
    changes:
      - createIndex:
          tableName: order
          indexName: idx_order_user
          columns:
            - column:
                name: user_id

3. 使用SQL文件优化复杂脚本

- changeSet:
    id: 3
    author: dev_team
    changes:
      - sqlFile:
          path: db/changelog/v1.0/complex_migration.sql
          # splitStatements: true  # 自动分割多条SQL
          # stripComments: true   # 去除注释

4. 监控和日志

# 启用详细日志
logging:
  level:
    liquibase: INFO
    liquibase.changelog: DEBUG

实际案例:电商订单系统实战

需求场景

某电商平台订单系统需要:

  1. 支持MySQL和Oracle双数据库
  2. 用户表、订单表、订单详情表
  3. 订单表需添加大字段(商品图片URL)
  4. 初始化支付方式配置数据
  5. 支持回滚到上一版本

完整变更日志

master.yaml

databaseChangeLog:
  - include:
      file: db/changelog/tables.yaml
  - include:
      file: db/changelog/data.yaml

tables.yaml

databaseChangeLog:
  - changeSet:
      id: 1
      author: backend_team
      changes:
        - createTable:
            tableName: user
            columns:
              - column:
                  name: id
                  type: bigint
                  autoIncrement: true
                  constraints:
                    primaryKey: true
              - column:
                  name: username
                  type: varchar(50)
                  constraints:
                    nullable: false
              - column:
                  name: email
                  type: varchar(100)
                  constraints:
                    nullable: false
                remarks: "用户邮箱"
              - column:
                  name: created_at
                  type: datetime
                  constraints:
                    nullable: false
                  defaultValueComputed: CURRENT_TIMESTAMP
      rollback:
        - dropTable:
            tableName: user

  - changeSet:
      id: 2
      author: backend_team
      changes:
        - createTable:
            tableName: order
            columns:
              - column:
                  name: id
                  type: bigint
                  autoIncrement: true
                  constraints:
                    primaryKey: true
              - column:
                  name: user_id
                  type: bigint
                  constraints:
                    nullable: false
                  remarks: "下单用户ID"
              - column:
                  name: order_no
                  type: varchar(50)
                  constraints:
                    nullable: false
                    unique: true # <-- 移动到 constraints 节点内部
              - column:
                  name: total_amount
                  type: decimal(10,2)
                  constraints:
                    nullable: false
              - column:
                  name: status
                  type: varchar(20)
                  constraints:
                    nullable: false
                  defaultValue: 'PENDING'
              - column:
                  name: created_at
                  type: datetime
                  constraints:
                    nullable: false
                  defaultValueComputed: CURRENT_TIMESTAMP
              - column:
                  name: updated_at
                  type: datetime
                  constraints:
                    nullable: false
                  defaultValueComputed: CURRENT_TIMESTAMP
            remarks: "订单表"
      rollback:
        - dropTable:
            tableName: order

  - changeSet:
      id: 3
      author: backend_team
      changes:
        - addColumn:
            tableName: order
            columns:
              - column:
                  name: product_image
                  type: longtext
                  constraints:
                    nullable: true
                  remarks: "商品图片URL(支持多个图片,逗号分隔)"
      rollback:
        - dropColumn:
            tableName: order
            columnName: product_image

  - changeSet:
      id: 4
      author: backend_team
      changes:
        - createIndex:
            tableName: order
            indexName: idx_order_user
            columns:
              - column:
                  name: user_id
        - createIndex:
            tableName: order
            indexName: idx_order_created
            columns:
              - column:
                  name: created_at
      rollback:
        - dropIndex:
            tableName: order
            indexName: idx_order_user
        - dropIndex:
            tableName: order
            indexName: idx_order_created
  - changeSet:
      id: add-payment_method
      author: backend_team
      changes:
        - createTable:
            tableName: payment_method
            columns:
              - column:
                  name: id
                  type: bigint
                  autoIncrement: true
                  constraints:
                    primaryKey: true
              - column:
                  name: code
                  type: varchar(50)
                  constraints:
                    nullable: false
              - column:
                  name: name
                  type: varchar(100)
                  constraints:
                    nullable: false
                remarks: "用户邮箱"
              - column:
                  name: created_at
                  type: datetime
                  constraints:
                    nullable: false
                  defaultValueComputed: CURRENT_TIMESTAMP
      rollback:
        - dropTable:
            tableName: payment_method
  - changeSet:
      id: add-payment_method-column-enabled
      author: backend_team
      changes:
        - addColumn:
            tableName: payment_method
            columns:
              - column:
                  name: enabled
                  type: int(1)
                  constraints:
                    nullable: false
                  defaultValue: 1
      rollback:
        - dropColumn:
            tableName: payment_method
            columnName: enabled

data.yaml

databaseChangeLog:
  - changeSet:
      id: 5
      author: backend_team
      # 将 contexts 属性简化为逗号分隔的字符串
      contexts: dev, test, production 
      changes:
        # 将多行数据拆分为多个单独的 insert change
        - insert:
            tableName: payment_method
            columns:
              - column:
                  name: id
                  value: 1
              - column:
                  name: code
                  value: 'ALIPAY'
              - column:
                  name: name
                  value: '支付宝'
              - column:
                  name: enabled
                  valueBoolean: true
        - insert:
            tableName: payment_method
            columns:
              - column:
                  name: id
                  value: 2
              - column:
                  name: code
                  value: 'WECHAT'
              - column:
                  name: name
                  value: '微信支付'
              - column:
                  name: enabled
                  valueBoolean: true
        - insert:
            tableName: payment_method
            columns:
              - column:
                  name: id
                  value: 3
              - column:
                  name: code
                  value: 'CARD'
              - column:
                  name: name
                  value: '银行卡'
              - column:
                  name: enabled
                  valueBoolean: true
      rollback:
        - delete:
            tableName: payment_method
            where: "id IN (1,2,3)"
  - changeSet:
      id: 6-insert-sql
      author: backend_team
      # 将 contexts 属性简化为逗号分隔的字符串
      contexts: dev, test, production 
      changes:
        # 将多行数据拆分为多个单独的 insert change
        - sql: INSERT INTO payment_method (id,code,name,enabled) VALUES (5,'Paypal','贝宝', true)
      rollback:
        - delete:
            tableName: payment_method
            where: "id IN (5)"
  - changeSet:
      id: 7-load-data
      author: backend_team
      # 将 contexts 属性简化为逗号分隔的字符串
      contexts: dev, test, production 
      comment: 使用loadData加载文件
      changes:
        - loadData:
            tableName: payment_method
            columns:
              - column:
                  name: id
                  type: int
              - column:
                  name: code
              - column:
                  name: name
              - column:
                  name: enabled
                  index: 4
                  type: boolean
            # 注释开头信息
            commentLineStartsWith: "//"
            encoding: UTF-8
            file: example/payment_method.csv
            # 字段分隔符
            separator: ","
            # 是否用引号引用起来
            quotchar: "\""
            # 是否使用相对路径
            relativeToChangelogFile: true
            # 插入参数优化
            usePreparedStatements: true
      rollback:
        - delete:
            tableName: payment_method
            where: "id IN (6,7)"

使用步骤

可先使用 update-sql 查看要执行的 sql 脚本,也可验证写的 yaml或xml等规则是否正确

liquibase update-sql --changeLogFile=master.yaml \
  --driver=com.mysql.cj.jdbc.Driver \
  --url="jdbc:mysql://localhost:3308/schedule_develop" \
  --username=root \
  --password=123456 

则会有类似如下输出,可以根据输出的错误或 sql语句调整规则文件,以确保准确性和完整性

####################################################
##   _     _             _ _                      ##
##  | |   (_)           (_) |                     ##
##  | |    _  __ _ _   _ _| |__   __ _ ___  ___   ##
##  | |   | |/ _` | | | | | '_ \ / _` / __|/ _ \  ##
##  | |___| | (_| | |_| | | |_) | (_| \__ \  __/  ##
##  \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___|  ##
##              | |                               ##
##              |_|                               ##
##                                                ## 
##  Get documentation at docs.liquibase.com       ##
##  Get certified courses at learn.liquibase.com  ## 
##                                                ##
####################################################
Starting Liquibase at 15:00:24 using Java 21.0.8 (version 5.0.1 #9400 built at 2025-10-03 17:37+0000)
Liquibase Version: 5.0.1

WARNING: 

Liquibase detected the following invalid Community LIQUIBASE_* environment variables:

- LIQUIBASE_PATH

Find the list of valid environment variables at https://docs.liquibase.com/environment-variables

--  Create Database Lock Table
CREATE TABLE schedule_develop.databasechangeloglock (ID INT NOT NULL, `LOCKED` TINYINT NOT NULL, LOCKGRANTED datetime NULL, LOCKEDBY VARCHAR(255) NULL, CONSTRAINT PK_DATABASECHANGELOGLOCK PRIMARY KEY (ID));

--  Initialize Database Lock Table
DELETE FROM schedule_develop.databasechangeloglock;

INSERT INTO schedule_develop.databasechangeloglock (ID, `LOCKED`) VALUES (1, 0);

--  Lock Database
UPDATE schedule_develop.databasechangeloglock SET `LOCKED` = 1, LOCKEDBY = 'zml (172.20.0.1)', LOCKGRANTED = NOW() WHERE ID = 1 AND `LOCKED` = 0;

--  Create Database Change Log Table
CREATE TABLE schedule_develop.databasechangelog (ID VARCHAR(255) NOT NULL, AUTHOR VARCHAR(255) NOT NULL, FILENAME VARCHAR(255) NOT NULL, DATEEXECUTED datetime NOT NULL, ORDEREXECUTED INT NOT NULL, EXECTYPE VARCHAR(10) NOT NULL, MD5SUM VARCHAR(35) NULL, `DESCRIPTION` VARCHAR(255) NULL, COMMENTS VARCHAR(255) NULL, TAG VARCHAR(255) NULL, LIQUIBASE VARCHAR(20) NULL, CONTEXTS VARCHAR(255) NULL, LABELS VARCHAR(255) NULL, DEPLOYMENT_ID VARCHAR(10) NULL);

--  *********************************************************************
--  Update Database Script
--  *********************************************************************
--  Change Log: master.yaml
--  Ran at: 2025/12/10 15:00
--  Against: root@172.20.0.1@jdbc:mysql://localhost:3308/schedule_develop
--  Liquibase version: 5.0.1
--  *********************************************************************

--  Changeset db/changelog/tables.yaml::1::backend_team
CREATE TABLE schedule_develop.user (id BIGINT AUTO_INCREMENT NOT NULL, username VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL, created_at datetime DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, CONSTRAINT PK_USER PRIMARY KEY (id));

INSERT INTO schedule_develop.databasechangelog (ID, AUTHOR, FILENAME, DATEEXECUTED, ORDEREXECUTED, MD5SUM, `DESCRIPTION`, COMMENTS, EXECTYPE, CONTEXTS, LABELS, LIQUIBASE, DEPLOYMENT_ID) VALUES ('1', 'backend_team', 'db/changelog/tables.yaml', NOW(), 1, '9:473199fafaa63fd7cb06368dcfb88273', 'createTable tableName=user', '', 'EXECUTED', NULL, NULL, '5.0.1', '5350023913');

--  Changeset db/changelog/tables.yaml::2::backend_team
CREATE TABLE schedule_develop.`order` (id BIGINT AUTO_INCREMENT NOT NULL, user_id BIGINT NOT NULL COMMENT '下单用户ID', order_no VARCHAR(50) NOT NULL, total_amount DECIMAL(10, 2) NOT NULL, status VARCHAR(20) DEFAULT 'PENDING' NOT NULL, created_at datetime DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, updated_at datetime DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, CONSTRAINT PK_ORDER PRIMARY KEY (id), UNIQUE (order_no)) COMMENT='订单表';

INSERT INTO schedule_develop.databasechangelog (ID, AUTHOR, FILENAME, DATEEXECUTED, ORDEREXECUTED, MD5SUM, `DESCRIPTION`, COMMENTS, EXECTYPE, CONTEXTS, LABELS, LIQUIBASE, DEPLOYMENT_ID) VALUES ('2', 'backend_team', 'db/changelog/tables.yaml', NOW(), 2, '9:17792f8514bf1c660310075d9899ca86', 'createTable tableName=order', '', 'EXECUTED', NULL, NULL, '5.0.1', '5350023913');

--  Changeset db/changelog/tables.yaml::3::backend_team
ALTER TABLE schedule_develop.`order` ADD product_image LONGTEXT NULL COMMENT '商品图片URL(支持多个图片,逗号分隔)';

INSERT INTO schedule_develop.databasechangelog (ID, AUTHOR, FILENAME, DATEEXECUTED, ORDEREXECUTED, MD5SUM, `DESCRIPTION`, COMMENTS, EXECTYPE, CONTEXTS, LABELS, LIQUIBASE, DEPLOYMENT_ID) VALUES ('3', 'backend_team', 'db/changelog/tables.yaml', NOW(), 3, '9:f6588d2e7fc6e251dcd9508403663d77', 'addColumn tableName=order', '', 'EXECUTED', NULL, NULL, '5.0.1', '5350023913');

--  Changeset db/changelog/tables.yaml::4::backend_team
CREATE INDEX idx_order_user ON schedule_develop.`order`(user_id);

CREATE INDEX idx_order_created ON schedule_develop.`order`(created_at);

INSERT INTO schedule_develop.databasechangelog (ID, AUTHOR, FILENAME, DATEEXECUTED, ORDEREXECUTED, MD5SUM, `DESCRIPTION`, COMMENTS, EXECTYPE, CONTEXTS, LABELS, LIQUIBASE, DEPLOYMENT_ID) VALUES ('4', 'backend_team', 'db/changelog/tables.yaml', NOW(), 4, '9:007ae082c863fc53491754bd0fd72084', 'createIndex indexName=idx_order_user, tableName=order; createIndex indexName=idx_order_created, tableName=order', '', 'EXECUTED', NULL, NULL, '5.0.1', '5350023913');

--  Changeset db/changelog/data.yaml::5::backend_team
INSERT INTO schedule_develop.payment_method (id, code, name, enabled) VALUES ('1', 'ALIPAY', '支付宝', 1);

INSERT INTO schedule_develop.payment_method (id, code, name, enabled) VALUES ('2', 'WECHAT', '微信支付', 1);

INSERT INTO schedule_develop.payment_method (id, code, name, enabled) VALUES ('3', 'CARD', '银行卡', 1);

INSERT INTO schedule_develop.databasechangelog (ID, AUTHOR, FILENAME, DATEEXECUTED, ORDEREXECUTED, MD5SUM, `DESCRIPTION`, COMMENTS, EXECTYPE, CONTEXTS, LABELS, LIQUIBASE, DEPLOYMENT_ID) VALUES ('5', 'backend_team', 'db/changelog/data.yaml', NOW(), 5, '9:e871bc39d1a40a3a5af7f7868edddde7', 'insert tableName=payment_method; insert tableName=payment_method; insert tableName=payment_method', '', 'EXECUTED', NULL, NULL, '5.0.1', '5350023913');

--  Release Database Lock
UPDATE schedule_develop.databasechangeloglock SET `LOCKED` = 0, LOCKEDBY = NULL, LOCKGRANTED = NULL WHERE ID = 1;

Liquibase command 'update-sql' was executed successfully.

确保无误后,则可以通过 update 直接执行

liquibase update --changeLogFile=master.yaml \
  --driver=com.mysql.cj.jdbc.Driver \
  --url="jdbc:mysql://localhost:3308/schedule_develop" \
  --username=root \
  --password=mingliang 
  1. 开发环境
mvn spring-boot:run
# Liquibase自动创建表结构
  1. 测试环境
mvn clean package
java -jar order-system.jar
# 应用自动更新数据库结构
  1. 生产环境
# 备份数据库
mysqldump -u root -p order_db > backup_$(date +%Y%m%d).sql

# 执行迁移
java -jar order-system.jar
# 或使用命令行
liquibase --changeLogFile=master.yaml update

# 验证
mysql -u root -p order_db -e "SHOW TABLES;"
  1. 回滚示例(如果发现product_image字段有问题):

liquibase需要补充 --url 等参数,这里只示例回滚参数

# 回滚到上一版本
liquibase rollbackCount --rollbackCount=1

# 或回滚到指定版本
liquibase rollback v1.0.0
#  按日期回滚,回滚在指定日期和时间之后应用的所有变更集(包含指定的时间)。 
liquibase rollbackToDate --date="2025-12-13 21:52:58"

安全预览:回滚 SQL
在执行任何实际的回滚操作之前,强烈建议先预览将要执行的原始 SQL 语句。这些预览命令不会修改您的数据库。

  • 预览按标签回滚的 SQL: rollbackSql --rollbackTag=...
  • 预览按数量回滚的 SQL: rollbackCount-sql --rollbackCount=...
  • 预览按日期回滚的 SQL: rollbackToDateSql --rollbackDate=...

结语

参考内容:

如果这篇文章对你有帮助,请点赞、收藏、转发三连~

Q.E.D.


寻门而入,破门而出