添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

# 通过Maven用LiquiBase对数据库变更进行版本控制

# LiquiBase是什么

# 1.1 概述与特点

LiquiBase 是用于跟踪、管理和应用数据库变更的开源工具,它把数据库的变更记录在文件(xml/sql等)中,然后把变更作用到数据库中,并记录。我觉得它的主要特点:

(1)它支持多种数据库,如 Oracle/MySQL/PostgreSQL/DB2 等;

(2)提供数据库比较功能,把结果存在 xml 中,然后可基于该 xml 升级;

(3)会在数据库中保存修改历史,可查看记录、可支持回滚(按标签、数量和时间);已经升级的变更不会再操作;

(4)可通过 maven 集成,方便 CI/CD

# 1.2 基本概念

(1) change log 文件,用于记录数据库变更(changeset);可以把变更记录在 xml 文件、 sql 文件等;

(2) changeset ,变更集,作为一次变更的单位,可以是新建一张表、加一列,插入数据、删除索引等。升级与回滚都是以 changeset 为单位的;每个 changeset 会通过 id author 来标记。

(3) DATABASECHANGELOG 表用于记录被执行过的 changeset ,当回滚后,就会删去被回滚掉的记录,下表有6条记录,表示有6个 changeset 被执行了。记录了很多关键信息,如作者、所在文件、执行时间等。

# 2 项目整合

LiquiBase 有命令行工具,但本文主要介绍通过 maven 来操作。之前文章《 Docker启动PostgreSQL并推荐几款连接工具 (opens new window) 》介绍了如何快速安装 PostgreSQL ,那我们这次就用它作为数据库。

# 2.1 maven配置

主要通过 maven 的插件来执行变更,所以要引入 LiquiBase 的插件,及配置相关属性如下:

<build>
  <plugins>
    <plugin>
      <groupId>org.liquibase</groupId>
      <artifactId>liquibase-maven-plugin</artifactId>
      <version>4.1.1</version>
      <configuration>
        <propertyFileWillOverride>true</propertyFileWillOverride>
        <propertyFile>target/classes/liquibase/properties/liquibase.properties</propertyFile>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>org.postgresql</groupId>
          <artifactId>postgresql</artifactId>
          <version>42.2.18</version>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>

这里还要引入 PostgreSQL 是因为需要它的数据库驱动。

# 2.2 属性配置文件

运行 LiquiBase 需要传递一些参数,如文件名、数据库连接信息等,所以 liquibase.properties 的内容如下:

changeLogFile:  src/main/resources/liquibase/db.changelog.xml
driver:  org.postgresql.Driver
url:  jdbc:postgresql://localhost:5432/pkslow
username:  pkslow
password:  pkslow
verbose:  true
dropFirst:  false

# 2.3 数据库变更文件

变更文件 db.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"
        xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd
        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
    <changeSet  id="1"  author="larry">
        <createTable  tableName="person">
            <column  name="id"  type="int"  autoIncrement="true">
                <constraints  primaryKey="true"  nullable="false"/>
            </column>
            <column  name="firstname"  type="varchar(50)"/>
            <column  name="lastname"  type="varchar(50)">
                <constraints  nullable="false"/>
            </column>
            <column  name="state"  type="char(2)"/>
        </createTable>
        <rollback>
            <dropTable tableName="person"/>
        </rollback>
    </changeSet>
    <changeSet  id="2"  author="larry">
        <addColumn  tableName="person">
            <column  name="username"  type="varchar(8)"/>
        </addColumn>
        <rollback>
            <dropColumn tableName="person">
                <column  name="username"/>
            </dropColumn>
        </rollback>
    </changeSet>
</databaseChangeLog>

这个文件定义了两个变更集,第一个用于创建表 person ,第二个用于加字段 username ,同时提供了 rollback 语句以支持回滚。

准备好以上文件后,执行以下命令便可以使变更生效:

mvn clean package liquibase:update

查看数据库如下:

新建了一个 person 表,并有字段 username

因为执行了两个变更集,所以如果我想要回滚所有变更,命令如下:

mvn liquibase:rollback -Dliquibase.rollbackCount=2

# 3 进阶使用

LiquiBase 很强大,有很多强大的用法,这里选几个我觉得很有用的介绍一下。

# 3.1 执行条件判断

change log 文件中,可以指定前置判断条件,如下:

<preConditions>
  <dbms  type="oracle"/> 
  <runningAs  username="pkslow"/>
</preConditions>

这样表示数据库为 Oracle ,数据库的用户名为 pkslow 才允许执行。可以防止配置错误和升级错误。

# 3.2 多环境属性文件

通常我们的数据库都是与多个环境对应的,如 dev uat prod 等。我们可以通过 maven 的变量来指定环境:

<properties>
  <liquibase.env>dev</liquibase.env>
</properties>
<propertyFile>target/classes/liquibase/properties/liquibase.${liquibase.env}.properties</propertyFile>

默认值为 dev ,然后可以通过传参指定其它:

mvn clean package liquibase:update -Dliquibase.env=uat

这样它就能匹配到下面对应的文件:

liquibase.dev.properties
liquibase.uat.properties
liquibase.prod.properties

# 3.3 多种文件嵌套使用

LiquiBase 可以嵌套文件,如 xml 嵌套 xml 文件或 sql 文件:

<include file="xml/pkslow.person.xml" relativeToChangelogFile="true"/>
<include file="sql/pkslow.student.sql" relativeToChangelogFile="true"/>
<include file="sql/pkslow.order.sql" relativeToChangelogFile="true"/>

其实还支持 yaml json 等,但我觉得还是 xml sql 更适用。

# 3.4 多环境动态标识替换

不同数据库字段标识是不一样的,语法也会有不同,通过定义属性,能解决这个问题:

<property  name="clob.type"  value="clob"  dbms="oracle,postgresql"/> 
<property  name="clob.type"  value="longtext"  dbms="mysql"/> 
<property  name="table.name"  value="tableA"/> 
<changeSet  id="1"  author="joe"> 
  <createTable  tableName="${table.name}"> 
    <column  name="id"  type="int"/> 
    <column  name="column1"  type="${clob.type}"/> 
    <column  name="column2"  type="int"/> 
  </createTable> 
</changeSet> 

# 3.5 密码加密

把数据库密码以明文形式配置在文件中不是什么好习惯, LiquiBase 提供了替换属性类的功能,可以用来实现加密解密:

<configuration>
  <propertyFileWillOverride>true</propertyFileWillOverride>
  <propertyFile>target/classes/liquibase/properties/liquibase.${liquibase.env}.properties</propertyFile>
  <propertyProviderClass>com.pkslow.liquibase.PkslowLiquibaseProperties</propertyProviderClass>
</configuration>

propertyProviderClass 就是用来加密的类,如下:

package com.pkslow.liquibase;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import java.util.Properties;
public class PkslowLiquibaseProperties extends Properties {
    private static final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
    static {
        System.out.println("init for encryptor");
        //设置密钥
        encryptor.setPassword("pkslow-salt");
        //设置加密算法
        encryptor.setAlgorithm("PBEWithMD5AndTripleDES");
    @Override
    public synchronized Object put(Object key, Object value) {
        if ("password".equals(key)) {
            value = encryptor.decrypt((String) value);
        return super.put(key, value);

如果想,还可以加密其它字段。

# 3.6 强大的回滚能力

LiquiBase 提供了按数量、日期和标签回滚的能力,如下:

# 按数量回滚,2表示回滚两个changeset
mvn liquibase:rollback -Dliquibase.rollbackCount=2
# 按日期回滚
mvn liquibase:rollback "-Dliquibase.rollbackDate=Jun 03, 2017"
# 按标签回滚
mvn liquibase:rollback -Dliquibase.rollbackTag=V1.0

这里我觉得最实用的是按标签回滚功能。

每次版本升级,就打上一个标签,如 V1.0 V1.1 V2.0 等,如果有问题,就回滚至对应标签(版本)。添加标签命令如下:

mvn liquibase:tag -Dliquibase.tag=V1.1

我把三个文件分三次升级,打了三个 tag 如下:

现在回滚到 V1.1 如下:

mvn liquibase:rollback -Dliquibase.rollbackTag=V1.1

结果如下:

# 4 总结

相信本文能帮助大家理解和使用 LiquiBase ,代码请查看:https://github.com/LarryDpk/pkslow-samples


参考资料:

Jasypt加密: 如何使用优秀的加密库Jasypt来保护你的敏感信息? (opens new window)

XML标签:https://docs.liquibase.com/change-types/community/home.html

多种格式参考: Changelog (opens new window)

maven 整合指导: Maven (opens new window)

另一种整合方式,把密码信息保存在settings.xml里: Liquibase with Maven (opens new window)

其它用法: The Magic of Using XML Changelogs in Liquibase (opens new window)

参数参考: LiquiBase 管理数据库变更实践 (opens new window)

上次更新: 2023/8/18 23:39:36