0%

浅谈Liquibase

Liquibase作为一款开源的数据库架构变更管理工具,可以帮助开发者方便跟踪、管理数据库变化的修订。其支持MySQL、PostgreSQL、Oracle等众多数据库

abstract.png

集成Spring

Spring boot内置了对Liquibase的支持,只需要在项目中引入Liquibase依赖并进行配置即可

1
2
3
4
5
6
<!--Liquibase-->
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.6.3</version>
</dependency>

对于配置而言,Liquibase默认会使用Spring配置项的数据库连接。如果期望不使用该默认配置,也可以显式单独配置

1
2
3
4
5
6
7
8
9
10
# 数据库配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db1?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456

# 使能 Liquibase
spring.liquibase.enabled=true
# 指定 Liquibase 变更记录的位置
spring.liquibase.change-log=classpath:/db/changelog/master.xml

在上面的配置项spring.liquibase.change-log中我们指定了Liquibase 变更记录的位置。事实上,通常这只是一个master配置文件,如下所示。其中通过include标签来包含、汇聚所有的changlog文件

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<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.9.xsd">

<!-- master.xml文件的作用就是汇集所有changlog文件 -->
<!-- relativeToChangelogFile 为 true 时表示使用的是相对路径 而不是classpath-->
<include file="changelog-1.0.xml" relativeToChangelogFile="true"/>

</databaseChangeLog>

目录结构如下所示

figure 1.jpeg

详解ChangeLog

一个典型地changlog文件如下所示,其记录了我们对数据库的变更、修订内容。这里我们使用XML进行介绍,其实Liquibase支持SQL、XML、JSON、YAML等多种形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?xml version="1.0" encoding="UTF-8"?>
<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.9.xsd">

<changeSet id="create_density_range" author="Aaron" runOnChange="true">
<!-- 执行该SQL的先决条件: 该表不存在 -->
<preConditions onError="MARK_RAN" onFail="MARK_RAN">
<not>
<tableExists tableName="density_range"/>
</not>
</preConditions>

<createTable remarks="密度区间" tableName="density_range">
<column name="id" type="INTEGER" remarks="主键id" autoIncrement="true" startWith="1" incrementBy="1">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="low_start" remarks="低密度区间: 下限" type="INTEGER"/>
<column name="low_end" remarks="低密度区间: 上限" type="INTEGER"/>
<column name="medium_start" remarks="中密度区间: 下限" type="INTEGER"/>
<column name="medium_end" remarks="中密度区间: 上限" type="INTEGER"/>
<column name="high_start" remarks="高密度区间: 下限" type="INTEGER"/>
<column name="high_end" remarks="高密度区间: 上限" type="INTEGER"/>
</createTable>
</changeSet>

<changeSet id="create_geohash_6" author="Aaron" runOnChange="true">
<preConditions onError="MARK_RAN" onFail="MARK_RAN">
<not>
<tableExists tableName="geohash_6"/>
</not>
</preConditions>

<createTable remarks="GeoHash6信息" tableName="geohash_6">
<column name="id" type="INTEGER" remarks="主键id" autoIncrement="true" startWith="1" incrementBy="1">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="geohash" remarks="GeoHash6数据" type="VARCHAR(255)"/>
<column name="center_lng" remarks="中心点经度" type="VARCHAR(255)"/>
<column name="center_lat" remarks="中心点纬度" type="VARCHAR(255)"/>
<column name="max_lng" remarks="最大经度" type="VARCHAR(255)"/>
<column name="max_lat" remarks="最大纬度" type="VARCHAR(255)"/>
<column name="min_lng" remarks="最小经度" type="VARCHAR(255)"/>
<column name="min_lat" remarks="最小纬度" type="VARCHAR(255)"/>
<column name="is_hz_edge" remarks="是否为边界" type="BOOLEAN"/>
</createTable>
</changeSet>

<changeSet id="add tag" author="Aaron">
<!-- tag 为版本号 -->
<tagDatabase tag="1.0"/>
</changeSet>

</databaseChangeLog>
  • changeSet标签:出于便于阅读的角度考虑,每个changeSet每次只包含对一张表的修订操作。同时,对于已执行过changeSet一般情况下不应做任何修改
  • runOnChange属性:用于控制每次运行changeSet时,如果本次changeSet发生修改是否重新执行。其默认值为false,当本次changeSet发生修改不会执行变更,而是会发出错误以提醒发生了意外修改。其原理是通过比对changeSet的MD5校验和、存储在DATABASECHANGELOG表中的MD5SUM进行 判定的。典型地对于存储过程场景而言,该属性一般设置为true。这样每次对历史changeSet进行修改后,都需要Liquibase重新执行该changeSet以保证修改生效
  • preConditions标签:控制数据库变更的先决条件。如果先决条件满足要求,则会执行该标签后相应的数据库变更;如果先决条件在执行过程抛出异常,则行为由onError属性控制;如果先决条件不满足要求,则行为由onFail属性控制;
  • onFail、onError属性:preConditions标签的onFail、onError属性可用的取值有:
    • CONTINUE:跳过该changeSet、会在下次更新时再次尝试。继续执行changeLog
    • HALT:停止执行整个changeLog。这也是默认值
    • MARK_RAN:跳过该changeSet同时将其标记为已执行。继续执行changeLog
    • WARN:发出警告并继续执行changeSet、changeLog
  • tagDatabase:对本次版本的数据库修订打标签

至此当我们启动SpringBoot项目后,即可发现数据库不仅包含了我们期望的业务表,还新增了两张Liquibase相关的表——DATABASECHANGELOG、DATABASECHANGELOGLOCK

figure 2.jpeg

而对于DATABASECHANGELOGLOCK锁表而言,如果某记录的LOCKED字段为1,则表示该锁被占有了。故在Liquibase锁无法正常释放时,可能会导致无法创建表,这时可通过手动置零来释放锁

生成ChangeLog

liquibase-maven-plugin可以帮助我们直接地从数据库中创建、生成相应的ChangeLog文件,配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<build>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.6.3</version>
<configuration>
<!--生成Changelog的输出目录-->
<outputChangeLogFile>${basedir}/src/main/resources/db/generate/changeLog.xml</outputChangeLogFile>
<!--DB连接信息-->
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://localhost:3306/db1?useSSL=false</url>
<username>root</username>
<password>123456</password>
<dropFirst>false</dropFirst>
<verbose>true</verbose>
<logging>debug</logging>
<promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
<outputFileEncoding>UTF-8</outputFileEncoding>
<propertyFileWillOverride>true</propertyFileWillOverride>
</configuration>
</plugin>
</plugins>
</build>

操作、效果如下所示

figure 3.jpeg

请我喝杯咖啡捏~
  • 本文作者: Aaron Zhu
  • 本文链接: https://xyzghio.xyz/Liquibase/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-ND 许可协议。转载请注明出处!

欢迎关注我的微信公众号:青灯抽丝