# 通过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)
远程过程调用框架gRPC入门及Java示例代码 Java如何设置代理来访问受限资源