数据库版本管理 Liquibase 的基本使用

550 阅读5分钟

🚀为什么需要 Liquibase

官方网站:

www.liquibase.org

在实际开发中,数据库中的表设计往往不是一成不变的,可能在某个版本中我们需要加入几个字段;或者加入一张表;修改某个字段的约束;添加一些初始数据;等等…… 而在个人开发中,这种修改造成的影响尚可以忍受,毕竟自己知道数据库做了哪些更改,或者自己记录了文档方便自己回溯。 但是,在团队开发中,数据库的版本修改造成的影响就比较大了,例如:

  1. 团队中某个成员没有同步更新数据库,影响开发进度
  2. 由于没有同步更新数据库,某个成员使用了错误的数据

除了数据库版本不一致对开发带来的影响之外,还有一些原因让我们需要 Liquibase:

  1. 手工管理数据库版本,繁琐
  2. 没有一致的方法管理数据库版本变更
  3. 日志不好维护
  4. 等等

Liquibase 能够让开发者通过日志的方式记录数据库版本的变更,执行日志中的修改,将数据库更新,或者回滚到某一个版本。Liquibase 有如下特点:

  • 支持几乎所有主流的数据库:MySQL,Oracle,SQLServer 等
  • 支持多开发者的协作维护
  • 日志文件支持多种格式,xml,yaml,json,sql 等
  • 支持多种运行方式,命令行、Maven 插件、Gradle 插件等

本文将通过 maven 插件的方式演示如何使用 Liquibase

🚀初试 Liquibase

初始化项目

使用 IDEA 新建一个 SpringBoot 项目,不使用任何依赖,下面的 pom.xml 文件列出需要用到的依赖和插件

<dependencies>
    <!-- 一个 SpringBoot 项目 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.3.7.RELEASE</version>
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
        <version>5.1.49</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <!-- Liquibase 插件 -->
        <plugin>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-maven-plugin</artifactId>
            <version>4.17.2</version>
            <configuration>
                <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
                <!-- liquibase 的配置文件位置 -->
                <propertyFile>db/liquibase-mysql.properties</propertyFile>
                <!-- 设置每次回滚的变更集数 -->
                <rollbackCount>1</rollbackCount>
            </configuration>
        </plugin>
    </plugins>
</build>

编写 application.yml

spring:
  application:
    name: Liuqibase-demo
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC

编写 Liquibase 的配置文件

在资源目录下新建目录db,在该目录下编写 Liquibase 的配置文件 liquibase-mysql.properties,主要配置使用的数据库的基本信息

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username=root
password=123456
# 变更日志文件的入口
changeLogFile=db/changelog-master.xml

编写变更日志

在资源目录的db目录下存放数据库相关文件,目录结构如下:

# 资源目录
- db
	- 1.0.0												# 数据库 1.0.0 版本
  	- changelog.xml							# 变更日志
	- changelog-master.xml				# 数据库变更日志主入口文件
	- liquibase-mysql.properties	# liquibase 配置文件

Liquibase 的 xml 文件的基础模板如下:

<?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-3.8.xsd">
</databaseChangeLog>

db目录下创建数据库变更日志主入口文件:changelog-master.xml,Liquibase 会根据包含关系层层往下,执行变更集。

<?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-3.8.xsd">
  <!-- 包含的变更日志 -->
  <include file="1.0.0/changelog.xml" relativeToChangelogFile="true"/>
  <!-- 当变更日志数量庞大时,也可以使用 includeAll 包含一个目录,这将引入目录下的所有变更日志 -->
</databaseChangeLog>

db\1.0.0 目录下创建变更日志:changelog.xml

<?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-3.8.xsd">
  <!-- 变更集,必须指定 id 和 author -->
  <changeSet id="1.0.0-20221107" author="abc">
    <!-- 主要操作,创建一张表 -->
    <createTable tableName="test_liquibase">
      <column name="id" type="varchar(50)" remarks="主键">
        <constraints primaryKey="true" />
      </column>
      <column name="name" type="varchar(50)" remarks="名字"/>
    </createTable>
    <!-- 这个变更集的回滚操作 -->
    <rollback>
      <dropTable tableName="test_liquibase"/>
      <!-- 也可以直接编写sql,例如下面的示例 -->
      <!-- <sql>drop table test_liquibase</sql>-->
    </rollback>
  </changeSet>

</databaseChangeLog>

执行版本变更

❗❗❗ 注意,每次修改了变更日志,都需要执行 maven 的生命周期插件 package 重新打包,将变更日志文件更新到 target目录中。

使用 maven 插件:liquibase:update 将数据库变更更新到数据库中,执行成功后应该能看到如下语句:

[INFO] BUILD SUCCESS

💡可以使用 liquibase:help 查看插件的使用说明

初次执行成功后,会在指定的数据库中添加两张表databasechangelogdatabasechangeloglock,这是Liquibase 维护变更日志的。 查看数据库,可以看到我们的添加表的操作成功执行了:

image.png

继续修改数据库版本

database 目录下创建 2.0.0 目录,创建变更日志:changelog.xml

<?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-3.8.xsd">
  <changeSet id="2.0.0-20221107" author="abc">
    <!-- 新增加一个字段 -->
    <addColumn tableName="test_liquibase">
      <column name="age" type="int(1)" remarks="年龄"/>
    </addColumn>
    <!-- 回滚操作 -->
    <rollback>
      <dropColumn tableName="test_liquibase" columnName="age"/>
    </rollback>
  </changeSet>

  <changeSet id="insert" author="abc">
    <!-- 该变更集的主要操作为执行一段 sql,添加数据 -->
    <!-- sqlFile 标签配置要执行的 sql 脚本 -->
    <sqlFile path="data.sql" relativeToChangelogFile="true"/>
    <rollback>
      <!-- 回滚脚本 -->
      <sqlFile path="rollback.sql" relativeToChangelogFile="true"/>
    </rollback>
  </changeSet>

</databaseChangeLog>

2.0.0目录下分别创建 data.sqlrollback.sql 文件

-- liquibase formatted sql
-- changeset abc:data-20221107
insert into test_liquibase values ('11111', 'name', 18);
-- liquibase formatted sql
-- changeset abc:rollback-20221107
delete from test_liquibase where id='11111';

❗❗❗ sql 脚本中,文件开头需要编写如下文本,标识作者,id

-- liquibase formatted sql
-- changeset author:id

执行变更,执行 maven 插件 liquibase:update,查看结果:

mysql> select * from test_liquibase;
+-------+------+------+
| id    | name | age  |
+-------+------+------+
| 11111 | name |   18 |
+-------+------+------+
1 row in set (0.00 sec)

进行回滚操作

执行一次 maven 插件操作:liquibase:rollback,根据上面的配置文件的设置,Liquibase 会回滚一个变更集,即将刚才插入的数据删除。 查看结果:

mysql> select * from test_liquibase;
Empty set (0.00 sec)

🚀一些标签的说明

  • <include>,包含另一个变更集文件,一般主入口文件使用该标签,去包含变更版本中的变更日志文件
  • <includeAll>,包含整个目录中变更集文件
  • <changeSet>标签对应一个变更集,即一个变更操作,通常一个变更日志文件中会有该标签或者<include>。一些常见属性:
    • id:唯一,标识该变更集
    • author:该变更集的作者
    • dbms:执行的数据库
  • changeSet 的子标签:
    • <sql>,编写一段 sql 脚本
    • <sqlFile>,引用其他 sql 脚本文件
    • <createTable>,创建一张表
    • <addColumn>,添加一个字段
    • <rollback>,回滚操作

🚀注意点

在实际使用过程中,建议一个变更版本使用一个变更日志主文件,包含其他变更日志,一个变更日志对应一个日志文件。 对于数据的插入操作,建议使用 sql 脚本的方式插入,这样文件结构看起来更清晰。 如:

- database
	- 1.0.0
  	- data.sql
  	- XxxChangeSet1.xml
		- XxxChangeSet2.xml
  	- changelog.xml
	- changelog-master.xml

liquibase 的 maven 插件的使用说明,可以使用其提供的插件:liquibase:help进行查看

🚀总结

Liquibase 可以帮助我们管理数据库版本,使得数据库版本能够与应用程序版本一致,便于开发维护。

本文介绍了 Liquibase 的简单用法,更多用法还请参考官网自行学习。