LiquiBase是一個用於數據庫重構和遷移的開源工具,通過日志文件的形式記錄數據庫的變更,然后執行日志文件中的修改,將數據庫更新或回滾到一致的狀態。
LiquiBase的主要特點有:
- 支持幾乎所有主流的數據庫,如MySQL, PostgreSQL, Oracle, Sql Server, DB2等;
- 支持多開發者的協作維護;
- 日志文件支持多種格式,如XML, YAML, JSON, SQL等;
- 支持多種運行方式,如命令行、Spring集成、Maven插件、Gradle插件等;
本文首先簡單介紹一下LiquiBase的changelog文件的常用標簽配置,然后介紹在Maven中集成並運行LiquiBase。
1. changelog文件格式
changelog是LiquiBase用來記錄數據庫的變更,一般放在CLASSPATH
下,然后配置到執行路徑中。
changelog支持多種格式,主要有XML/JSON/YAML/SQL,其中XML/JSON/YAML除了具體格式語法不同,節點配置很類似,SQL格式中主要記錄SQL語句,這里僅給出XML格式和SQL格式的示例,更多的格式示例請參考文檔
changelog.xml
<changeSet id="2" author="daniel" runOnChange="true"> <insert tableName="contest_info"> <column name="id">3</column> <column name="title">title 3</column> <column name="content">content 3</column> </insert> </changeSet>
changelog.sql
--liquibase formatted sql --changeset daniel:16040707 CREATE TABLE `role_authority_sum` ( `row_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id', `role_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '關聯role的role_id', `authority_sum` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'perms的值的和', `data_type_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '關聯data_type的id', PRIMARY KEY (`row_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色的權限值的和,如角色有RD權限,則和為2+8=10';
2. 常用的標簽及命令
2.1 標簽
一個<changeSet>標簽對應一個變更集,由id、name、以及changelog的文件路徑組成唯一標識。changelog在執行的時候並不是按照id的順序,而是按照changeSet在changelog中出現的順序。
LiquiBase在執行changelog時,會在數據庫中插入兩張表:DATABASECHANGELOG
和DATABASECHANGELOGLOCK
,分別記錄changelog的執行日志和鎖日志。
LiquiBase在執行changelog中的changeSet時,會首先查看DATABASECHANGELOG
表,如果已經執行過,則會跳過(除非changeSet的runAlways
屬性為true,后面會介紹),如果沒有執行過,則執行並記錄changelog日志;
changelog中的一個changeSet對應一個事務,在changeSet執行完后commit,如果出現錯誤則rollback;
<changeSet>
標簽的主要屬性有:
- runAlways:即使已經執行過,仍然每次都執行;注意: 由於
DATABASECHANGELOG
表中還記錄了changeSet的MD5校驗值MD5SUM,如果changeSet的id
和name
沒變,而內容變了,則由於MD5值變了,即使runAlways的值為True,執行也是失敗的,會報錯。這種情況應該使用runOnChange
屬性。 - runOnChange:第一次的時候執行以及當changeSet的內容發生變化時執行。不受MD5校驗值的約束。
- runInTransaction:是否作為一個事務執行,默認為true。設置為false時需要小心:如果執行過程中出錯了則不會rollback,數據庫很可能處於不一致的狀態;
<changeSet>
下有一個重要的子標簽<rollback>
,即定義回滾的SQL語句。對於create table
, rename column
和add column
等,LiquiBase會自動生成對應的rollback語句,而對於drop table
、insert data
等則需要顯示定義rollback語句。
2.2 <include>
與<includeAll>
標簽
當changelog文件越來越多時,可以使用<include>
將文件管理起來,如:
<?xml version="1.0" encoding="utf-8"?> <databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd"> <include file="logset-20160408/0001_authorization_init.sql" relativeToChangelogFile="true"/> </databaseChangeLog>
<include>
的file屬性表示要包含的changelog文件的路徑,這個文件可以是LiquiBase支持的任意格式,relativeToChangelogFile如果為true,則表示file屬性表示的文件路徑是相對於根changelog而不是CLASSPATH的,默認為false。
<includeAll>
指定的是changelog的目錄,而不是為文件,如:
<includeAll path="com/example/changelogs/"/>
注意: 目前<include>
沒有解決重復引用和循環引用的問題,重復引用還好,LiquiBase在執行的時候可以判斷重復,而循環引用會導致無限循環,需要注意!
2.3 diff命令
diff命令用於比較數據庫之間的異同。比如通過命令行執行:
java -jar liquibase.jar --driver=com.mysql.jdbc.Driver \
--classpath=./mysql-connector-java-5.1.29.jar \
--url=jdbc:mysql://127.0.0.1:3306/test \
--username=root --password=passwd \
diff \
--referenceUrl=jdbc:mysql://127.0.0.1:3306/authorization \
--referenceUsername=root --referencePassword=passwd
2.4 generateChangeLog
在已有的項目上使用LiquiBase,要生成當前數據庫的changeset,可以采用兩種方式,一種是使用數據庫工具導出SQL數據,然后changelog文件以SQL格式記錄即可;另一種方式就是用generateChangeLog
命令,如:
liquibase --driver=com.mysql.jdbc.Driver \
--classpath=./mysql-connector-java-5.1.29.jar \
--changeLogFile=liquibase/db.changelog.xml \
--url="jdbc:mysql://127.0.0.1:3306/test" \
--username=root \
--password=yourpass \
generateChangeLog
不過generateChangeLog
不支持以下功能:存儲過程、函數以及觸發器;
3. Maven集成LiquiBase
3.1 liquibase-maven-plugin
的配置
Maven中集成LiquiBase,主要是配置liquibase-maven-plugin
,首先給出一個示例:
<plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>3.4.2</version> <configuration> <changeLogFile>src/main/resources/liquibase/test_changelog.xml</changeLogFile> <driver>com.mysql.jdbc.Driver</driver> <url>jdbc:mysql://127.0.0.1:3306/test</url> <username>root</username> <password>passwd</password> </configuration> <executions> <execution> <phase>process-resources</phase> <goals> <goal>update</goal> </goals> </execution> </executions> </plugin>
其中<configuration>
節點中的配置可以放在單獨的配置文件里。
如果需要在父項目中配置子項目共享的LiquiBase配置,而各個子項目可以定義自己的配置,並覆蓋父項目中的配置,則只需要在父項目的pom中將propertyFileWillOverride
設置為true即可,如:
<plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>3.4.2</version> <configuration> <propertyFileWillOverride>true</propertyFileWillOverride> <propertyFile>liquibase/liquibase.properties</propertyFile> </configuration> </plugin>
3.2 liquibase:update
執行changelog中的變更:
$ mvn liquibase:update
3.3 liquibase:rollback
rollback有3中形式,分別是:
- rollbackCount: 表示rollback的changeset的個數; - rollbackDate:表示rollback到指定的日期; - rollbackTag:表示rollback到指定的tag,需要使用LiquiBase在具體的時間點打上tag;
rollbackCount
比較簡單,示例如:
$ mvn liquibase:rollback -Dliquibase.rollbackCount=3
rollbackDate
需要注意日期的格式,必須匹配當前平台上執行DateFormat.getDateInstance()
得到的格式,比如我的格式為MMM d, yyyy
,示例如:
$ mvn liquibase:rollback -Dliquibase.rollbackDate="Apr 10, 2016"
rollbackTag
使用tag標識,所以需要先打tag,示例如:
$ mvn liquibase:tag -Dliquibase.tag=tag20160410
然后rollback到tag20160410,如:
$ mvn liquibase:rollback -Dliquibase.rollbackTag=tag20160410