Liquibase-數據庫版本管理使用
- Liquibase-數據庫版本管理
- 一、數據庫版本管理說明
- 1、liquibase介紹
- 1.1、changelog文件格式
- 2、flyway介紹
- 3、liquibase與flyway比較
- 1、liquibase介紹
- 二、spring boot + liquibase
- 1、gradle配置引入liquibase包:
- 2、修改application.yml或加LiquibaseConfig.java
- 2.1、application.yml
- 2.2、LiquibaseConfig.java
- 3、新增changelog.xml
- 3.1、新增master.xml
- 3.2、新增V1.1__init.sql(以sql語句方式,也可以用xml、yml、json請查看官網)
- 三、gradle + liquibase插件
- 1、修改build.gradle文件
- 1.1、多模塊項目在根路徑修改build.gradle
- 1.2、子模塊修改build.gradle
- 2、命令示例
- 3、命令詳解
- 3.1、數據庫更新命令
- 3.2、數據庫回滾命令
- 3.2.1、直接執行回滾
- 3.2.2、生成回滾腳本
- 3.2.3、生成“未來回滾”腳本
- 3.2.4 回滾命令
- 3.3、差異命令
- 3.4、文檔命令
- 3.5、維護命令
- 3.6、必需參數
- 3.7、可選參數
- 3.8、必需的Diff參數
- 3.9、可選的Diff參數
- 3.10、更改日誌屬性
- 3.11、liquibase.activities詳解
- 4、插件升級
- 1、修改build.gradle文件
- 一、數據庫版本管理說明
Liquibase-數據庫版本管理
一、數據庫版本管理說明
數據庫遷移工具很多,這裏我們選擇Flyway和Liquibase來說主要是兩個原因,
一是它們都是Java生態圈的,其次就是Spring Boot提供了這兩者的內建支持,可以很快應用到產品中。
1、liquibase介紹
LiquiBase是一個用於數據庫重構和遷移的開源工具,通過日誌文件的形式記錄數據庫的變更,然後執行日誌文件中的修改,將數據庫更新或回滾到一致的狀態。
LiquiBase的主要特點有:
支持幾乎所有主流的數據庫,如MySQL, PostgreSQL, Oracle, Sql Server, DB2等
支持多開發者的協作維護
日誌文件支持多種格式,如XML, YAML, JSON, SQL等
支持多種運行方式,如命令行、Spring集成、Maven插件、Gradle插件等
1.1、changelog文件格式
changelog是LiquiBase用來記錄數據庫的變更,一般放在CLASSPATH下,然後配置到執行路徑中。
changelog支持多種格式,主要有XML/JSON/YAML/SQL,其中XML/JSON/YAML除了具體格式語法不同,節點配置很類似,SQL格式中主要記錄SQL語句,以下示例僅給出SQL格式的示例,更多的格式示例請參考文檔
2、flyway介紹
flyway相對簡單,直接將你需要執行的SQL語句保存為文件,放入應用中執行即可。
Flyway的好處在於簡單,而且直接書寫SQL並不需要額外的學習。社區版的不支持UNDO操作,需要購買企業版。
3、liquibase與flyway比較
Flyway 自動升級(自動發現更新項):Flyway 會將任意版本的數據庫升級到最新版本。
Flyway 可以脫離JVM 環境通過命令行執行,可以通過Ant 腳本執行,通過Maven 腳本執行(這樣就可以在集成環境自動執行),並且可以在應用中執行(比如在應用啟動時執行)。
Flyway 規約優於配置:Flyway 有一套默認的規約,所以不需要修改任何配置就可以正常使用。
Flyway 既支持SQL 腳本,又支持Java 代碼:可以使用SQL 腳本執行數據庫更新,也可以使用Java 代碼來進行一些高級數據升級操作。
Flyway 高可靠性:在集群環境下進行數據庫升級是安全可靠的。
Flyway 支持清除已存在的庫表結構:Flyway 可以清除已存在的庫表結構,可以從零開始搭建您的庫表結構,並管理您的數據庫版本升級工作
Flyway 支持失敗修復。新的2.0 版本提供了repair 功能,用於解決數據庫更新操作失敗問題。
Liquibase 自動升級,將任意版本的數據庫升級到最新版本。
Liquibase 可以根據數據庫的情況為你生成最後的遷移語句,同時因為數據庫變動首先是被Liquibase解析,所以也可以簡單支持回滾。
Liquibase 支持大部分常見的數據庫變動操作,比如建表,刪表,變動字段等等
Liquibase 可以在不使用SQL的情況下造成數據庫變動,其可讀性更高一些,特別是團隊並不直接使用SQL而整體相關知識儲備不完善的情況下優勢更明顯。
兩款數據庫遷移工具其實定位上是差別的。一般小項目整體變動不大的用Flyway,大應用和企業應用用Liquibase更合適。
二、spring boot + liquibase
註:spring boot + liquibase此方式僅僅是簡單的實現更新數據庫表結構,用於啟動項目時更新表結構。
1、gradle配置引入liquibase包:
dependencies {
compile group: ‘org.liquibase‘, name: ‘liquibase-core‘, version: ‘3.5.3‘
testCompile group: ‘junit‘, name: ‘junit‘, version: ‘4.12‘
}
2、修改application.yml或加LiquibaseConfig.java
2.1、application.yml
liquibase:
change-log: classpath:/db/changelog/master.xml
user: root
password: 1qaz2wsx
url: jdbc:mysql://127.0.0.1:3306/test_1.0.1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullNamePatternMatchesAll=true&useSSL=true
drop-first: false
2.2、LiquibaseConfig.java
@Configuration
public class LiquibaseConfig {
@Bean
public SpringLiquibase liquibase(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:/db/changelog/master.xml");
liquibase.setContexts("development,test,production");
liquibase.setShouldRun(true);
return liquibase;
}
}
3、新增changelog.xml
3.1、新增master.xml
在src/main.resouces下新增db/changelog/master.xml。
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.5.xsd">
<include file="classpath:/db/changelog/V1.1__init.sql" relativeToChangelogFile="false"/>
</databaseChangeLog>
3.2、新增V1.1__init.sql(以sql語句方式,也可以用xml、yml、json請查看官網)
在src/main.resouces/db/changelog下新增db/changelog/V1.1__init.sql。
V1.1__init.sql內容如下:
--liquibase formatted sql
--changeset whx:1.1
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for test_user_tab
-- ----------------------------
DROP TABLE IF EXISTS `test_user_tab`;
CREATE TABLE `test_user_tab` (
`userId` int(11) NOT NULL AUTO_INCREMENT,
`userAccount` varchar(16) NOT NULL,
`password` varchar(32) NOT NULL,
`userStatus` tinyint(1) NOT NULL DEFAULT ‘1‘,
`addTime` datetime NOT NULL,
PRIMARY KEY (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘測試用戶表‘;
--changeset whx:1.2
ALTER TABLE `test_user_tab`
DROP COLUMN `addTime`;
--rollback ALTER TABLE `test_user_tab` ADD COLUMN `addTime` datetime NOT NULL AFTER `userStatus`;
--changeset whx:1.3
ALTER TABLE `test_user_tab`
ADD COLUMN `addTime` datetime NOT NULL AFTER `userStatus`;
--rollback ALTER TABLE `test_user_tab` DROP COLUMN `addTime`;
--changeset whx:1.4
ALTER TABLE `test_user_tab`
DROP COLUMN `addTime`;
--rollback ALTER TABLE `test_user_tab` ADD COLUMN `addTime` datetime NOT NULL AFTER `userStatus`;
--changeset whx:1.5
ALTER TABLE `test_user_tab`
ADD COLUMN `addTime` datetime NOT NULL AFTER `userStatus`;
--rollback ALTER TABLE `test_user_tab` DROP COLUMN `addTime`;
啟動項目可以查看數據庫此時新增了三張表:
databasechangelog
databasechangeloglock
test_user_tab
三、gradle + liquibase插件
plugin地址:https://github.com/liquibase/liquibase-gradle-plugin
1、修改build.gradle文件
1.1、多模塊項目在根路徑修改build.gradle
註:此方式將使所有model都有liquibase插件,在執行命令可能因為文件重復導致失敗,不建議此方式。
group = ‘test‘
version = ‘0.0.1-SNAPSHOT‘
buildscript {
ext {
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "gradle.plugin.org.liquibase:liquibase-gradle-plugin:2.0.1"
}
}
subprojects {
apply plugin: ‘java‘
apply plugin: ‘eclipse‘
apply plugin: "org.liquibase.gradle"
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven {
url ‘http://maven.aliyun.com/nexus/content/groups/public/‘
}
}
dependencies {
liquibaseRuntime ‘org.liquibase:liquibase-core:3.5.3‘
liquibaseRuntime ‘org.liquibase:liquibase-groovy-dsl:2.0.1‘
liquibaseRuntime ‘mysql:mysql-connector-java:5.1.34‘
}
liquibase {
activities {
main {
changeLogFile "${this.rootDir}/user/src/main/resources/db/changelog/V1.1__init.sql"
url "jdbc:mysql://127.0.0.1:3306/test_1.0.1?useUnicode=true&characterEncoding=UTF-8"
username "root"
password "1qaz2wsx"
}
runList = ‘main‘
}
}
}
1.2、子模塊修改build.gradle
例如:core model的core.gradle文件,推薦此方式。
group ‘projectManage‘
version ‘1.0-SNAPSHOT‘
apply plugin: ‘java‘
sourceCompatibility = 1.8
buildscript {
repositories {
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath "org.liquibase:liquibase-gradle-plugin:2.0.1"
}
}
apply plugin: ‘org.liquibase.gradle‘
dependencies {
liquibaseRuntime ‘org.liquibase:liquibase-core:3.5.3‘
liquibaseRuntime ‘org.liquibase:liquibase-groovy-dsl:2.0.1‘
liquibaseRuntime ‘mysql:mysql-connector-java:5.1.34‘
compile project(":common")
testCompile group: ‘junit‘, name: ‘junit‘, version: ‘4.12‘
liquibase {
activities {
main {
changeLogFile "${this.rootDir}/core/src/main/resources/db/changelog/V1.1__init.sql"
//changeLogFile "${this.rootDir}/core/src/main/resources/db/changelog/changelog.mysql.sql"
url "jdbc:mysql://127.0.0.1:3306/yb_oa_xmgl_1.0.1?useUnicode=true&characterEncoding=UTF-8"
username "root"
password "1qaz2wsx"
}
runList = ‘main‘
}
}
}
說明:
0、註意mysql驅動版本
??liquibaseRuntime ‘mysql:mysql-connector-java:5.1.34‘
1、註意${this.rootDir}
??changeLogFile "${this.rootDir}/user/src/main/resources/db/changelog/V1.1__init.sql"
??如果不加${this.rootDir}可能會報Gradle Liquibase change log file could not be found
另一種寫法:
liquibase {
activities {
main {
changeLogFile ‘src/main/resources/db/dbchangelog-master.xml‘
url ‘jdbc:mysql://localhost:3306/test‘
username ‘XXX‘
password ‘XXX‘
classpath "$rootDir"
}
}
runList = ‘main‘
}
2、命令示例
0、插件命令 gradle command -PliquibaseCommandValue=<value> # -PliquibaseCommandValue=<value> 為非填。
1、gradle update -PrunList=main #執行命令,將按V1.1__init.sql中的sql語句更新數據庫。
2、gradle generateChangeLog #數據庫sql文件的標準輸出,執行於已存在表結構的數據庫將會得到表數據結構文件。
註:changeLogFile ‘src/main/resources/db/changelog.mysql.sql‘ ,changelog.xxx.sql允許不存在,執行時將自動創建。xxx代表的是數據庫。
例如:changelog.h2.sql,必須是此格式,否則將報錯。
3、gradle rollbackCount -PliquibaseCommandValue=1 # rollbackCount 回滾最後的<value>更改集
4、gradle dbDoc # 將在/項目路徑/.idea/dataSources下生成/xxx.xml文件,該文件是對數據庫表數據結構的描述。
3、命令詳解
liquibase官網:https://www.liquibase.org/documentation/command_line.html
註:插件命令 gradle command -PliquibaseCommandValue=<value>
以下操作均已/core/src/main/resources/db/changelog/V1.1__init.sql中的V1.1__init.sql為例子。
3.1、數據庫更新命令
命令 | 描述 |
---|---|
update | 更新數據庫到當前版本。 |
updateCount <value> | 更新數據庫到指定的value版本,即第幾個changeset。 例如:gradle updateCount -PliquibaseCommandValue=1 將只會執行V1.1__init.sql中--changeset whx:1.1,後續的changeset將不會執行。 若繼續執行value=5,則--changeset whx:1.1~1.5都將執行,且之前的操作並不會沖突。 |
updateSQL | 預執行sql(執行全部changeset),並不會更新數據庫,僅在控制臺輸出。 |
updateCountSQL <value> | 預執行sql到指定的value版本。 |
3.2、數據庫回滾命令
Liquibase有三種管理回滾的模式:
3.2.1、直接執行回滾
可以直接針對目標數據庫執行回滾命令。如果無法回滾任何更改,您將收到通知,並且不會回滾任何更改。
3.2.2、生成回滾腳本
可以生成回滾數據庫所需的SQL,而不是實際更新數據庫。如果要預覽在實際運行之前將執行的回滾命令,這將非常有用。
3.2.3、生成“未來回滾”腳本
此模式旨在允許在生成遷移腳本的同時生成回滾腳本。它允許獲取更新的應用程序並生成SQL以將數據庫更新為新版本以及SQL,以便在需要時將該新版本恢復到當前版本。當DBA想要控制進入數據庫的SQL時,以及需要內部和/或“SOX兼容”進程的回滾文檔的應用程序時,此功能非常有用。無需在此模式下指定回滾日期,標記或計數。
3.2.4 回滾命令
命令 | 描述 |
---|---|
rollback <tag> | 要回滾到那個tag。例如:gradle rollback -PliquibaseCommandValue=tag20190107。 需要註意的是在V1.1__init.sql中,必須有--rollback。 例如: --changeset whx:1.5 ALTER TABLE `test_user_tab` ADD COLUMN `addTime` datetime NOT NULL AFTER `userStatus`; --rollback ALTER TABLE `test_user_tab` DROP COLUMN `addTime`; 初始版本的sql數據表結構可以沒有--rollback,但是後續的--changeset建議都加上--rollback。 |
rollbackToDate <date/time> | 設置回滾的日期。日期格式要符合插件執行theDateFormat.getDateInstance()操作設置的日期格式。 |
rollbackCount <value> | value指定往前回滾幾個版本,例如:gradle rollbackCount -PliquibaseCommandValue=1 將會回滾--changeset whx:1.5的操作。 |
rollbackSQL <tag> | 根據tag,預執行rollback。並不會更新數據庫,僅在控制臺輸出。 |
rollbackToDateSQL <date/time> | 根據date/time,預執行rollback。 |
rollbackCountSQL <value> | 根據value,預執行rollback。 |
futureRollbackSQL | SQL以在應用更改日誌中的更改後將數據庫回滾到當前狀態。 |
updateTestingRollback | 更新數據庫,然後在更新之前回滾更改。 |
generateChangeLog | 數據庫sql文件的標準輸出,執行於已存在表結構的數據庫將會得到表數據結構文件。 註:changeLogFile ‘src/main/resources/db/changelog.mysql.sql‘, changelog.xxx.sql允許不存在,執行時將自動創建。xxx代表的是那種數據庫。 例如:changelog.h2.sql,必須是此格式,否則將報錯。 |
3.3、差異命令
diff命令用於比較數據庫之間的異同。
註:它目前不檢查:非外鍵約束(檢查等)、存儲過程、數據類型長度。
詳見 3.11、liquibase.activities詳解。
命令 | 描述 |
---|---|
diff [diff parameters] | 將差異描述寫入標準輸出。 例如:gradle diff -PrunList=diffMain。 |
diffChangeLog [diff parameters] | 寫入更改日誌XML以將基礎數據庫更新到目標數據庫以標準輸出,默認控制臺輸出。 例如:gradle diffChangeLog -PrunList=diffMain。 配置changeLogFile 則按規則輸出。 |
3.4、文檔命令
命令 | 描述 |
---|---|
dbDoc <outputDirectory> | 默認將在/項目路徑/.idea/dataSources下生成/xxx.xml文件,該文件是對數據庫表數據結構的描述。 outputDirectory指定輸出路徑,不同後綴文件名將生產不同格式的Doc。 例如 gradle dbDoc -PliquibaseCommandValue= D:\work_idea\yboa\pro_manage_dev\user\src\main\resources\db\changelog\123.sql 將生成html文件。 |
3.5、維護命令
命令 | 描述 |
---|---|
tag <tag> | "標記"當前數據庫狀態以供將來回滾。 例如:gradle tag -PliquibaseCommandValue=tag20190107 |
tagExists <tag> | 檢查給定標記是否已存在。 |
status | |
validate | 檢查更改日誌中的錯誤。 |
changelogSync | 將所有更改標記為在數據庫中執行。 |
changelogSyncSQL | SQL以將在數據庫中執行的所有更改標記為STDOUT。 |
markNextChangeSetRan | 將下一個更改集標記為在數據庫中執行。 |
listLocks | 列出當前鎖定數據庫更改日誌的人員。 |
releaseLocks | 釋放數據庫更改日誌上的所有鎖定。 |
dropAll | 刪除用戶擁有的所有數據庫對象。請註意,不刪除函數,過程和包(1.8.1中的限制)。 |
clearCheckSums | 從數據庫中刪除當前的校驗和。在下次運行時,將重新計算校驗和。 |
3.6、必需參數
詳見:build.gradle文件中的配置
命令 | 描述 |
---|---|
--changeLogFile=<path and filename> | 要使用的changelog文件。 |
--username=<value> | 數據庫用戶名 |
--password=<value> | 數據庫密碼。 |
--url=<value> | 數據庫JDBC URL。 |
--driver=<jdbc.driver.ClassName> | 數據庫驅動程序類名。 |
3.7、可選參數
命令 | 描述 |
---|---|
--classpath=<value> | 包含遷移文件和JDBC驅動程序的類路徑。 |
--contexts=<value> | ChangeSet上下文要執行。 |
--defaultSchemaName=<schema> | 指定用於托管數據庫對象和Liquibase控制表的默認架構。 |
--databaseClass=<custom.DatabaseImpl> | 指定要使用的自定義數據庫實現 |
--defaultsFile=</path/to/file> | 包含默認選項值的文件。(默認值:./ liquibase.properties) |
--includeSystemClasspath=<true or false> | 在Liquibase類路徑中包含系統類路徑。(默認值:true) |
--promptForNonLocalDatabase=<true or false> | 提示非本地主機數據庫。(默認值:false) |
--currentDateTimeFunction=<value> | 覆蓋SQL中使用的當前日期時間函數。適用於不受支持的數據庫。 |
--logLevel=<level> | 執行日誌級別((debug, info, warning, severe, off)。 |
--help | 輸出命令行參數幫助。 |
--exportDataDir | 將保留insert語句csv文件的目錄(generateChangeLog命令所需)。 |
--propertyProviderClass=<properties.ClassName> | 要使用的自定義Properties實現 |
3.8、必需的Diff參數
命令 | 描述 |
---|---|
--referenceUsername=<value> | 基礎數據庫用戶名 |
--referencePassword=<value> | 基礎數據庫密碼。 |
--referenceUrl=<value> | 基礎數據庫URL。 |
3.9、可選的Diff參數
命令 | 描述 |
---|---|
--referenceDriver=<jdbc.driver.ClassName> | 基礎數據庫驅動程序類名。 |
3.10、更改日誌屬性
命令 | 描述 |
---|---|
-D<property.name>=<property.value> | 傳遞名稱/值對以替換更改日誌中的$ {}塊。 |
3.11、liquibase.activities詳解
liquibase {
activities {
main {
changeLogFile "${this.rootDir}/core/src/main/resources/db/changelog/V1.1__init.sql"
//changeLogFile "${this.rootDir}/core/src/main/resources/db/changelog/changelog.mysql.sql"
url "jdbc:mysql://127.0.0.1:3306/yb_oa_xmgl_1.0.1?useUnicode=true&characterEncoding=UTF-8"
username "root"
password "1qaz2wsx"
//driver "" // 該參數可非必填,url將自動匹配驅動。
//exportDataDir // 將保留insert語句csv文件的目錄(generateChangeLog命令所需)。
}
security {
changeLogFile ‘src/main/db/security.groovy‘
url project.ext.securityUrl
username project.ext.securityUsername
password project.ext.securityPassword
}
/**比較數據庫之間的差異**/
diffMain {
//若不配置changeLogFile 則將在控制臺進行xml輸出。diff.mysql.sql會自動創建,必須以 *.databaseType.sql才會生成sql文件
changeLogFile "${this.rootDir}/core/src/main/resources/db/changelog/diff.mysql.sql"
url "jdbc:mysql://127.0.0.1:3306/yb_oa_xmgl_1.0.1?useUnicode=true&characterEncoding=UTF-8"
username "root"
password ‘1qaz2wsx‘
referenceUrl "jdbc:mysql://127.0.0.1:3306/yb_oa_xmgl_1.0.2?useUnicode=true&characterEncoding=UTF-8"
referenceUsername "root"
referencePassword ‘1qaz2wsx‘
}
runList = ‘main‘ # 不同環境可執行不同的main方法, 例如:runList = ‘diffMain‘
}
}
4、插件升級
升級Liquibase Gradle插件本身的版本
大多數時候,Liquibase的新版本與舊版本的版本相同,但有時新版本與現有的更改集存在兼容性問題,就像Liquibase 3發布時一樣。
發生這種情況時,建議執行以下升級程序:
1、確保所有Liquibase的管理數據庫是最新通過運行 gradle update它們升級到Liquibase插件的新版本之前。
2、創建一個新的丟棄數據庫來測試Liquibase更改集。gradle update使用最新版本的Liquibase插件在新數據庫上運行 。
這很重要,因為Groovy DSL中的項目已棄用,並且因為不同Liquibase版本生成SQL的方式存在一些細微差別。
例如,使用defaultValue: "0"Liquibase 2中的工作正常,在MySql中向布爾列添加默認值,但在Liquibase 3中,
它生成的SQL不適用於MySql - defaultValueNumeric: 0需要使用它。
3、一旦確定所有更改集都使用最新的Liquibase插件,請清除所有由Liquibase 2舊版本計算的校驗和,方法是gradle clearChecksums對所有數據庫運行。
4、最後,gradle changeLogSync在所有數據庫上運行以計算新的校驗和。
Liquibase-數據庫版本管理使用