使用Liquibase和Spring Boot進行資料庫遷移的一站式指南 - reflectoring

banq發表於2020-05-05

Liquibase有助於自動化資料庫遷移,而Spring Boot使使用Liquibase更加容易。該指南提供了有關如何在Spring Boot應用程式中使用Liquibase的詳細資訊以及一些最佳實踐。

本文隨附GitHub上的工作程式碼示例。

資料庫遷移工具可幫助我們跟蹤,版本控制和自動執行資料庫架構更改。它們幫助我們在不同環境中擁有一致的架構。

Liquibase簡介

Liquibase不僅使用簡單的舊SQL指令碼,而且還使用不同的抽象的,與資料庫無關的格式(包括XML,YAML和JSON)來促進資料庫遷移。當我們使用非SQL格式進行資料庫遷移時,Liquibase會為我們生成特定於資料庫的SQL。它照顧不同資料庫的資料型別和SQL語法的變化。它支援大多數流行的關聯式資料庫

Liquibase允許通過Liquibase擴充套件對其當前支援的資料庫進行增強。這些擴充套件也可用於新增對其他資料庫的支援。

讓我們看一下Liquibase的核心概念:

  • ChangeSet:changeSet是一組需要應用於資料庫的更改。Liquibase在ChangeSet級別跟蹤更改的執行。
  • Change變更:變更描述了需要應用於資料庫的單個變更。Liquibase開箱即用提供了幾種更改型別,例如“建立表”或“刪除列”,它們都是對SQL的抽象。
  • Changelog變更日誌:具有需要應用的資料庫變更集列表的檔案稱為變更日誌。這些變更日誌檔案可以採用SQL,YAML,XML或JSON格式。
  • Preconditions前提條件: 前提條件用於控制變更日誌或變更集的執行。它們用於定義需要在其下執行changeSets或change日誌的資料庫狀態。
  • Context上下文:changeSet可以用上下文表示式標記。在給定特定上下文的情況下,Liquibase將評估此表示式以確定是否應在執行時執行changeSet。您可以將上下文表示式與環境變數進行比較。
  • Labels標籤:標籤目標類似上下文。區別在於changeSet用標籤(而不是表示式)列表標記,並且在執行時,我們可以傳遞標籤表示式來選擇與表示式匹配的changeSet。
  • Changelog引數:Liquibase允許我們在changelog中使用佔位符,它在執行時會動態替換。

Liquibase會建立兩個表,databasechangelog 並databasechangeloglock在第一次在資料庫中執行時建立。它使用該databasechangelog表來跟蹤databasechangeloglockchangeSets 的執行狀態,並防止Liquibase併發執行。有關更多詳細資訊,請參考文件

Spring Boot的Liquibase

預設情況下,當我們將Liquibase依賴項新增到構建檔案中時,Spring Boot會自動配置Liquibase 。

Spring Boot使用主資料庫DataSource執行Liquibase.如果我們需要使用其他名稱DataSource,可以將該bean標記為@LiquibaseDataSource。

另外,我們可以設定spring.liquibase.[url,user,password]屬性,以便spring自己建立一個資料來源,並使用它來自動配置Liquibase。

預設情況下,Spring Boot在應用程式啟動時自動執行Liquibase資料庫遷移。

在類路徑中名為db/migration的資料夾中查詢主變更日誌檔案db.changelog-master.yaml。如果要使用其他Liquibase變更日誌格式或使用不同的檔案命名約定,則可以將spring.liquibase.change-log應用程式屬性配置為指向其他主變更日誌檔案。

例如,要用db/migration/my-master-change-log.json作主變更日誌檔案,我們在中設定以下屬性application.yml:

spring:
  liquibase:
    changeLog: "classpath:db/migration/my-master-change-log.json"

主變更日誌可以包括其他變更日誌,以便我們可以按邏輯步驟將變更拆分。

執行我們的第一個資料庫遷移

設定完所有內容後,讓我們建立我們的第一個資料庫遷移。在本示例中,我們將建立資料庫表user_details。

讓我們建立一個具有名稱的檔案db.changelog-master.yaml並將其放置在中src/main/resources/db/changelog:

databaseChangeLog:
  - include:
      file: db/changelog/db.changelog-yaml-example.yaml

主檔案只是一個包含集合的集合,這些集合指向具有實際更改的更改日誌。

接下來,我們使用第一個實際的變更集建立變更日誌,並將其放入檔案中src/main/resources/db/changelog-yaml-example.yaml:

databaseChangeLog:
  - changeSet:
      id: create-table-user
      author: liquibase-demo-service
      preConditions:
        - onFail: MARK_RAN
          not:
            tableExists:
              tableName: user_details
      changes:
        - createTable:
            columns:
              - column:
                  autoIncrement: true
                  constraints:
                    nullable: false
                    primaryKey: true
                    primaryKeyName: user_pkey
                  name: id
                  type: BIGINT
              - column:
                  constraints:
                    nullable: false
                  name: username
                  type: VARCHAR(250)
              - column:
                  constraints:
                    nullable: false
                  name: first_name
                  type: VARCHAR(250)
              - column:
                  name: last_name
                  type: VARCHAR(250)
            tableName: user_details

我們使用了changeType createTable,它抽象了表的建立。Liquibase將基於我們的應用程式使用的資料庫將上述changeSet轉換為適當的SQL。

在執行此更改之前,preCondition檢查user_details表是否不存在。如果該表已經存在,則Liquibase會將changeSet標記為已成功執行而不實際執行。

現在,當我們執行Spring Boot應用程式時,Liquibase執行changeSet,該表將建立user_details表,user_pkey作為主鍵。

使用變更日誌引數

當我們要為不同的環境使用不同的值替換佔位符時,Changelog引數非常有用。我們可以使用application屬性設定這些引數spring.liquibase.parameters,該屬性採用鍵/值對的對映:

spring:
  profiles: docker
  liquibase:
    parameters:
      textColumnType: TEXT
    contexts: local
---
spring:
  profiles: h2
  liquibase:
    parameters:
      textColumnType: VARCHAR(250)
    contexts: local    

我們設定Liquibase引數textColumnType來VARCHAR(250)時春啟動的啟動h2配置檔案,並TEXT當它在啟動docker配置檔案(假定泊塢窗型材啟動一個“真正”的資料庫)。

現在,我們可以在變更日誌中使用此引數:

databaseChangeLog:
  - changeSet:
     ...
      changes:
        - createTable:
            columns:
             ...
              - column:
                  constraints:
                    nullable: false
                  name: username
                  type: ${textColumnType}

現在,當Spring Boot應用程式在docker配置檔案中執行時,它將TEXT用作列型別,並在h2配置檔案中使用VARCHAR(250)。

使用Liquibase上下文

上下文可用於控制應執行哪些changeSet。讓我們用它在test和local環境中新增測試資料:

<databaseChangeLog>
  <changeSet 
    author="liquibase-docs" 
    id="loadUpdateData-example" 
    context="test or local">
    <loadUpdateData
      encoding="UTF-8"
      file="db/data/users.csv"
      onlyUpdate="false"
      primaryKey="id"
      quotchar="'"
      separator=","
      tableName="user_details">
    </loadUpdateData>
  </changeSet>
</databaseChangeLog>

我們正在使用該表示式test or local,它可以在這些上下文中執行,但不適用於生產環境。

現在,我們需要使用屬性將上下文傳遞給Liquibase spring.liquibase.contexts:

---
spring:
  profiles: docker
  liquibase:
    parameters:
      textColumnType: TEXT
    contexts: test

在Spring Boot中配置Liquibase

下面是Spring Boot提供的用於配置Liquibase行為的所有屬性的列表。

  •  
  • spring.liquibase.changeLog    主變更日誌配置路徑。預設為classpath:/db/changelog/db.changelog-master.yaml,
  • spring.liquibase.contexts    要使用的執行時上下文的逗號分隔列表。
  • spring.liquibase.defaultSchema    用於託管資料庫物件和Liquibase控制表的模式。
  • spring.liquibase.liquibaseSchema    Liquibase控制表的架構。
  • spring.liquibase.liquibaseTablespace    用於Liquibase物件的表空間。
  • spring.liquibase.databaseChangeLogTable    指定用於跟蹤更改歷史記錄的其他表。預設值為DATABASECHANGELOG。
  • spring.liquibase.databaseChangeLogLockTable    指定用於跟蹤併發Liquibase使用情況的其他表。預設值為DATABASECHANGELOGLOCK。
  • spring.liquibase.dropFirst    指示是否在執行遷移之前刪除資料庫架構。請勿在生產中使用!預設值為false。
  • spring.liquibase.user    登入使用者名稱以連線到資料庫。
  • spring.liquibase.password    登入密碼以連線到資料庫。
  • spring.liquibase.url    要遷移的資料庫的JDBC URL。如果未設定,則使用主要配置的資料來源。
  • spring.liquibase.labels    執行liquibase時要使用的標籤表示式。
  • spring.liquibase.parameters    引數對映將傳遞給Liquibase。
  • spring.liquibase.rollbackFile    執行更新時將回滾SQL寫入的檔案。
  • spring.liquibase.testRollbackOnUpdate    在執行更新之前是否應該測試回滾。預設值為false。

在Spring Boot中啟用Liquibase的日誌記錄

INFO為Liquibase 啟用級別日誌記錄將有助於檢視Liquibase在應用程式啟動期間執行的changeSet。它還有助於識別應用程式尚未啟動,因為它在啟動過程中正在等待獲取變更日誌鎖。

在其中新增以下應用程式屬性application.yml以啟用INFO日誌:

logging:
  level:
    "liquibase" : info

使用Liquibase的最佳實踐

  • 組織變更日誌:建立一個主變更日誌檔案,該檔案沒有實際的變更集,但包含其他變更日誌(僅YAML,JSON和XML支援(包括include),而SQL不包含)。這樣做使我們可以將changeSets組織在不同的changelog檔案中。每次我們向需要資料庫更改的應用程式新增新功能時,我們都可以建立一個新的變更日誌檔案,將其新增到版本控制中,並將其包含在主變更日誌中。
  • 每個ChangeSet只能進行一次更改:每個changeSet只能進行一次更改,因為在應用changeSet失敗的情況下,這可以使回滾更加容易。
  • 不要修改ChangeSet:一旦更改集執行完畢,就不要再對其進行修改。相反,如果需要修改現有changeSet所應用的更改,則新增新的changeSet。Liquibase跟蹤已執行的changeSet的校驗和。如果修改了已經執行的changeSet,預設情況下,Liquibase將無法再次執行該changeSet,並且不會繼續執行其他changeSet。
  • ChangeSet Id:Liquibase允許我們為changeSets使用描述性名稱。最好使用唯一的描述性名稱作為changeSetId,而不要使用序列號。它們使多個開發人員可以新增不同的changeSet,而不必擔心他們需要為changeSetId選擇的下一個序列號。
  • 參考資料管理:使用Liquibase來填充應用程式所需的參考資料和程式碼表。這樣做可以一起部署所需的應用程式和配置資料。Liquibase提供了changeType loadUpdateData支援此功能。
  • 使用前提條件:具有changeSets的前提條件。他們確保Liquibase在應用更改之前檢查資料庫狀態。
  • 測試遷移:在將其應用於實際的非生產或生產環境之前,請確保始終測試本地編寫的遷移。始終使用Liquibase在非生產或生產環境中執行資料庫遷移,而不是手動執行資料庫更改。

在Spring Boot應用程式啟動期間自動執行Liquibase,可以輕鬆地將應用程式程式碼更改和資料庫更改一起提供。但是,在向具有大量資料的現有資料庫表新增索引的情況下,應用程式可能需要更長的時間才能啟動。一種選擇是預釋出資料庫遷移(在需要它的程式碼之前釋放資料庫更改)並非同步執行它們

其他執行Liquibase的方法

Liquibase除了支援Spring Boot整合外,還支援一系列其他選項來執行資料庫遷移:

Liquibase有一個Java API,我們可以在任何基於Java的應用程式中使用它來執行資料庫遷移。

相關文章