一直以来,部门内部对于数据库结构的变更跟踪都处于一种切肤之痛,却又无暇顾及的状态。
Liquibase 是一个用于跟踪,管理和应用数据库变化的开源的数据库重构工具。它将所有数据库的变化(包括结构和数据) 都保存在XML文件中,便于版本控制。
本文主要关注平时在liquibase使用过程中经常会遇到的一些需求和相应的对策,尝试提供一站式解决方案。
从官方提供的文档 可以看出,liquibase提供了多种使用方式,例如Ant,CommandLine,Maven Plugin等等。下面将挑选比较常用的来进行说明,方便读者快速入门。
本文主要受众是研发人员,因此这里将与SpringBoot的集成放在首位。
其实liquibase与SpringBoot的集成可谓相当简单,因为SpringBoot的AutoConfig特性内置了对其的支持。
pom.xml
<dependency>
<groupId>org.liquibasegroupId>
<artifactId>liquibase-coreartifactId>
dependency>
application.yml
spring:
liquibase:
# 显式启用
enabled: true
# 配置文件的路径,默认值为 classpath:/db/changelog/db.changelog-master.yaml
# 这是总配置文件
change-log: classpath:/db/changelog/db.changelog-master.xml
db.changelog-master.xml 的内容形如:
<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.1.xsd">
<include file="classpath:liquibase/changelog/sample-dsl-xml-table.xml"/>
<include file="classpath:liquibase/changelog/sample-dsl-xml-column.xml"/>
<include file="classpath:liquibase/changelog/sample-dsl-xml-data.xml"/>
databaseChangeLog>
以上两步就算是完成了liquibase与SpringBoot的集成。
一些源码细节:
LiquibaseAutoConfiguration
。SpringBoot中对于liquibase的自动化配置入口。其中的SpringLiquibase
实现了 InitializingBean
接口。LiquibaseProperties
。可配置属性项的集散地。参考链接:
以笔者个人的经验,使用到Maven Plugin的时候主要是单独性地测试changesets,进行数据库对比,以及对以存在的数据库进行逆向,将其纳入版本控制中。
引入Maven Plugin
<plugin>
<groupId>org.liquibasegroupId>
<artifactId>liquibase-maven-pluginartifactId>
<version>3.4.2version>
<configuration>
<changeLogFile>src/test/resources/liquibase/changelog/sample-dsl-xml-table.xmlchangeLogFile>
<driver>com.mysql.jdbc.Driverdriver>
<url>jdbc:mysql://81.xx.xxx.xx:3306/xxxxxurl>
<username>rootusername>
<password>123456password>
<promptOnNonLocalDatabase>falsepromptOnNonLocalDatabase>
configuration>
<executions>
<execution>
<phase>process-resourcesphase>
<goals>
<goal>updategoal>
goals>
execution>
executions>
plugin>
常用的Maven Plugin Commands:
# 执行某个changelog
mvn liquibase:update -Dliquibase.changeLogFile=xxxx.xml
# 对比两个数据库结构, 最终结果存放在指定位置. 注意最终的结果反映的是由源数据库结构变成目标数据库结构(由referenceUrl指示), 需要作出哪些变化.
mvn liquibase:diff '-Dliquibase.referenceUrl=jdbc:mysql://81.xx.xxx.xxx:3307/xxx' '-Dliquibase.referenceUsername=root' '-Dliquibase.referencePassword=123456' '-Dliquibase.diffChangeLogFile=D:/111.xml'
# 逆向工程 - 生成当前数据库的changeset, 默认是生成数据库结构, 不包含数据
mvn liquibase:generateChangeLog
# 逆向工程 - 生成当前数据库的changeset, 只包含数据; diffTypes的可选值可参照文档(文档的生成参见下方命令)
mvn liquibase:generateChangeLog '-Dliquibase.diffTypes="data"'
# 生成数据库结构文档
# 生成的文档默认位于target/liquibase/dbDoc目录,入口文件为其中的index.html。
mvn liquibase:dbDoc
# 获取liquibase-maven-plugin的完整帮助文档
mvn help:describe '-DgroupId=org.liquibase' '-DartifactId=liquibase-maven-plugin' '-Dversion=3.4.2' '-Dfull=true'
参考链接:
数据库的版本控制的受众不仅仅是研发类岗位,对此liquibase给出的解决方案是 CLI工具。
安装。
首先从 liquibase CLI下载地址 下载相应操作系统版本的liquibase二进制包,笔者这里下载的是 Liquibase 4.1.0-Windows-20200928
。
# 如果本地下载太慢,笔者的解决方案是找个云服务器(例如腾讯云),在上面下载完之后拉取到本地。
wget -c https://github.com/liquibase/liquibase/releases/download/v4.1.0/liquibase-4.1.0.zip
这里千万别想当然地以为是我们通过Maven方式引入的那个liquibase-core-3.8.8.jar
。虽然两者的内部结构是一致的,但 Liquibase 4.1.0-Windows-20200928
中不仅仅包含liquibase.jar
,还附带了相应的JAR依赖,以及一组示例代码等。
配置。
# 1. 下载的liquibase-4.1.0.zip解压到任意目录,并配置相应的环境变量Path。(笔者有强迫症,这里就以powershell下为例)
$Env:path=$Env:Path+";D:\liquibase-4.1.0"
# 获取完整的文档信息
liquibase --help
示例。
################################ 准备工作
# 这里我们假设:
# 1. 操作目录:C:\Users\{User}\Desktop\liquibase\
# 2. liquibase-4.1.0-bin目录:D:\liquibase-4.1.0\
# 将 liquibase.properties , changelog-dsl-xml-table.xml, mysql-connector-java-8.0.13.jar 放置到 C:\Users\{User}\Desktop\liquibase\ 下。
# liquibase.properties中的内容
driver: com.mysql.jdbc.Driver
classpath: ./mysql-connector-java-8.0.13.jar
url: jdbc:mysql://81.68.158.66:3307/kqauth?useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
# 打开powershell, 切换到操作目录
cd C:\Users\{
User}\Desktop\liquibase\
################################ 常用操作
# dbDoc : 生成数据库结构文档 ( 经过简单测试:changeLogFile参数对生成的文档没有影响,但又必须要 )
java -jar D:\liquibase-4.1.0\liquibase.jar dbDoc D:/liquibase/ --logLevel=error --changeLogFile=changelog-dsl-xml-table.xml
# update : 执行某个changelog
java -jar D:\liquibase-4.1.0\liquibase.jar --changeLogFile=changelog-dsl-xml-table.xml update
# 逆向工程 - 生成当前数据库的changeset, 默认是生成数据库结构, 不包含数据
java -jar D:\liquibase-4.1.0\liquibase.jar --changeLogFile=generateChangeLog.xml generateChangeLog
# 逆向工程 - 生成当前数据库的changeset, 只包含数据; diffTypes的可选值可参照文档.
java -jar D:\liquibase-4.1.0\liquibase.jar --changeLogFile=generateChangeLog-data.xml --diffTypes="data" generateChangeLog
# 对比两个数据库结构, 最终结果存放在指定位置.
# 注意最终的结果反映的是由源数据库结构变成目标数据库结构(由referenceUrl指示), 需要作出哪些变化.
java -jar D:\liquibase-4.1.0\liquibase.jar --referenceUrl=jdbc:mysql://81.68.158.66:3306/kqauth --referenceUsername=root --referencePassword=123456 --changeLogFile=D:/111.xml --diffTypes="data" diff
参考链接:
直接参考官方文档 - servlet-listener。
对于一些历史项目,还是以spring为主的,这里提供两个参考链接:
我们平时会使用到的主要分为两类格式 sql和xml。
文件格式 | 优点 | 缺点 | 参考 |
---|---|---|---|
sql | 复用SQL知识。 | 缺少提示,数据库强相关。 | 官方文档 - sql-format |
xml | 1. 数据库无关。 2. 有XSD(智能提示和校验)。 |
1. 一定的学习曲线。但在XSD提供的智能提示以及XML标签的良好命名规范下,相信这些都不会是主要障碍。 | 官方文档 - xml-format |
SQL
<changeSet id="createTable" author="fulizhe">
<sql>
create table users ( id int primary key not null , name varchar(20) )
sql>
<sql>
insert into users ( id, name ) values (1, 'fulizhe')
sql>
changeSet>
<changeSet id="6" author="joe">
<sqlFile path="insert-distributor-data.sql"/>
changeSet>
XML
<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.1.xsd">
<property name="now" value="now()" dbms="mysql" />
<property name="clob.type" value="clob" dbms="oracle,postgresql"/>
<property name="clob.type" value="longtext" dbms="mysql"/>
<changeSet id="addTable" author="fulizhe">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="tb2_member" />
not>
preConditions>
<createTable tableName="tb2_member">
<column name="id" type="bigint" autoIncrement="true" remarks="主键">
<constraints primaryKey="true" nullable="false" />
column>
<column name="mobile" type="varchar(50)" remarks="手机号">
<constraints nullable="false" />
column>
createTable>
<addLookupTable existingTableName="tb2_member"
existingColumnName="mobile"
newTableName="tb2_member_COPY"
newColumnName="mobile" />
changeSet>
<changeSet id="dropTable" author="fulizhe">
<preConditions>
<tableExists tableName="tb2_member" />
preConditions>
<dropTable tableName="tb2_member" />
changeSet>
<changeSet id="renameTable" author="fulizhe">
<preConditions>
<tableExists tableName="tb2_member" />
preConditions>
<renameTable
oldTableName="tb2_member"
newTableName="tb2_member2"
/>
changeSet>
<changeSet id="addColumn" author="WATER" context="add-column">
<preConditions>
<tableExists tableName="tb2_member" />
preConditions>
<addColumn tableName="tb2_member">
<column name="nick_name" type="varchar(10)" remarks="昵称">
column>
addColumn>
changeSet>
<changeSet id="modifyColumn" author="WATER" context="add-column">
<preConditions>
<tableExists tableName="tb2_member" />
preConditions>
<renameColumn tableName="tb2_member" oldColumnName="nick_name"
newColumnName="nick_name_new" columnDataType="varchar(20)"/>
<modifyDataType tableName="tb2_member" columnName="nick_name_new" newDataType="varchar(20)" />
<addAutoIncrement
columnDataType="int"
columnName="id"
incrementBy="2"
startWith="100"
tableName="tb2_member"/>
changeSet>
<createIndex
indexName="idx_mobile"
tableName="tb2_member"
unique="true">
<column name="mobile"/>
createIndex>
<dropIndex
indexName="idx_mobile"
tableName="tb2_member"/>
<insert
dbms="!h2"
tableName="tb2_member">
<column name="mobile" value="132"/>
insert>
<update tableName="tb2_member">
<column name="mobile" value="address value"/>
<where>id=100where>
update>
<delete tableName="tb2_member">
<where>id=100where>
delete>
databaseChangeLog>
参考链接:
这里直接借用网上资料,具体出处找不到了:
V[_][__description],如V1.0_0__init
。T100-20190705-001
。
的时候,禁止包含schema名称。