使用 Liquibase 管理資料庫版本 - SpringBoot 2.7 .2 實戰基礎

来源:https://www.cnblogs.com/youyacoder/archive/2022/08/10/16572856.html
-Advertisement-
Play Games

Liquibase 具有執行鎖,已經執行過的內容不會重覆執行。在執行 changeSet 時,由於改動的內容可以通過 Liquibase 提供的標簽編寫,所以無關具體的資料庫產品(MySQL、Oracle 等),Liquibase 底層會根據實際使用的資料庫類型轉化為對應的 SQL。 ...


優雅哥 SpringBoot 2.7 .2 實戰基礎 - 05 -使用 Liquibase 管理資料庫版本

在企業開發中,資料庫版本管理好像是一個偽命題,大多項目都是通過 Power Designer 之類的工具建模、生成 SQL 語句,然後去資料庫中執行。在開發過程中如果遇到修改表結構,再補充修改表結構的語句,大家依次去執行,在本地及各個環境中同步表結構。但這種模式,在我參與過的項目中或多或少都出現過問題:忘記同步表結構,導致在服務啟動或運行時出錯。

1 Liquibase 介紹

SpringBoot 官方文檔中推薦了兩款工具來管理資料庫版本:FlywayLiquibase。前者我沒有在項目中使用過,所以本文就只討論 Liquibase。

使用 Liquibase 需要定義一堆 XML 文件,這些 XML 稱為 changelog 文件。每個 changelog 文件中又包含多個變化集合 changeSet,每個 changeSet 記錄了作者、改變的內容。changeSet 中要修改的內容,通過 createTableaddColumn 等標簽進行操作。通過這種 XML 文件的方式,就可以將代碼版本與資料庫版本關聯在一起。項目啟動,會自動執行 changelog XML 文件。Liquibase 具有執行鎖,已經執行過的內容不會重覆執行。在執行 changeSet 時,由於改動的內容可以通過 Liquibase 提供的標簽編寫,所以無關具體的資料庫產品(MySQL、Oracle 等),Liquibase 底層會根據實際使用的資料庫類型轉化為對應的 SQL。

通過上面的描述,可以看出 Liquibase 帶來的幾個好處:

  1. 支持多類型的資料庫產品,無需維護 SQL 腳本;
  2. 項目啟動可以自動升級資料庫;
  3. 代碼版本與資料庫版本關聯在一起。

2 在老項目中使用 Liquibase

在咱們的 demo hero-springboot-demo 中,之前已經手動通過 SQL 語句創建了資料庫表 computer,現在想通過 Liquibase 來管理資料庫版本和維護表結構,該怎麼辦呢?本節就通過這個案例來說明已存在的老項目中如何引用 Liquibase。

2.1 配置 Maven 插件

Liquibase 提供了 Maven 插件,使用該插件可以根據資料庫逆向生成 changlog 文件。在 pom.xml 的 plugins 下添加 Liquibase 插件:

<plugin>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-maven-plugin</artifactId>
    <version>4.9.1</version>
    <configuration>
        <propertyFileWillOverride>true</propertyFileWillOverride>
        <outputChangeLogFile>temp/temp-changelog.xml</outputChangeLogFile>
        <driver>com.mysql.cj.jdbc.Driver</driver>
        <url>jdbc:mysql://127.0.0.1:3306/hero_springboot_demo?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true</url>
        <username>root</username>
        <password>Mysql.123</password>
        <outputFileEncoding>UTF-8</outputFileEncoding>
        <verbose>true</verbose>
        <diffTypes>tables, views, columns, indexs,foreignkeys, primarykeys, uniqueconstraints, data</diffTypes>
    </configuration>
</plugin>

上面配置 Liquibase 的 Maven 插件:資料庫連接信息和Liquibase生成規則配置。生成的文件路徑為 temp 目錄下的 temp-changelog.xml,Liquibase 不會自動生成文件夾,需要手動在項目根目錄下創建 temp 目錄。

2.2 逆向生成 changelog

在控制台執行 mvn liquibase:generateChangeLog 或者在界面上執行 generateChangeLog:

image-20220801160733941

執行後查看 temp/temp-changelog.xml 文件:

image-20220801163132937

生成的這個文件就是 changelog 文件,該文件中包括兩個 changeSet:第一個是創建表結構;第二個初始化數據。

在第一個 changeSet 的 createTable 中,對 id 欄位配置了自增屬性 autoIncrement:

<column autoIncrement="true" name="id" type="BIGINT">
    <constraints nullable="false" primaryKey="true"/>
</column>

不知道為啥,我這不生效。要使該欄位自增,要用 addAutoIncrement 標簽。第一個 changeSet 需要添加上該標簽,添加後如下:

<changeSet id="T100-20220801-yyg-001" author="yyg">
    <createTable remarks="電腦" tableName="computer">
        <column autoIncrement="true" name="id" type="BIGINT">
            <constraints nullable="false" primaryKey="true"/>
        </column>
        <column name="size" remarks="尺寸" type="DECIMAL(4, 1)"/>
        <column name="operation" remarks="操作系統" type="VARCHAR(32)"/>
        <column name="year" remarks="年份" type="VARCHAR(4)"/>
    </createTable>
    <addAutoIncrement tableName="computer" columnName="id" columnDataType="BIGINT"/>
</changeSet>

獲取到歷史表結構及數據的 changelog 文件後,接下來的步驟與 SpringBoot 整合 Liquibase 一致。

3 在 SpringBoot 中使用 Liquibase

3.1 添加依賴

pom.xml 文件中添加 liquibase 依賴 liquibase-core,該依賴版本號在 spring-boot-dependencies中已定義,直接添加依賴即可。

<dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
</dependency>

3.2 添加 changelog

src/main/resources 創建目錄 dbdb 目錄用來存放 Liquibase 相關的 changelog 文件。

db 目錄下還可以按模塊創建其他目錄,由於我們這裡只有一個 computer 類,屬於 demo 演示,故就在 db 目錄下創建子目錄 demodb/demo/ 目錄就存放 demo 演示的所有 changelog 配置文件。將前面 Liquibase 逆向生成的 temp-changelog.xml 文件移動到 db/demo/ 目錄下,並重命名為 demo-changelog-v1.xml

db 目錄下創建 changelog-master.xml 文件,該文件為主配置文件,作用就是引入所有模塊的 changelog 文件:

<?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
                        https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">

    <includeAll path="demo" relativeToChangelogFile="true"/>
</databaseChangeLog>

除了根節點,裡面就一句話,表示將當前路徑下 demo 目錄中的 changelog 文件都引入。假設 db 目錄下還有其他模塊(目錄),繼續通過 <includeAll> 元素引入即可。

src/main/resources 中文件目錄結構如下:

src/main/resources/
|- db/
	|- demo/
		|- demo-changelog-v1.xml
	|- changelog-master.xml

3.3 changeSet

回過頭來看 demo-changelog-v1.xml 文件,裡面的內容是之前生成的,前面講過包括兩個 changeSetchangeSet 用來定義對資料庫的表更操作,包括:

表結構的操作:創建表、刪除表、修改表結構(添加列、刪除列等)

表數據的操作:表中數據的增刪改查

視圖的操作、索引的操作等

幾乎只要是對資料庫的操作,都可以寫在 changeSet 中。當服務啟動的時候,會自動執行主配置文件中包含的所有 changeSet。需要特別註意:changeSet 一經執行,就不能修改!! 雖然 changeSet 元素有一個屬性 runOnChange ,非常不建議亂用。如果要修改 changeSet裡面的內容怎麼辦呢?重新寫一個 changeSet,在裡面編寫要修改的內容。例如在第一個 changeSet 中使用 <createTable> 創建表,表中有一個列名為 field,在該 changeSet 執行後(成功啟動過服務),想將該列列名 field 修改為 f,這時候不能直接修改第一個 changeSet,而是要寫第二個 changeSet,通過 <renameColumn> 來修改列名,然後重啟服務。

changeSet元素有兩個必填的屬性 authoridauthor 表示作者,當前的是誰定義的這個changeSet,就填誰的名字,這樣便於追溯。 id 要求唯一,我在項目開發中,id一般按照這個規則:[任務ID]-[日期]-[作者]-[序號],如 T100-20220801-yyg-001

通常小版本更新(如欄位級別的變更),就在同一個文件中追加新的 changeSet 即可。一個 changelog 文件可以包括多個 changeSet,每個 changeSet 中可以包括多個語句,如多個 createTable、insert 等。

大版本更新,就重新編寫 changeLog 文件,如 demo-changelog-v2.xml

按照上面所講,我們修改一下 demo-changelog-v1.xml 中的兩個 changeSet 的 id 和 author:

<?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
                    https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
    <!--
    1. id使用[任務ID]-[日期]-[作者]-[序號],如 T100-20220801-yyg-001
    2. 必須填寫author
    3. 所有 表、列 必須加remarks進行註釋
    4. 已經執行過的ChangeSet嚴禁修改
    -->
    <changeSet id="T100-20220801-yyg-001" author="yyg">
        <createTable remarks="電腦" tableName="computer">
				...
				</createTable>
    </changeSet>
    <changeSet id="T100-20220801-yyg-002" author="yyg">
        ...
    </changeSet>
</databaseChangeLog>

3.4 添加配置

在 application.yml 中添加 liquibase 的配置:

spring:
  liquibase:
    enabled: true
    drop-first: false
    change-log: classpath:/db/changelog-master.xml

上述配置開啟 liquibase,並指定主文件的路徑。

3.5 啟動服務測試

現在啟動服務,會發現服務啟動失敗,而且控制台會出現如下提示:

Reason: liquibase.exception.DatabaseException: Table 'computer' already exists

此時需要刪除資料庫中的表,然後重新啟動服務。

服務啟動成功後,會自動創建兩張 liquibase 相關的表:DATABASECHANGELOGDATABASECHANGELOGLOCK表。

現在嘗試在 demo-changelog-v1.xml中編寫第三個 changeSet:

<changeSet id="T100-20220801-yyg-003" author="yyg">
    <addColumn tableName="computer">
        <column name="color" type="VARCHAR(8)" defaultValue="red" remarks="顏色"/>
    </addColumn>
</changeSet>

這個 changeSet 為 computer 表新增列 color,預設值為 red。重啟服務,服務啟動後查看 computer 表:

  1. 已新增列 color
  2. color 預設值已設置為 red

image-20220801225227371

這樣便成功在 SpringBoot 中使用 liquibase。

4 常見問題

4.1 Waiting for changelog lock

如果啟動服務時,控制台提示如下信息:

Liquibase - Waiting for changelog lock
Waiting for changelog lock....

通常是由於 Liquibase 在重構資料庫時使資料庫死鎖。解決方法如下:

1 查看鎖住資料庫的id:

SELECT * FROM DATABASECHANGELOGLOCK where LOCKED = true;

2 解鎖:

UPDATE DATABASECHANGELOGLOCK
SET locked=0, lockgranted=null, lockedby=null
WHERE id={id}

{id} 為第一步中查詢出來對應記錄的id。

4.2 主鍵自增無效

前面已經談到,需要配置 addAutoIncrement 標簽

<addAutoIncrement tableName="xxx" columnName="xx" columnDataType="BIGINT"/>

4.3 預設值無效

與主鍵自增無效類似,為 column 設置預設值 defaultValuedefaultValueNumeric也無效。

設置預設值需要使用標簽 addDefaultValue

<addDefaultValue tableName="xxx" columnName="xx" defaultValueNumeric="0"/>

Liquibase 還有很多強大的功能,就留給大家在使用過程中一步一步探索吧。

image

今日程式員優雅哥(/ youyacoder;[email protected])學習到此結束~~~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 面向對象編程(高級) 筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 類變數和類方法(static) 類變數 類變數-提出問題 提出問題的主要目的就是讓大家思考解決之道,從而引出我要講的知識點.說:有一群小孩在玩堆雪人,不時有新的小 ...
  • Java常用類(一) 一、String 類:(不可變的字元序列) 1.1 String:字元串,使用一對 " " 引起來表示。 String 類聲明為 final 的,不可被繼承。 String 類實現了 Serializable 介面:表示字元串是支持序列化的。實現了 Comparable 介面: ...
  • 哈嘍兄弟們,又是新的一天!今天你敲代碼了嗎? 一、序言 為什麼要挑戰自己在代碼里不寫 for loop?因為這樣可以迫使你去學習使用比較高級、比較地道的語法或 library。文中以 python 為例子,講了不少大家其實在別人的代碼里都見過、但自己很少用的語法。 自從我開始探索 Python 中驚 ...
  • (防扒小助手) 本人CSDN博客: https://blog.csdn.net/m0_61753302 本人博客園博客(同步CSDN): 何以牽塵 - 博客園 (cnblogs.com)https://www.cnblogs.com/kalesky/ 如果對你有用的話歡迎點贊關註喲! ​​​​​​​ ...
  • 一、前言 ReentrantLock 是基於 AQS 實現的同步框架,關於 AQS 的源碼在 這篇文章 已經講解過,ReentrantLock 的主要實現都依賴AQS,因此在閱讀本文前應該先瞭解 AQS 機制。本文並不關註 ReentrantLock 如何使用,只敘述其具體實現。 二、Reentra ...
  • Java集合01 1.什麼是集合? 前面我們保存數據使用的是數組,數組有不足的地方,我們來分析一下: 長度開始時必須指定,而且一但指定不能更改 保存的必須是同一類型的元素 使用數組進行增加/刪除元素的很麻煩 重新創建一個數組,將舊數組的元素拷貝過來 集合的好處: 可以動態地保存任意多個對象,使用比較 ...
  • 在生產環境中,對發在的API增加授權保護是非常必要的。JWT作為一個無狀態的授權校撿技術,非常適合於分散式系統架構。伺服器端不需要保存用戶狀態,因此,無須採用Redis等技術來實現各個服務節點之間共用Session數據。 本節通過實例講解如何用JWT技術進行授權認證和保護。 1.1 配置安全類 (1 ...
  • Tabby Tabby 是一名老外在 Github 開源的終端連接的工具,至今已經累積 20K+ star。 Tabby 的功能特性大概有: 支持多平臺,Windows、MacOS(Intel 晶元/M1 晶元)、Linux 都有對應的安裝包的; 自帶 SFTP 功能,能夠與 Linux 系統傳輸文 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...