Liquibase-資料庫腳本版本管理控制
- 2022 年 9 月 10 日
- 筆記
- db, DevOps, springboot
1. 簡介
Liquibase是一個用於跟蹤、管理和應用資料庫變化的開源的資料庫重構工具。它將所有資料庫的變化(包括結構和數據)都保存在XML文件中,便於版本控制。
Liquibase使參與應用程式發布過程的任何人都可以輕鬆地:
- 不依賴於特定的資料庫,Liquibase會自動適配目標資料庫進行腳本初始化,目前支援至少30種主流資料庫。
- 提供資料庫比較功能,比較結果保存在XML中,基於該XML可以用Liquibase輕鬆部署或升級資料庫。
- 以XML記錄/存儲資料庫變化,其中以
author
和id
唯一標識一個變化(ChangSet),支援資料庫變化的合併,因此支援多開發人員同時工作。 - 在資料庫中保存資料庫修改歷史(DatabaseChangeHistory),在資料庫升級時自動跳過已應用的變化(ChangSet)。
- 提供變化應用的回滾功能,可按時間、數量或標籤(tag)回滾已應用的變化。通過這種方式,開發人員可輕易的還原資料庫在任何時間點的狀態。
- 可生成資料庫修改文檔(HTML格式)
- 提供數據重構的獨立的IDE和Eclipse插件
- 將所有變化(包括結構和數據)存在XML文件中,便於版本控制的工具
springboot支援,只需要導入依賴。
application.yml配置(可選)
不配置,默認去resource/db/changelog下找db.changelog-mastert.yml文件
2. Quick Start
使用步驟
- step1: 創建一個資料庫變更日誌(change log)文件。
- step2: 在變更日誌文件內部創建一個變更集(change set)。
- step3: 通過命令行或構建腳本對資料庫進行變更集。
- step4: 檢驗資料庫中的變更
面向spring開發😉,通過springboot 整合 liquibase 了解其作用。
2.1 添加maven依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>4.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
2.2 application.yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
username: root
password: 123456
liquibase:
# 指定配置文件路徑
change-log: classpath:db/db.changelog-master.xml
# 覆蓋本地 ddl dml
drop-first: true
# 是否啟用
enabled: true
# 記錄版本日誌表
database-change-log-table: databasechangelog
# 記錄版本改變lock表
database-change-log-lock-table: databasechangeloglock
2.3 添加 liquibase xml
db.changelog-master.xml
liquibase 配置文件入口,主要用來引用其他的
changelog.xml
,如下配置文件中的include
,當然也可以使用includeAll
來引用整個目錄
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="//www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.liquibase.org/xml/ns/dbchangelog
//www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">
<include file="classpath:db/changelog/changelog-init-0.0.1.xml"/>
</databaseChangeLog>
changelog-init-0.0.1.xml
主要記錄了
ddl
的變化資訊,比如 如下配置文件中創建了兩個表role
和user
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="//www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="//www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="//www.liquibase.org/xml/ns/pro" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="//www.liquibase.org/xml/ns/dbchangelog-ext //www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd //www.liquibase.org/xml/ns/pro //www.liquibase.org/xml/ns/pro/liquibase-pro-4.6.xsd //www.liquibase.org/xml/ns/dbchangelog //www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">
<changeSet author="ludangxin" id="1662615627445-2">
<createTable tableName="role" remarks="角色資訊表">
<column name="name" remarks="角色名稱" type="VARCHAR(255)"/>
<column name="role_key" remarks="角色key" type="VARCHAR(255)"/>
</createTable>
</changeSet>
<changeSet author="ludangxin" id="1662615627445-3">
<createTable tableName="user" remarks="用戶資訊表">
<column name="id" type="INT" remarks="主鍵">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="username" type="VARCHAR(255)" remarks="用戶名稱"/>
<column name="password" type="VARCHAR(255)" remarks="密碼"/>
<column name="age" type="INT" remarks="性別"/>
<column name="sex" type="VARCHAR(255)" remarks="性別"/>
<column name="role" type="VARCHAR(255)" remarks="角色"/>
<column name="create_time" type="DATETIME" defaultValueComputed="NOW()" remarks="創建時間"/>
</createTable>
</changeSet>
</databaseChangeLog>
2.4 啟動測試
項目啟動完成後,查看資料庫如下圖,我們在changelog-init-0.0.1.xml
文件中定義的腳本初始化到了資料庫中
通過該demo快速的完成了springboot 集成 liquibase,且完成資料庫的初始化。
2.5 測試changeset版本控制
前提是我們將
application.yml
中的drop-first
置為false
,因為drop-first=true
相當於每次都重置資料庫
此時我們想在user表中新增一個create_by
欄位,便直接在之前的changeset中添加了欄位,如下圖所示,然後啟動項目看結果
啟動時控制台報錯資訊如下:
報錯資訊是我們直接修改了changeset後導致md5值與之前的不匹配(直接在之前的changeset中做了修改)
🤔 liquibase如何判斷 是同一changeset的?
author
和id
唯一標識一個變化(ChangSet)
🤔 liquibase是如何進行changeset版本控制的?
Liquibase
會對已經執行的changelog
的每一個changeSet
的內容進行md5
計算,生成的值是databasechanglog
表的MD5SUM
欄位。
當重新啟動Liquibase
時,會對每個changeSet
進行md5
值計算,與databasechanglog
表中的MD5SUM
欄位進行對比,如果不一致,說明changeSet
值已經被修改,無法啟動成功。
3. DDL操作
3.1 創建表
<changeSet author="ludangxin" id="20220908-1">
<createTable tableName="order" remarks="訂單資訊表">
<column name="id" remarks="主鍵" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="order_number" remarks="訂單編號" type="VARCHAR(255)"/>
<column name="user_id" remarks="用戶id" type="BIGINT"/>
</createTable>
</changeSet>
3.2 刪除表
<changeSet author="ludangxin" id="20220908-2">
<dropTable tableName="order"/>
</changeSet>
3.3 修改表
3.3.1 添加欄位
<changeSet author="ludangxin" id="20220908-3">
<addColumn tableName="order">
<column name="status" remarks="訂單狀態" type="INT" defaultValue="0"/>
</addColumn>
</changeSet>
3.3.2 刪除欄位
<changeSet author="ludangxin" id="20220908-4">
<dropColumn tableName="order" columnName="order_number"/>
</changeSet>
3.3.3 修改欄位
<changeSet author="ludangxin" id="20220908-5">
<renameColumn tableName="order" oldColumnName="status" newColumnName="state" columnDataType="VARCHAR(10)" remarks="訂單狀態"/>
</changeSet>
修改欄位類型 不建議使用 因為會把欄位的其他資訊搞丟,比如欄位注釋
<changeSet author="ludangxin" id="20220908-7">
<modifyDataType tableName="order" columnName="state" newDataType="INT" />
</changeSet>
4. DML操作
4.1 數據初始化
項目部署難免會有系統內置的數據,這時我們可以通過使用liquibase進行初始化
新建csv文件user-init-0.0.1.csv
"id","username","password","age","sex","role","create_time","create_by"
"111","張三","222","23","1","admin","2022-09-08 14:22:33","system"
"112","李四","333","26","1","admin","2022-09-08 14:22:33","system"
"113","王五","444","25","1","admin","2022-09-08 14:22:33","system"
使用loadData
標籤進行數據的初始化
<changeSet author="ludangxin" id="20220908-8">
<loadData tableName="user" file="data/user-init-0.0.1.csv"
separator=","
encoding="UTF-8"
relativeToChangelogFile="true"/>
</changeSet>
4.2 新增數據
<changeSet author="ludangxin" id="20220908-6">
<insert tableName="order">
<column name="id" valueNumeric="666"/>
<column name="user_id" value="2222"/>
<column name="state" value="2"/>
</insert>
<insert tableName="order">
<column name="id" valueNumeric="888"/>
<column name="user_id" value="3333"/>
<column name="state" value="1"/>
</insert>
</changeSet>
4.3 初始化總是變動的數據
使用上述的loadData
標籤載入數據,當數據發生變化時,直接修改csv文件進行發布時,會報錯版本不一致。
這時可以使用loadUpdateData
標籤進行處理,注意的是changeset
上需要加參數runOnChange="true"
(當數據發生改變時不去校驗md5)如下
<changeSet author="ludangxin" id="20220908-9" runOnChange="true">
<loadUpdateData tableName="user" file="data/user-init-0.0.1.csv"
primaryKey="id"
separator=","
encoding="UTF-8"
relativeToChangelogFile="true"/>
</changeSet>
5. 集成maven
使用maven集成liquibase可以方便的通過liquibase maven plaugin實現很多功能。例如:腳本運行,生成文檔,資料庫差異比較等
5.1 添加 properties
添加properties 文件用來配置liquibase plugin的配置資訊。例如 資料庫的鏈接資訊,配置文件路徑的配置等
liquibase.properties
-- 資料庫連接資訊
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
username=root
password=123456
-- liquibse系統表 表名稱配置
databaseChangeLogTableName=databasechangelog
databaseChangeLogLockTableName=databasechangeloglock
-- 輸出文件路徑配置
outputChangeLogFile=src/main/resources/db/changelog/changelog-output-0.0.1.xml
-- liuquibase xml文件路徑指定
changeLogFile=src/main/resources/db/db.changelog-master.xml
5.2 修改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="//maven.apache.org/POM/4.0.0"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>liquibase-demo2</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<liquibase.version>4.8.0</liquibase.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>${liquibase.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>${liquibase.version}</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<configuration>
<!--properties文件路徑,該文件記錄了資料庫連接資訊等-->
<propertyFile>src/main/resources/db/liquibase.properties</propertyFile>
<propertyFileWillOverride>true</propertyFileWillOverride>
</configuration>
</plugin>
</plugins>
</build>
</project>
6. plugin-逆向生成xml
通過liquibase maven 插件,從已有的資料庫生成xml配置資訊
通過idea的maven功能,找到 liquibase plugin,雙擊如圖liquibase:generateChangeLog
選項,執行完成之後就會在properties
文件中配置的outputChangeLogFile
路徑生成對應的xml文件,如下圖所示
7. plugin-生成資料庫修改文檔
雙擊liquibase plugin面板中的liquibase:dbDoc
選項,會生成資料庫修改文檔,默認會生成到target
目錄中,如下圖所示
訪問index.html
會展示如下頁面,簡直應有盡有
8. plugin-發布changelog
之前我們對changelog的編輯都需要通過啟動項目來運行changelog,有時候我們可能想不重啟項目便能將修改發布運行到資料庫中
雙擊liquibase plugin面板中的liquibase:update
選項,便可以將修改同步到資料庫中
註:這裡有個bug(也可能不是bug,我目前還沒找到對應的解決辦法,如果您有解決方案,🙏),通過plugin
發布changelog
時,由於我們在db.changelog-master
中include
的是classpath
路徑,但是通過plugin
發布時會報錯找不到include
的xml文件,此時我們可以通過設置相對路徑來解決這個問題,比如<include file="classpath:changelog/changelog-init-0.0.1.xml" relativeToChangelogFile="true"/>
,然而這又引發了另外一個問題,plugin
和直接啟動springboot項目生成的databasechangelog表中的filename欄位值
不同,導致運行changelog
時報錯,因為liquibase默認會比較同一filename
下的changeset
9. plugin-比較資料庫差異
首先使用liquibase diff 功能前,我們在properties中加入參考的資料庫 test3
配置資訊
用於差異比較的資料庫,以此資料庫為準,生成diff xml
# 對比參考的資料庫資訊 (用於plugin-diff)
referenceUrl=jdbc:mysql://localhost:3306/test3?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
referenceDriver=com.mysql.cj.jdbc.Driver
referenceUsername=root
referencePassword=123456
# 生成的差異比較xml的輸出路徑
diffChangeLogFile=src/main/resources/db/changelog/changelog-diff-0.0.1.xml
我們先觀察下兩個資料庫有什麼樣的差異,再驗證生成的diff xml
兩個資料庫如下,差異都用紅色框起來了。
然後再用liquibase插件看下生成的 diff xml資訊
雙擊liquibase plugin面板中的liquibase:diff
選項,如下圖所示
執行完畢後,查看diff xml 內容如下:
生成的xml文件符合預期,將與test3資料庫的差異都生成到了diff xml中