Spring Boot使用Liquibase最佳實踐

Liquibase問題

隨着項目的發展,一個項目中的代碼量會很是龐大,同時數據庫表也會錯綜複雜。若是一個項目使用了Liquibase對數據庫結構進行管理,愈來愈多的問題會浮現出來。java

  1. ChangeSet文件同時多人在修改,本身的ChangeSet被改掉,甚至被刪除掉。
  2. 開發人員將ChangeSet添加到已經執行過的文件中,致使執行順序出問題。
  3. 開發人員擅自添加對業務數據的修改,其它環境沒法執行並報錯。
  4. ChangeSet中SQL包含schema名稱,致使其它環境schema名稱變化時,ChangeSet報錯。
  5. 開發人員不當心改動了已經執行過的ChangeSet,在啓動時會報錯。

Liquibase基本規範

  1. ChangeSet id使用[任務ID]-[日期]-[序號],如 T100-20181009-001
  2. ChangeSet必須填寫author
  3. Liquibase禁止對業務數據進行sql操做
  4. 使用<sql>時,禁止包含schema名稱
  5. Liquibase禁止使用存儲過程
  6. 全部表,列要加remarks進行註釋
  7. 已經執行過的ChangeSet嚴禁修改。
  8. 不要隨便升級項目liquibase版本,特別是大版本升級。不一樣版本ChangeSet MD5SUM的算法不同。

其它數據庫規範再也不贅述。算法

<?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">
    <changeSet id="T100-20181009-001" author="markfredchen" >
        <createTable tableName="demo_user" remarks="用戶表">
            <column name="id" type="bigint" remarks="用戶ID,主鍵">
                <constraints nullable="false" primaryKey="true" primaryKeyName="pk_demo_user_id"/>
            </column>
            <column name="username" type="varchar(100)" remarks="用戶名">
                <constraints nullable="false"/>
            </column>
            ...
        </createTable>
    </changeSet>
</databaseChangeLog>

有效文件管理

使用Liquibase中提供<include file="xxx"/>tag,能夠將ChangeSet分佈在不一樣文件中。同時<include/>支持多級引用。
基於此功能能夠對項目中的ChangeSet進行有效管理。推薦使用如下規範進行管理。spring

根據發佈進行管理

  1. 每一個發佈新建一個文件夾,全部發布相關的ChangeSet文件以及數據初始化文件,均放在些文件夾中。
  2. 每一個發佈新建一個master.xml。此master.xml中,include本次發佈須要執行的ChangeSet文件
  3. 根據開發小組獨立ChangeSet文件(可選)
  4. 根據功能獨立ChangeSet文件。例如user.xml, company.xml
resources
  |-liquibase
    |-user
    | |- master.xml
    | |- release.1.0.0
    | | |- release.xml
    | | |- user.xml -- 用戶相關表ChangeSet
    | | |- user.csv -- 用戶初始化數據
    | | |- company.xml -- 公司相關表ChangeSet
    | |- release.1.1.0
    | | |- release.xml
    | | |- ...

模塊化管理

當項目變得龐大以後,一個服務可能包含的功能模塊會愈來愈多。此時你們會想盡辦法進行模塊拆分,逐步進行微服務化。然而在面對錯綜複雜的Liquibase ChangeSet就會無從下手。
針對這種未來可能會面對的問題,項目初期就對Liquibase進行模塊化管理,將在將來帶來很大收益。
首先說明一下Spring Boot中Liquibase默認是如何執行以及執行結果。sql

  1. 在啓動時,LiquibaseAutoConfiguration會根據默認配置初始化SpringLiquibase
  2. SpringLiquibase.afterPropertiesSet()中執行ChangeSet文件
  3. 第一次跑ChangeSets的時候,會在數據庫中自動建立兩個表databasechangelogdatabasechangeloglock

所以咱們能夠認爲一個SpringLiquibase執行爲一個模塊。數據庫

引入多模塊管理時,基於上節文件管理規範,咱們基於模塊管理再作下調整。數據結構

resources
  |-liquibase
    |-user
    | |- master.xml
    | |- release.1.0.0
    | | |- release.xml
    | | |- user.xml -- 用戶相關表ChangeSet
    | | |- user.csv -- 用戶初始化數據
    | | |- company.xml -- 公司相關表ChangeSet
    | |- release.1.1.0
    | | |- release.xml
    | | |- ...
    |- order
    | |- master.xml
    | |- release.1.0.0
    | | |- ...

當有一天咱們須要把訂單模塊拆分紅獨立服務時,咱們只須要將模塊相關的ChangeSet文件遷出來。便可完成數據結構的拆分。app

那如何在一個Spring Boot運行多個SpringLiquibase呢?須要對代碼進行如下調整。模塊化

  1. 禁用Spring Boot自動運行Liquibase。

當如下配置被啓用時,Spring Boot AutoConfigure會使用默認配置初始化名爲springLiquibase的Bean。而後咱們不對其進行配置,Spring Boot啓動時會報錯。微服務

# application.properties
# spring boot 2以上
spring.liquibase.enabled=false
# spring boot 2如下
liquibase.enabled=false
  1. Spring Boot配置Liquibase Bean

配置兩個SpringLiquibase Bean,Bean名稱分別爲userLiquibase和orderLiqubase。ui

@Configuration
public class LiquibaseConfiguration() {

    /**
     *  用戶模塊Liquibase   
     */
    @Bean
    public SpringLiquibase userLiquibase(DataSource dataSource) {
        SpringLiquibase liquibase = new SpringLiquibase();
        // 用戶模塊Liquibase文件路徑
        liquibase.setChangeLog("classpath:liquibase/user/master.xml");
        liquibase.setDataSource(dataSource);
        liquibase.setShouldRun(true);
        liquibase.setResourceLoader(new DefaultResourceLoader());
        // 覆蓋Liquibase changelog表名
        liquibase.setDatabaseChangeLogTable("user_changelog_table");
        liquibase.setDatabaseChangeLogLockTable("user_changelog_lock_table");
        return liquibase;
    }
    /**
     *  訂單模塊Liquibase   
     */
    @Bean
    public SpringLiquibase orderLiquibase() {
      SpringLiquibase liquibase = new SpringLiquibase();
      liquibase.setChangeLog("classpath:liquibase/order/master.xml");
      liquibase.setDataSource(dataSource);
      liquibase.setShouldRun(true);
      liquibase.setResourceLoader(new DefaultResourceLoader());
      liquibase.setDatabaseChangeLogTable("order_changelog_table");
      liquibase.setDatabaseChangeLogLockTable("order_changelog_lock_table");
      return liquibase;
    }
}

Cheers~~

相關文章
相關標籤/搜索