文章已收录至https://lichong.work,转载请注明原文链接。 ps:欢迎关注公众号“Fun肆编程”或添加我的私人微信交流经验🤝

一、背景

如果你还在每个版本发布时单独分发数据库变动脚本,甚至简单粗暴的将开发库直接导出并导入生产环境,建议一起来读一下这篇文章。

二、主流方案

这个一般来说就两个: FlywayLiquibase ,如有更优秀的开源技术请大家留言补充。

✌Flyway VS Liquibase

感兴趣的可以看一下这篇文章,我的总结如下:

  1. Flyway 和 Liquibase 都支持数据库重构和版本控制所需的所有功能,并且这两个工具都可以完美集成在 Maven 或 Gradle 构建脚本以及 Spring Boot 生态系统中。
  2. Flyway 使用 SQL 来定义数据库更改,因此您可以定制 SQL 脚本以与 Oracle 或 PostgreSQL 等数据库很好地配合使用。另一方面,Liquibase 不使用 SQL ,而是通过使用 XML , YAML 或 JSON 来定义数据库更改来引入抽象层。因此,Liquibase 更适合用于拥有多数据源的应用中。但是,如果想完全控制 SQL ,Flyway 是首选工具,因为我们可以使用定制的 SQL 甚至 Java 代码修改数据库。
  3. Flyway 相比 Liquibase 在社区更活跃。

简单来说:

  • 面向 SQL,选择 Flyway
  • 不面向 SQL,选择 Liquibase

三、Flyway中常见概念

Flyway 中的概念可查阅官方文档,这里挑选一些重要的进行简单介绍。

Schema History Table

  • Flyway 对数据库进行版本控制的方式,是在指定数据库中创建一张表,即 Schema History Table(默认为 flyway_schema_history),记录由 Flyway 所执行的 sql 脚本状态。

Migration

  • 在 Flyway 中,所有对数据库的变动,均称为 migration(译:迁移),migration 可以是 SQL 文件,也可以是 Java 类。
  • 默认的查找 migration 的路径是 classpath:db/migration,也就是说对应 SQL 文件可以放置在 src/main/resources/db/migration 下,Java 类可以放置在 src/main/java/db/migration 下。
  • Migration 可以是仅执行一次的(versioned),也可是重复执行的(repeatable)。

Versioned Migration

  • Versioned migration 包括版本号、描述和校验值(checksum,自动计算),命名方式如下: Versioned migration图片说明

  • 其中版本号必须是全局(在一个 Schema History Table 里维护)唯一,默认情况下(可通过参数调整)版本号只能增加,不能在已经执行了高版本的 migration 之后再执行低版本的 migration。

  • 版本号可以是数字加 . 的形式,例如下面都是合法的版本号:

1
001
5.2
2013.01.15.11.35.56
  • checksum 用来检查 migration 在执行过后是否发生了变化,如果发生了变化,会导致这个版本以及后续版本的 migration 无法执行。

flyway_schema_history表举例: flyway_schema_history表

Undo Migration

  • Versioned migration 中,还有一种特殊的类型 undo migration,可为对应版本的常规 versioned migration 进行回退操作(但这玩意是收费的),命名方式如下: Undo Migration图片说明

Repeatable Migration

  • Repeatable migration 包括描述和校验值,没有版本号信息,会在每次校验值(migration 内容变化会导致校验值变化)发生变化时重复执行,命名方式如下: Repeatable Migration图片说明
  • Repeatable migration 会在所有的 versioned migration 都执行过后再进行执行,Repeatable migration 内部按照其文件名中描述(description)部分的顺序执行。
  • 在 repeatable migration 中需保证其中内容可重复执行,比如在 DDL 中使用 CREATE OR REPLACE。

Baseline

  • 当数据中已经存在内容时,再引入 Flyway,可通过 Baseline 设定一条基线。 Baseline图片说明
  • 如果想将基线之前的数据库中表结构和数据纳入 Flyway 一同管理,可以将基线前的状态导出成数据库脚本,通过 versioned migration 添加至 flyway 中,并设定 baselin version,Flyway 会忽略基线版本号(包括)之前版本的所有 migration。
  • 如果不想管理基线之前的数据库状态(比如多模块或应用操作同一数据库,互相之间不受影响),可以只告诉 Flyway 执行 migration 的时候是存在基线的,这样就不会报出数据库非空的异常。

四、在 Spring Boot 项目中引入 Flyway

在 Spring Boot 项目中,引入 Flyway 很简单,因为在 Spring Boot 的 spring-boot-autoconfigure 中包含了 Flyway 的自动配置,只要添加 flyway 的依赖即可,如下图: 在 Spring Boot 项目中引入 Flyway

引入Flyway

Gradle方式:

implementation 'org.flywaydb:flyway-core

Maven方式:

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>

配置配置项

  • spring.flyway.enabled:是否开启 flyway,默认就是开启的
  • spring.flyway.encoding:flyway 字符编码
  • spring.flyway.locations:sql 脚本的目录,默认是classpath:db/migration,如果有多个用 , 隔开
  • spring.flyway.clean-disabled:表示是否要清除已有库下的表,如果执行的脚本是 V1__xxx.sql,那么会先清除已有库下的表,然后再执行脚本,这在开发环境下还挺方便,但是在生产环境下就要命了,而且它默认就是要清除,生产环境一定要自己配置设置为 true。
  • spring.flyway.table:配置数据库信息表的名称,默认是flyway_schema_history。

添加 sql 脚本

引入 Flyway 之后,再启动应用时,会在默认路径 classpath:db/migration 查找 sql 脚本,如果没找到会报错,影响应用启动。

非空数据库处理

当在一个已有项目中引入 Flyway 后,数据库中可能已经存在了一些表和初始化数据,此时按照上述方式引入 Flyway 时会提示如下异常:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.api.FlywayException: Found non-empty schema(s) `demo` but no schema history table. Use baseline() or set baselineOnMigrate to true to initialize the schema history table.

此时需要设定 spring.flyway.baseline-on-migrate=true 参数,告诉 Flyway 在执行 sql 脚本之前,数据库是非空状态。

其他问题

  • SQL 报错 通过 Spring Boot 自动执行 migration 时要注意,一旦 migration 执行失败,应用启动会终止。出现 migration 执行失败时,需要将 Schema History Table 表中的失败记录处理掉,才能再次执行 migration,否则应用会一直无法启动。

  • out-of-order 多人开发时,可能会出现 A 写了 V1 脚本,B 写了 V2 脚本,B 的代码先合并进去了,V2 脚本先执行了,此时 A 的 V1 脚本受版本号只能增加的要求不能再执行。 这种情况可以通过将 spring.flyway.out-of-order 设置为 true 来暂时取消这个限制,不过还是强烈建议 A 将 V1 脚本版本号改为 V3。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~往期精选🪶~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

【前端-开发环境】使用NVM实现不同nodejs版本的自由切换(NVM完整安装使用手册) 【前端-NPM私服】内网使用verdaccio搭建私有npm服务器 【前端-IE兼容】Win10和Win11使用Edge调试前端兼容IE6、IE7、IE8、IE9、IE10、IE11问题 【工具-Shell脚本】java程序产品包模板-linux和windows通用shell启动停止脚本(无需系统安装Java运行环境) 【工具-Nginx】Nginx高性能通用配置文件-注释版-支持防刷限流、可控高并发、HTTP2、防XSS、Gzip、OCSP Stapling、负载、SSL 【工具-WireShark】网络HTTP抓包使用教程 【后端-maven打包】通过profile标签解决同时打jar包 war包需求 【后端-SpringCache】基于Spring Cache封装一个能够批量操作的Redis缓存记录下踩坑历程(pipeline或mget封装) 【后端-SkyWalking】SkyWalking前后端开发环境搭建详细教程步骤-6.x/7.x/8.x版本通用-插件二次开发利器(一)

✨欢迎为耿直少年点赞、关注、收藏!!!

👇👇👇