Liquibase 学习笔记 | 集成SpringBoot

372 阅读4分钟

SpringBoot 集成 Liquibase

1.引入maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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>3.1.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.com.servision.liquibase</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>demo</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--   liquibase     -->
        <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
        </dependency>
        <!-- 添加数据库驱动依赖,例如MySQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- 添加MyBatis依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
        <!-- MySQL 8 connector -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        
    </dependencies>

</project>

2.修改配置文件

server:
  port: 9001

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/liquibase_demo
    username: root
  liquibase:
    change-log: classpath:/liquibase/db.changelog-master.xml
    enabled: true
    contexts: dev

change-log 属性指定变更日志文件位置。 该文件配置着需要同步的数据库结构。 如果需要执行多个变更日志文件,查看下面执行单个和多个变更日志文件的示例。

3.创建 change-log 文件

在配置文件 spring.liquibase.change-log 指定 db.changelog-master.xml 文件

下面是 db.changelog-master.xml 的配置

<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
    <changeSet author="kafeim (generated)" id="1699778673366-1" context="dev">
        <createTable remarks="用户" tableName="t_user">
            <column autoIncrement="true" name="id" type="BIGINT">
                <constraints nullable="false" primaryKey="true"/>
            </column>
            <column name="username" remarks="名称" type="VARCHAR(255)"/>
            <column name="password" remarks="密码" type="VARCHAR(255)"/>
        </createTable>
    </changeSet>
</databaseChangeLog>

需要注意的是 changeSet 标签中的 context="dev" 对应着 spring.liquibase.contexts 的值 spring.liquibase.contexts的值可以是多个的。

应用启动的时候回去执行 对应 contexts 值的 changeSet标签的内容。

1. 如果想执行多个变更日志

执行多个变更文件,使用includeAll标签 ,指定需要执行变更文件的文件夹,项目启动时会将配置的文件夹下的变更日志全部执行一遍。

需要注意的是,执行的顺序是按照字母的排序顺序来的,有执行先后顺序的要求,需要把文件名规范一下。如 001.xml 002.xml

<!-- src/main/resources/db/changelog/db.changelog-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.8.xsd">

    <!-- 引用其他变更日志文件 -->
    <includeAll path="classpath:/liquibase/changelog" context="dev" />

</databaseChangeLog>
2. 如果只是想执行指定的几个文件可以使用下面的配置
<!-- src/main/resources/db/changelog/db.changelog-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.8.xsd">

    <!-- 引用其他变更日志文件 -->
    <include file="classpath:/db/changelog/changes/001-initial-schema1.xml" context="dev" />
    !-- 引用其他变更日志文件 -->
    <include file="classpath:/db/changelog/changes/001-initial-schema2.xml" context="uat"/>

</databaseChangeLog>

xml 中 include 或者是 changeSet 标签的 context 不是必填的,但是如果配置文件中spring.liquibase.contexts 指定了要执行的上下文,那么你想要执行的变更文件的标签也要带上相应的 context。

4.启动应用执行变更文件

image.png

当你看到控制台输出成功,并且数据库中有 DATABASECHANGELOG 表 ,还有执行的变更日志的记录,就说明成功了。

5.liquibase 变更日志执行的逻辑

  1. 检查 DATABASECHANGELOG: Liquibase 首先检查 DATABASECHANGELOG 表是否存在于当前数据库中。如果表不存在,Liquibase 会创建它。
  2. 查询已执行的变更集: Liquibase 会查询 DATABASECHANGELOG 表以确定哪些变更集已被执行。每个已执行的变更集都记录了 idauthorfilename
  3. 变更集唯一性检查: 对于要执行的每个变更集,Liquibase 会检查 DATABASECHANGELOG 表中是否已存在相同的 idauthorfilename 的组合。如果这样的记录存在,它表明该变更集已经被执行过,因此 Liquibase 将跳过它。
  4. 上下文和标签过滤: 如果为变更集指定了 contextlabel,Liquibase 还会检查这些属性是否与运行时指定的上下文或标签匹配。只有当它们匹配或未定义时,变更集才会被执行。
  5. 执行变更集: 对于那些未在 DATABASECHANGELOG 表中记录的变更集,Liquibase 会执行它们,并在执行后将它们添加到 DATABASECHANGELOG 表中,这样在未来的运行中,Liquibase 就知道这些变更集已经被执行过了。
  6. 记录执行细节: 当一个变更集被执行时,Liquibase 会在 DATABASECHANGELOG 表中插入一条新记录,包含变更集的 idauthorfilename,以及执行时间、订单号(orderexecuted)、执行类型(EXECTYPE)等信息。这样即使在多次执行过程中,变更集也不会被执行第二次。 在 DATABASECHANGELOG 表中,idauthorfilename 以及 contexts 这些列一起用来确定变更集是否已经被执行。但是,只有 idauthorfilename 用来标识变更集的唯一性。context 则用来决定在给定的运行上下文中是否应该执行变更集。
  7. MD5SUM:Liquibase 已经执行并记录了校验和的变更集(changeSet)上做了修改,那么下次尝试运行项目启动时的 Liquibase 迁移时,它会报错。Liquibase 会在启动时尝试校验每个变更集的校验和,如果发现校验和与 DATABASECHANGELOG 表中记录的不一致,它会抛出错误,并且不会执行任何进一步的数据库迁移。这里的校验和 指的是数据库中 MD5SUM 字段。