Liquibase-数据库脚本版本管理控制

1. 简介

Liquibase是一个用于跟踪、管理和应用数据库变化的开源的数据库重构工具。它将所有数据库的变化(包括结构和数据)都保存在XML文件中,便于版本控制。

Liquibase使参与应用程序发布过程的任何人都可以轻松地:

  • 不依赖于特定的数据库,Liquibase会自动适配目标数据库进行脚本初始化,目前支持至少30种主流数据库。
  • 提供数据库比较功能,比较结果保存在XML中,基于该XML可以用Liquibase轻松部署或升级数据库。
  • 以XML记录/存储数据库变化,其中以authorid唯一标识一个变化(ChangSet),支持数据库变化的合并,因此支持多开发人员同时工作。
  • 在数据库中保存数据库修改历史(DatabaseChangeHistory),在数据库升级时自动跳过已应用的变化(ChangSet)。
  • 提供变化应用的回滚功能,可按时间、数量或标签(tag)回滚已应用的变化。通过这种方式,开发人员可轻易的还原数据库在任何时间点的状态。
  • 可生成数据库修改文档(HTML格式)
  • 提供数据重构的独立的IDE和Eclipse插件
  • 将所有变化(包括结构和数据)存在XML文件中,便于版本控制的工具
    springboot支持,只需要导入依赖。
    application.yml配置(可选)
    不配置,默认去resource/db/changelog下找db.changelog-mastert.yml文件

2. Quick Start

使用步骤

  • step1: 创建一个数据库变更日志(change log)文件。
  • step2: 在变更日志文件内部创建一个变更集(change set)。
  • step3: 通过命令行或构建脚本对数据库进行变更集。
  • step4: 检验数据库中的变更

面向spring开发😉,通过springboot 整合 liquibase 了解其作用。

2.1 添加maven依赖

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.9.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.liquibase</groupId> <artifactId>liquibase-core</artifactId> <version>4.8.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>

2.2 application.yaml

spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true username: root password: 123456 liquibase: # 指定配置文件路径 change-log: classpath:db/db.changelog-master.xml # 覆盖本地 ddl dml drop-first: true # 是否启用 enabled: true # 记录版本日志表 database-change-log-table: databasechangelog # 记录版本改变lock表 database-change-log-lock-table: databasechangeloglock

2.3 添加 liquibase xml

db.changelog-master.xml

liquibase 配置文件入口,主要用来引用其他的changelog.xml,如下配置文件中的include,当然也可以使用includeAll来引用整个目录

<?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.8.xsd"> <include file="classpath:db/changelog/changelog-init-0.0.1.xml"/> </databaseChangeLog>

changelog-init-0.0.1.xml

主要记录了ddl的变化信息,比如 如下配置文件中创建了两个表roleuser

<?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-4.6.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd"> <changeSet author="ludangxin" id="1662615627445-2"> <createTable tableName="role" remarks="角色信息表"> <column name="name" remarks="角色名称" type="VARCHAR(255)"/> <column name="role_key" remarks="角色key" type="VARCHAR(255)"/> </createTable> </changeSet> <changeSet author="ludangxin" id="1662615627445-3"> <createTable tableName="user" remarks="用户信息表"> <column name="id" type="INT" remarks="主键"> <constraints nullable="false" primaryKey="true"/> </column> <column name="username" type="VARCHAR(255)" remarks="用户名称"/> <column name="password" type="VARCHAR(255)" remarks="密码"/> <column name="age" type="INT" remarks="性别"/> <column name="sex" type="VARCHAR(255)" remarks="性别"/> <column name="role" type="VARCHAR(255)" remarks="角色"/> <column name="create_time" type="DATETIME" defaultValueComputed="NOW()" remarks="创建时间"/> </createTable> </changeSet> </databaseChangeLog>

2.4 启动测试

项目启动完成后,查看数据库如下图,我们在changelog-init-0.0.1.xml文件中定义的脚本初始化到了数据库中

通过该demo快速的完成了springboot 集成 liquibase,且完成数据库的初始化。

2.5 测试changeset版本控制

前提是我们将application.yml中的drop-first置为false,因为drop-first=true相当于每次都重置数据库

此时我们想在user表中新增一个create_by字段,便直接在之前的changeset中添加了字段,如下图所示,然后启动项目看结果

启动时控制台报错信息如下:

报错信息是我们直接修改了changeset后导致md5值与之前的不匹配(直接在之前的changeset中做了修改)

🤔 liquibase如何判断 是同一changeset的?

authorid唯一标识一个变化(ChangSet)

🤔 liquibase是如何进行changeset版本控制的?

Liquibase会对已经执行的changelog的每一个changeSet的内容进行md5计算,生成的值是databasechanglog表的MD5SUM字段。

​ 当重新启动Liquibase时,会对每个changeSet进行md5值计算,与databasechanglog表中的MD5SUM字段进行对比,如果不一致,说明changeSet值已经被修改,无法启动成功。

3. DDL操作

3.1 创建表

<changeSet author="ludangxin" id="20220908-1"> <createTable tableName="order" remarks="订单信息表"> <column name="id" remarks="主键" type="BIGINT"> <constraints nullable="false" primaryKey="true"/> </column> <column name="order_number" remarks="订单编号" type="VARCHAR(255)"/> <column name="user_id" remarks="用户id" type="BIGINT"/> </createTable> </changeSet>

3.2 删除表

<changeSet author="ludangxin" id="20220908-2"> <dropTable tableName="order"/> </changeSet>

3.3 修改表

3.3.1 添加字段

<changeSet author="ludangxin" id="20220908-3"> <addColumn tableName="order"> <column name="status" remarks="订单状态" type="INT" defaultValue="0"/> </addColumn> </changeSet>

3.3.2 删除字段

<changeSet author="ludangxin" id="20220908-4"> <dropColumn tableName="order" columnName="order_number"/> </changeSet>

3.3.3 修改字段

<changeSet author="ludangxin" id="20220908-5"> <renameColumn tableName="order" oldColumnName="status" newColumnName="state" columnDataType="VARCHAR(10)" remarks="订单状态"/> </changeSet>

修改字段类型 不建议使用 因为会把字段的其他信息搞丢,比如字段注释

<changeSet author="ludangxin" id="20220908-7"> <modifyDataType tableName="order" columnName="state" newDataType="INT" /> </changeSet>

3.4 执行自定义sql

如果想执行自定义的sql 可以直接使用sql标签, 标签内可以写任何执行语句, 虽然这样可以执行任意sql 但是也损失了liquibase的数据库兼容能力, 如下示例就无法在mysql数据库下运行

<changeSet author="ludangxin" id="20220908-10"> <sql> CREATE TABLE user_base_info ( id int not null, username VARCHAR ( 100 ), password VARCHAR ( 50 ), age int ); COMMENT ON TABLE user_base_info IS '人员基本信息'; COMMENT ON COLUMN user_base_info.username IS '用户名称'; COMMENT ON COLUMN user_base_info.password IS '密码'; COMMENT ON COLUMN user_base_info.age IS '年龄'; </sql> </changeSet>

当然也支持直接执行sql文件, 如下

<changeSet author="ludangxin" id="20220908-11"> <sqlFile path="/db/sql/init.sql"/> </changeSet>

3.5 创建函数

创建一个postgre数据库的函数 endDelimiter 需要跟sql文件中的分隔符一致 dbms指定sql方言

<changeSet author="ludangxin" id="20220908-8"> <sqlFile path="/db/sql/functions/postgre.sql" endDelimiter="$$" dbms="postgresql"/> </changeSet>

postgre.sql内容如下 创建了一个str 2 date 的函数

-- str 2 TIMESTAMP CREATE OR REPLACE FUNCTION get_date(date_str VARCHAR) RETURNS TIMESTAMP AS $$ BEGIN RETURN CAST(date_str AS timestamp); END; $$ LANGUAGE plpgsql;

3.6 设置上下文

如上示例 创建函数的sql只能在postgre数据库中执行, 切换到mysql就无法执行

因此我们可以通过liquibase的contexts的属性设置当前的上下文, 实现当前运行环境下执行指定的上下文的changese

首先 在changeSet标签中设置context属性值, 表示只在postgre环境下执行该changeSet

<changeSet author="ludangxin" id="20220908-8" context="postgre"> <sqlFile path="/db/sql/functions/postgre.sql" endDelimiter="$$" dbms="postgresql"/> </changeSet>

然后在配置文件中设置当前的上下文

spring: liquibase: ... # 运行的上下文 需要跟changeSet中的context值保持一致 contexts: postgre

这样的话当项目启动时就只会执行context="postgre"和没有设置context的changeSet, 实现了非兼容脚本的多库适配

4. DML操作

4.1 数据初始化

项目部署难免会有系统内置的数据,这时我们可以通过使用liquibase进行初始化

新建csv文件user-init-0.0.1.csv

"id","username","password","age","sex","role","create_time","create_by" "111","张三","222","23","1","admin","2022-09-08 14:22:33","system" "112","李四","333","26","1","admin","2022-09-08 14:22:33","system" "113","王五","444","25","1","admin","2022-09-08 14:22:33","system"

使用loadData标签进行数据的初始化

<changeSet author="ludangxin" id="20220908-8"> <loadData tableName="user" file="data/user-init-0.0.1.csv" separator="," encoding="UTF-8" relativeToChangelogFile="true"/> </changeSet>

4.2 新增数据

<changeSet author="ludangxin" id="20220908-6"> <insert tableName="order"> <column name="id" valueNumeric="666"/> <column name="user_id" value="2222"/> <column name="state" value="2"/> </insert> <insert tableName="order"> <column name="id" valueNumeric="888"/> <column name="user_id" value="3333"/> <column name="state" value="1"/> </insert> </changeSet>

4.3 初始化总是变动的数据

使用上述的loadData标签加载数据,当数据发生变化时,直接修改csv文件进行发布时,会报错版本不一致。

这时可以使用loadUpdateData标签进行处理,注意的是changeset上需要加参数runOnChange="true"(当数据发生改变时不去校验md5)如下

<changeSet author="ludangxin" id="20220908-9" runOnChange="true"> <loadUpdateData tableName="user" file="data/user-init-0.0.1.csv" primaryKey="id" separator="," encoding="UTF-8" relativeToChangelogFile="true"/> </changeSet>

5. 集成maven

使用maven集成liquibase可以方便的通过liquibase maven plaugin实现很多功能。例如:脚本运行,生成文档,数据库差异比较等

5.1 添加 properties

添加properties 文件用来配置liquibase plugin的配置信息。例如 数据库的链接信息,配置文件路径的配置等

liquibase.properties

-- 数据库连接信息 driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true username=root password=123456 -- liquibse系统表 表名称配置 databaseChangeLogTableName=databasechangelog databaseChangeLogLockTableName=databasechangeloglock -- 输出文件路径配置 outputChangeLogFile=src/main/resources/db/changelog/changelog-output-0.0.1.xml -- liuquibase xml文件路径指定 changeLogFile=src/main/resources/db/db.changelog-master.xml

5.2 修改pom

<?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 http://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>2.3.9.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>org.example</groupId> <artifactId>liquibase-demo2</artifactId> <version>1.0-SNAPSHOT</version> <properties> <liquibase.version>4.8.0</liquibase.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.liquibase</groupId> <artifactId>liquibase-core</artifactId> <version>${liquibase.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>${liquibase.version}</version> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <configuration> <!--properties文件路径,该文件记录了数据库连接信息等--> <propertyFile>src/main/resources/db/liquibase.properties</propertyFile> <propertyFileWillOverride>true</propertyFileWillOverride> </configuration> </plugin> </plugins> </build> </project>

6. plugin-逆向生成xml

通过liquibase maven 插件,从已有的数据库生成xml配置信息

通过idea的maven功能,找到 liquibase plugin,双击如图liquibase:generateChangeLog选项,执行完成之后就会在properties文件中配置的outputChangeLogFile路径生成对应的xml文件,如下图所示

7. plugin-生成数据库修改文档

双击liquibase plugin面板中的liquibase:dbDoc选项,会生成数据库修改文档,默认会生成到target目录中,如下图所示

访问index.html会展示如下页面,简直应有尽有

8. plugin-发布changelog

之前我们对changelog的编辑都需要通过启动项目来运行changelog,有时候我们可能想不重启项目便能将修改发布运行到数据库中

双击liquibase plugin面板中的liquibase:update选项,便可以将修改同步到数据库中

注:这里有个bug(也可能不是bug,我目前还没找到对应的解决办法,如果您有解决方案 🙏):通过plugin发布changelog时,由于我们在db.changelog-masterinclude的是classpath路径,但是通过plugin发布时会报错找不到include的xml文件,此时我们可以通过设置相对路径来解决这个问题,比如<include file="classpath:changelog/changelog-init-0.0.1.xml" relativeToChangelogFile="true"/>,然而这又引发了另外一个问题,plugin和直接启动springboot项目生成的databasechangelog表中的filename字段值不同,导致运行changelog时报错,因为liquibase默认会比较同一filename下的changeset

9. plugin-比较数据库差异

首先使用liquibase diff 功能前,我们在properties中加入参考的数据库 test3配置信息

用于差异比较的数据库,以此数据库为准,生成diff xml

# 对比参考的数据库信息 (用于plugin-diff) referenceUrl=jdbc:mysql://localhost:3306/test3?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true referenceDriver=com.mysql.cj.jdbc.Driver referenceUsername=root referencePassword=123456 # 生成的差异比较xml的输出路径 diffChangeLogFile=src/main/resources/db/changelog/changelog-diff-0.0.1.xml

我们先观察下两个数据库有什么样的差异,再验证生成的diff xml

两个数据库如下,差异都用红色框起来了。

然后再用liquibase插件看下生成的 diff xml信息

双击liquibase plugin面板中的liquibase:diff选项,如下图所示

执行完毕后,查看diff xml 内容如下:

生成的xml文件符合预期,将与test3数据库的差异都生成到了diff xml中


__EOF__

本文作者张铁牛
本文链接https://www.cnblogs.com/ludangxin/p/16676701.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   张铁牛  阅读(2263)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示