Liquibase 和 Flyway 是两款成熟的、优秀的、开源/商业版的数据库版本管理工具,鉴于 Flyway 的社区版本对 Oracle 数据库支持存在限制,所以 boot-admin 选择整合 Liquibase 提供数据库版本管理能力支持。
Liquibase 开源版使用 Apache 2.0 协议。

Liquibase的适用情形?

  • 在你的项目进行版本升级的时候,大概率情况下数据库也需要同步升级,Liquibase 会自动扫描数据库迁移文件(changeSet),将迁移文件的版本号与历史记录表(changelog )中的版本号进行对比,略过已执行的的迁移文件,顺序执行未执行的新版本迁移文件,最终实现数据库与代码版本相匹配;
  • 当多人协作开发项目的时候,系统源代码可使用 git 保持同步,那么数据库的同步就可交由 liquibase 来保证;
  • 使用 liquibase 可以方便地比较两个数据库的差异;
  • 使用 liquibase 还支持数据库版本回滚。

Liquibase的优点有哪些?

  1. 配置文件支持SQL、XML、JSON 或者 YAML;
  2. 可兼容14种主流数据库如 oracle,mysql 等,支持平滑迁移;
  3. 版本控制按序执行;
  4. 可以用上下文控制sql在何时何地如何执行;
  5. 具备在应用中具有if / then逻辑的能力;
  6. 支持 schema 的变更;
  7. 根据配置文件自动生成sql语句用于预览;
  8. 可重复执行迁移;
  9. 可插件拓展;
  10. 可回滚;
  11. 支持schema方式的多租户(multi-tenant);
  12. 能够在多种数据库类型上具有相同的更改描述;
  13. 生成的数据库历史记录文档;
  14. 能够轻松指定更复杂的多语句更改。

整合要点

boot-admin 是一款采用前后端分离模式、基于 SpringCloud 微服务架构的SaaS后台管理框架。系统内置基础管理、权限管理、运行管理、定义管理、代码生成器和办公管理6个功能模块,集成分布式事务 Seata、工作流引擎 Flowable、业务规则引擎 Drools、后台作业调度框架 Quartz 等,技术栈包括 Mybatis-plus、Redis、Nacos、Seata、Flowable、Drools、Quartz、SpringCloud、Springboot Admin Gateway、Liquibase、jwt、Openfeign、I18n等。

引入Maven依赖

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

添加配置

spring:
  liquibase:
    enabled: true
    change-log: classpath:liquibase/master.xml

创建master.xml

在 resources 下创建文件夹 liquibase ,创建文件 master.xml

<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.1.xsd">

    <includeAll path="liquibase/changelogs/" relativeToChangelogFile="false"/>

</databaseChangeLog>

在 liquibase 文件夹下创建 changelogs 和 sql 两个文件夹,如下图所示:

编写数据库变更单元 changeSet

在 resources\liquibase\changelogs 下创建 changeSet 文件,推荐每月一个 xml 文件,文件名格式:【changelog-年度+月份.xml】,如:changelog-202304.xml
常用操作举例:

创建表

    <changeSet author="admin (generated)" id="00001-9">
        <createTable remarks="行政区划表" tableName="TB_ADM_DIV">
            <column name="GUID" remarks="主键" type="NVARCHAR2(38)">
                <constraints nullable="false" primaryKey="true" primaryKeyName="PK_TB_ADM_DIV"/>
            </column>
            <column name="ADM_DIV_CODE" remarks="行政区划代码" type="NVARCHAR2(12)">
                <constraints nullable="false"/>
            </column>
            <column name="ADM_DIV_NAME" remarks="行政区划名称" type="NVARCHAR2(100)">
                <constraints nullable="false"/>
            </column>
            <column name="CREATE_BY" remarks="记录创建者" type="NVARCHAR2(100)">
                <constraints nullable="false"/>
            </column>
            <column name="CREATE_TIME" remarks="记录创建时间" type="${type.datetime}">
                <constraints nullable="false"/>
            </column>
            <column name="MODIFY_BY" remarks="记录最后修改者" type="NVARCHAR2(100)">
                <constraints nullable="false"/>
            </column>
            <column name="MODIFY_TIME" remarks="记录最后修改时间" type="${type.datetime}">
                <constraints nullable="false"/>
            </column>
            <column defaultValueComputed="${now}" name="DATESTAMP" remarks="时间戳" type="${type.datetime}">
                <constraints nullable="false"/>
            </column>
            <column name="ENABLED" remarks="启用状态;ENABLED" type="NVARCHAR2(1)">
                <constraints nullable="false"/>
            </column>
            <column name="DELETED" remarks="删除状态;DELETED" type="NVARCHAR2(1)">
                <constraints nullable="false"/>
            </column>
            <column name="VERSION" remarks="乐观锁" type="${type.int}">
                <constraints nullable="false"/>
            </column>
            <column name="REMARKS" remarks="备注" type="NVARCHAR2(900)"/>
            <column name="TENANT_ID_" remarks="租户ID" type="NVARCHAR2(38)">
                <constraints nullable="false"/>
            </column>
            <column name="PARENT_GUID" remarks="父级GUID" type="NVARCHAR2(38)">
                <constraints nullable="false"/>
            </column>
            <column name="LEAF" remarks="是否末级;YESNO" type="NVARCHAR2(1)">
                <constraints nullable="false"/>
            </column>
            <column name="SORT" remarks="顺序号" type="${type.int}">
                <constraints nullable="false"/>
            </column>
        </createTable>
    </changeSet>

添加表字段

    <changeSet author="admin" id="00002-1">
        <addColumn tableName="TB_ADM_DIV">
            <column name="EXT" remarks="扩展" type="VARCHAR(64)"/>
        </addColumn>
	</changeSet>

删除表字段

    <changeSet author="admin" id="00002-2">
        <dropColumn tableName="TB_ADM_DIV" columnName="EXT"/>
    </changeSet>

修改表字段说明

    <changeSet author="admin" id="00002-3">
        <setColumnRemarks tableName="TB_ADM_DIV" columnName="EXT" remarks="扩展字段"/>
    </changeSet>

修改表字段类型

    <changeSet author="admin" id="00002-4">
        <modifyDataType tableName="TB_ADM_DIV" columnName="EXT" newDataType="VARCHAR2(2000)"/>
    </changeSet>

创建视图

    <changeSet author="admin (generated)" id="00001-1" dbms="oracle">
        <createView fullDefinition="true" remarks="表和视图" viewName="V_TABLES_MASTER">
            CREATE OR REPLACE FORCE VIEW V_TABLES_MASTER (TABLE_SCHEMA, TABLENAME, TABLETYPE, COMMENTS, TENANT_ID_) AS
            select SYS_CONTEXT('USERENV','CURRENT_SCHEMA') TABLE_SCHEMA,
            t.tname tableName,
            tabtype tabletype,
            f.comments comments,
            'DEMO' TENANT_ID_
            from tab t
            inner join user_tab_comments f
            on t.tname = f.table_name
            where tname != 'DATABASECHANGELOG'
            and tname != 'DATABASECHANGELOGLOCK'
            and tname != 'UNDO_LOG'
        </createView>
    </changeSet>
    <changeSet author="37514 (generated)" id="00000-2" dbms="mysql">
        <createView fullDefinition="true" remarks="表和视图" viewName="V_TABLES_MASTER">
            CREATE OR REPLACE VIEW V_TABLES_MASTER  AS
            SELECT
            TABLE_SCHEMA,
            TABLE_NAME AS TABLENAME,
            case when table_type='BASE TABLE' then 'TABLE' ELSE table_type END AS TABLETYPE,
            TABLE_COMMENT AS COMMENTS,
            'DEMO' TENANT_ID_
            FROM
            information_schema.`TABLES`
            WHERE table_name != 'databasechangeloglock' AND TABLE_NAME != 'databasechangelog' AND TABLE_NAME != 'undo_log'
        </createView>
    </changeSet>

兼容 Oracle 和 Mysql 配置

    <property name="type.datetime" value="date" dbms="oracle"/>
    <property name="type.datetime" value="timestamp" dbms="mysql"/>
    <property name="type.int" value="NUMBER(*, 0)" dbms="oracle"/>
    <property name="type.int" value="INT" dbms="mysql"/>
    <property name="type.decimal" value="NUMBER(*, 2)" dbms="oracle"/>
    <property name="type.decimal" value="DECIMAL" dbms="mysql"/>
    <property name="now" value="SYSDATE" dbms="oracle"/>
    <property name="now" value="now()" dbms="mysql,h2"/>
    <property name="autoIncrement" value="true" dbms="mysql,h2,postgresql,oracle"/>
    <property name="amount" value="decimal(20,2)"/>
    <property name="uuid" value="sys_guid()" dbms="oracle"/>
    <property name="uuid" value="UUID()" dbms="mysql"/>

执行 SQL 文件

    <changeSet id="20000820-003" author="Administrator" dbms="oracle">
        <sqlFile dbms="oracle" path="classpath:/liquibase/sql/seata-undo_log-oracle.sql" />
    </changeSet>
    <changeSet id="20000820-003" author="Administrator" dbms="mysql">
        <sqlFile dbms="mysql" path="classpath:/liquibase/sql/seata-undo_log-mysql.sql" />
    </changeSet>

需将对应 sql 文件放在指定文件夹中。

Liquibase changeSet常用命令清单

add

create

drop

rename

sql

othr

05-05 20:16