# 通过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会通过idauthor来标记。

(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 多环境属性文件

通常我们的数据库都是与多个环境对应的,如devuatprod等。我们可以通过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"/>

其实还支持yamljson等,但我觉得还是xmlsql更适用。

# 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.0V1.1V2.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