一直以来,部门内部对于数据库结构的变更跟踪都处于一种切肤之痛,却又无暇顾及的状态。
Liquibase 是一个用于跟踪,管理和应用数据库变化的开源的数据库重构工具。它将所有数据库的变化(包括结构和数据) 都保存在XML文件中,便于版本控制。
本文主要关注平时在liquibase使用过程中经常会遇到的一些需求和相应的对策,尝试提供一站式解决方案。
从官方提供的文档 可以看出,liquibase提供了多种使用方式,例如Ant,CommandLine,Maven Plugin等等。下面将挑选比较常用的来进行说明,方便读者快速入门。
本文主要受众是研发人员,因此这里将与SpringBoot的集成放在首位。
其实liquibase与SpringBoot的集成可谓相当简单,因为SpringBoot的AutoConfig特性内置了对其的支持。
spring: liquibase: # 显式启用 enabled: true # 配置文件的路径,默认值为 classpath:/db/changelog/db.changelog-master.yaml # 这是总配置文件 change-log: classpath:/db/changelog/db.changelog-master.xmldb.changelog-master.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.1.xsd"> <!-- <includeAll path="classpath:liquibase/changelog" /> --> <!-- 以下排列順序就是執行順序 --> <!-- 至于sample-dsl-xml-table.xml中的内容,留待下面专门的小节进行专门讲解 --> <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。可配置属性项的集散地。参考链接:
官方文档 - SpringBoot集成一起来学SpringBoot | 第二十四篇:数据库管理与迁移(Liquibase)以笔者个人的经验,使用到Maven Plugin的时候主要是单独性地测试changesets,进行数据库对比,以及对以存在的数据库进行逆向,将其纳入版本控制中。
引入Maven Plugin
<plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>3.4.2</version> <configuration> <changeLogFile>src/test/resources/liquibase/changelog/sample-dsl-xml-table.xml</changeLogFile> <driver>com.mysql.jdbc.Driver</driver> <url>jdbc:mysql://81.xx.xxx.xx:3306/xxxxx</url> <username>root</username> <password>123456</password> <!--配置参数,以禁用弹出的对话框,该对话框将确认非本地数据库上的迁移 --> <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase> </configuration> <executions> <execution> <phase>process-resources</phase> <goals> <goal>update</goal> </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'参考链接:
官方文档 - Maven Plugin集成数据库的版本控制的受众不仅仅是研发类岗位,对此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参考链接: 1.官方文档 - CLI集成 2. Installing Liquibase Command Line for Windows
直接参考官方文档 - servlet-listener。
我们平时会使用到的主要分为两类格式 sql和xml。
文件格式优点缺点参考sql复用SQL知识。缺少提示,数据库强相关。官方文档 - sql-formatxml1. 数据库无关。 2. 有XSD(智能提示和校验)。1. 一定的学习曲线。但在XSD提供的智能提示以及XML标签的良好命名规范下,相信这些都不会是主要障碍。官方文档 - xml-formatSQL
<!-- 基于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> <!-- 基于SQL文件 --> <changeSet id="6" author="joe"> <sqlFile path="insert-distributor-data.sql"/> </changeSet>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.1.xsd"> <!-- office document : https://docs.liquibase.com/concepts/basic/xml-format.html --> <!-- ################### 统一配置属性, 供之后使用 ################### --> <property name="now" value="now()" dbms="mysql" /> <!-- 不同的数据库, 不同的配置 ( https://docs.liquibase.com/concepts/basic/changelog-parameters.html --> <property name="clob.type" value="clob" dbms="oracle,postgresql"/> <property name="clob.type" value="longtext" dbms="mysql"/> <!-- 基于XML, 这种方式与数据库无关, 迁移性强; 有XSD提示; 但需要熟悉其语法 --> <!-- ################### Table相关 ################### --> <!-- 创建表 --> <changeSet id="addTable" author="fulizhe"> <!-- preConditions --> <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> <!-- LookupTable --> <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> <!-- 请注意,这一条在liquibase4.1.0是没有的,虽然官方文档里确实有这个。 https://docs.liquibase.com/change-types/community/set-table-remarks.html <changeSet id="setTableRemarks" author="fulizhe"> <preConditions> <tableExists tableName="tb2_member2" /> </preConditions> <setTableRemarks remarks="this is a remark by liquibase" tableName="tb2_member2"/> </changeSet> --> <!-- ################### Column相关 ################### --> <!-- 添加字段 --> <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)"/> <!-- 修改字段类型 ( https://blog.csdn.net/m0_38105216/article/details/83896599 ) --> <modifyDataType tableName="tb2_member" columnName="nick_name_new" newDataType="varchar(20)" /> <!-- 设置Column注释, 和上面的Table类似,没有提供 <setColumnRemarks columnName="nick_name_new" remarks="A String" tableName="tb2_member"/> --> <!-- 将一个已经存在的Column修改为自增Column --> <addAutoIncrement columnDataType="int" columnName="id" incrementBy="2" startWith="100" tableName="tb2_member"/> </changeSet> <!-- ################### Index相关 ################### --> <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=100</where> </update> <delete tableName="tb2_member"> <where>id=100</where> </delete> <!-- Other loadData: Loads data from a CSV file into an existing table when you add it to your changelog. loadUpdateData: Loads or updates data from a CSV file into an existing table. mergeColumns: Concatenates the values in two columns and joins them with a string, and stores the resulting value in a new column. executeCommand: Executes a system command. <executeCommand executable="mysqldump" os="Windows 7" timeout="10s"> <arg value="-out"/> <arg value="-param2"/> </executeCommand> stop: Stops Liquibase execution with a message. Mainly useful for debugging and stepping through a changelog <stop message="What just happened???"/> output: Logs a message and continues execution. 类似于日志系统, 注意这个在CLI中并没有出现. <output target="STDERR">Make sure you feed the cat</output> customChange: creates a custom Change Type class. 通过该标签,用户将拥有针对数据库的完全自主权,用户可以按照自己的需求应对数据库的变更逻辑。 --> </databaseChangeLog>参考链接: 3. 官方文档 - change-types 其中按照被修改的对象进行了分类。 4. 官方文档 - Basic Concepts
这里直接借用网上资料,具体出处找不到了:
ChangeSet ID 建议使用: a. Flayway的命名格式V<version>[_<SEQ>][__description],如V1.0_0__init。 b. [任务ID]-[日期]-[序号],如T100-20190705-001 。ChangeSet 必须填写author。liquibase禁止对业务数据进行SQL操作。使用<sql>的时候,禁止包含schema名称。liquibase禁止使用存储过程。所有表,列都加remarks进行注释。若脚本有变动,勿直接对脚本进行修改,添加修改脚本并引入。尤其是对于已经提交近SCM的脚本。不要随意升级liquibase,特别是大版本升级。不同版本ChangeSet MD5SUM的算法不一样。