Springboot集成Flyway详解

news/2024/11/15 1:24:32/文章来源:https://www.cnblogs.com/zhaodalei/p/18333080

1、背景

随着项目的增多,各个项目的版本之间存在差异,因此在升级时,维护项目版本和最新版本之间增量的sql脚本成为一个严重的问题,非常耗时耗力,因此引入一个数据库变更管理工具迫在眉睫。目前比较常用的有flyway和liquibase,liquibase使用xml文件来定义和管理数据库脚本,不依赖于具体的数据库,学习成本相对较高,功能丰富,有自己的一套语法规则;flyway是基于sql语句的,依赖于具体的数据库,使用较为简单,容易上手。我们的项目固定是mysql的数据库,且项目开发周期短,因此引入了flyway。

2、Flyway介绍

官方文档地址:DocumentationFlyway 

flyway实现数据变更管理的大致原理如下:使用一张表专门记录每一个sql脚本的执行情况,执行过的脚本不在重复执行,没有执行过的脚本在程序启动时自动执行。因此我们使用springboot集成flyway进行数据库变更管理,只需要如下两步:

  • 将sql脚本按命名规则命名
  • 将脚本存放到指定位置

2.1、文件命名规则

Flyway的脚本名称有一定的规则,一个完整的脚本名称由五部分组成:前缀+版本+分隔符+描述+文件后缀,前缀、分隔符和文件后缀,都可以通过配置项进行修改。下面介绍的是默认的配置。

前缀:V表示版本迁移,版本号必须唯一,会计算校验和,执行过的文件不允许修改,按版本顺序执行,一个脚本只会执行一次;

U表示撤销迁移,和V开头的版本号相同,表示撤销当前版本的迁移,sql文件编写与版本迁移相反作用的语句即可。

R表示可重复迁移,其没有版本,不是只运行一次,而是每次程序启动,校验发生修改时,就会执行,R始终是在V执行完成后再执行。

版本号:任意的数字版本均可,可以是一个完整的数字或者用点符号分割的数字,版本之间从左到右进行比较大小,遇到点符号进行分割,以0开头的数字会自动移除前面的0后比较大小,如下均是合法的版本:

  • 1
  • 001
  • 6.3
  • 1.2.3.4
  • 2024.07.30
  • 20240730

分割符:默认是两个下划线(__)作为分隔符,分割版本和描述。

描述:官方文档上说明是:下划线或者空格分割单词即可。实际短横线分割单词也可以,理论上只要不和分割符冲突即可。

文件后缀:默认是 .sql

如下均是合法的脚本名称:

V1.0.0__init.sql

V1.0.002__add-new-table.sql

V2023.0709__add_new_table.sql

R__add_new_table.sql

详情请参考官方文档地址: 命名规则 

2.2、常用配置项

spring可能因为版本原因部分参数没有,根据实际版本配置

flyway参数名 默认值 描述 spring配置项
enabled true 是否开启flyway spring.flyway.enabled=true
baselineOnMigrate false 开启基线迁移 spring.flyway.baseline-on-migrate=false
baselineVersion 1 基线版本 spring.flyway.baseline-version=1
batch false 批量执行sql语句,提升插入、更新和删除语句效率 spring.flyway.batch=false
callbacks db/callback 指定java的回调,值为包的全限定名
cleanDisabled true 执行前清除数据库中的表,生产环境务必设置成true spring.flyway.clean-disabled=fasle
cleanOnValidationError false 脚本校验发生错误时(修改已执行的脚本),是否自动clean,生产环境务必禁用 spring.flyway.clean-on-validation-error=false
executeInTransaction true sql是否在事务中执行
outOfOrder false 是否允许不按版本号顺序执行,多个人员同时开发下有用 spring.flyway.out-of-order=false
placeholderReplacement true 是否替换占位符,默认占位符形如${xxx} spring.flyway.placeholder-replacement=false
lockRetryCount 50 迁移开始,flyway尝试获取锁,防止多实例并行执行,获取锁失败,则每15秒重试一次,直到重试次数则放弃迁移(flyway采用数据库排它锁实现) spring.flyway.lock-retry-count=50

示例截图:

更多详细的配置项使用参考官方文档地址:Flyway配置项

3、项目集成Flyway

3.1、依赖引入

基于SpringBoot搭建的项目,如下引入即可,会自动引入关联的版本

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

3.2、相关配置项

配置项可以使用spring的配置文件设置,也可以通过Java代码进行设置

3.2.1、Spring中的配置项

spring:flyway: #数据库存在表时,自动使用设置的基线版本(baseline-version),数据库不存在表时,即使设置了,也会从第一个版本开始执行,默认值为falsebaseline-on-migrate: true#基线版本号,baseline-on-migrate为true时小于等于此版本号的脚本不会执行,默认值为1baseline-version: 1.0.0#设置为false会删除指定schema下所有的表,生产环境务必禁用,spring中该参数默认是false,需要手动设置为trueclean-disabled: true#sql脚本存放位置,允许设置多个location,用英文逗号分割,默认值为classpath:db/migrationlocations: classpath:db/migration,classpath:db/callback#是否替换sql脚本中的占位符,占位符默认是${xxx},默认是替换,如果不需要替换,可以设置为falseplaceholder-replacement: false

更多详细的配置,可以参考上面的官方配置文档,或者根据spring的相关参数提示查看。

3.2.2、Java代码实现

下面给出简单的代码示例(以下代码未经过测试,仅供参考):

@Configuration
public class FlywayConfig {@Autowiredprivate DataSource dataSource;@PostConstructpublic void config() {Flyway flyway = Flyway.configure().dataSource(dataSource).baselineOnMigrate(true).locations("db/migration").baselineVersion("1.0.1").cleanDisabled(true).load();flyway.migrate();}
}

详细的相关的java方法调用参考:Flyway-Java文档

3.3、sql脚本存放位置

通过配置项spring.flyway.locations设置,默认值是classpath:db/migration,也可以参数配置指定位置(截图就是自定义的location位置),多个location通过英文逗号分割开

3.4、回调操作

flyway支持在任意操作(migrate、undo、clean、info、validate、baseline和repair)的前后,执行自定义的代码或者脚本,下面给一份支持的事件表格,官网文档:Flyway callback

Migrate Execution
beforeMigrate Before Migrate runs
beforeRepeatables Before all repeatable migrations during Migrate
beforeEachMigrate Before every single migration during Migrate
beforeEachMigrateStatement Before every single statement of a migration during Migrate
afterEachMigrateStatement After every single successful statement of a migration during Migrate
afterEachMigrateStatementError After every single failed statement of a migration during Migrate
afterEachMigrate After every single successful migration during Migrate
afterEachMigrateError After every single failed migration during Migrate
afterMigrate After successful Migrate runs
afterMigrateApplied After successful Migrate runs where at least one migration has been applied
afterVersioned After all versioned migrations during Migrate
afterMigrateError After failed Migrate runs
beforeUndo Before Undo runs
beforeEachUndo Before every single migration during Undo
beforeEachUndoStatement Before every single statement of a migration during Undo
afterEachUndoStatement After every single successful statement of a migration during Undo
afterEachUndoStatementError After every single failed statement of a migration during Undo
afterEachUndo After every single successful migration during Undo
afterEachUndoError After every single failed migration during Undo
afterUndo After successful Undo runs
afterUndoError After failed Undo runs
beforeClean Before Clean runs
afterClean After successful Clean runs
afterCleanError After failed Clean runs
beforeInfo Before Info runs
afterInfo After successful Info runs
afterInfoError After failed Info runs
beforeValidate Before Validate runs
afterValidate After successful Validate runs
afterValidateError After failed Validate runs
beforeBaseline Before Baseline runs
afterBaseline After successful Baseline runs
afterBaselineError After failed Baseline runs
beforeRepair Before Repair runs
afterRepair After successful Repair runs
afterRepairError After failed Repair runs
createSchema Before automatically creating non-existent schemas
beforeConnect Before Flyway connects to the database
  • 实现Flyway的callback接口

flyway默认是会执行db/callback下的sql脚本,如果不想执行,可以使用参数 flyway.skipDefaultCallbacks跳过,设置为true即可,sping里面的配置参数为:

spring: flyway: skip-default-callbacks: true
package org.example.config;import org.flywaydb.core.api.callback.Callback;
import org.flywaydb.core.api.callback.Context;
import org.flywaydb.core.api.callback.Event;
import org.flywaydb.core.api.configuration.Configuration;import java.sql.Connection;public class MyFlywayCallback implements Callback {@Overridepublic boolean supports(Event event, Context context) {return false;}@Overridepublic boolean canHandleInTransaction(Event event, Context context) {return false;}/*** 根据各种事件来自定义处理逻辑*/@Overridepublic void handle(Event event, Context context) {//迁移发生错误后的事件if (Event.AFTER_MIGRATE_ERROR == event) {Configuration configuration = context.getConfiguration();Connection connection = context.getConnection();//自定义进行逻辑处理,并操作数据库} else if (Event.CREATE_SCHEMA == event) {//}}@Overridepublic String getCallbackName() {return null;}
}
  • 设置回调的sql文件

设置回调的sql文件的默认存放位置在db/callback下,sql文件的名称和上面表格的Migrate函数回调名称命名(beforeMigrate.sql,beforeRepeatables.sql)即可。

例如:清除执行失败的记录,便于我们开发,当sql脚本执行失败后,不需要手动去表中删除失败的记录,可以通过设置错误回调sql,自动删除失败的记录

sql脚本中的语句如下,此处可能因为配置的表名和字段不一样,有一些差别,视实际的表字段编写语句。

delete from flyway_schema_history where success=false;

注:执行失败的记录,如果不清除掉,修改脚本后再次执行就会报错,也可以使用repair指令来修复,repair有以下几个功能:

  1. 移除表中执行记录为失败的数据
  2. 将已经执行的迁移和可执行的迁移之间的校验和、描述和类型重新对齐
  3. 删除数据表中,在当前location文件夹下不存在的迁移记录
  • 低版本的flyway是实现FlywayCallback接口(不建议使用),截图如下:

3.5、兼容历史项目

实际环境中不可能总是在项目开始就引入flyway,也可能是在项目中间引入flyway,针对数据库已经存在表和数据的情况,应该如何处理,这里flyway也有对应的解决方案,通过引入基线版本的方式来兼容历史数据,通过两个参数进行控制,baselineOnMigrate=true 和 flyway.baselineVersion=0.0,前者用于开启基线迁移,后者指定基线的版本,如何理解?

假设目前的项目,已经存在了很多表,我们需要在当前的基础上使用flyway,第一步是制作初始化的sql脚本,即将当前项目已有的数据库表结构,整体导出为一个sql文件,命名为V1.0__init.sql,后续如果再新增表或者修改表,则可以按版本号递增命名,如V1.1__add_new_table.sql。

在开启基线迁移且基线版本设置为1.0的情况下:

  1. 如果是历史环境升级,flyway会判断数据库非空,则会跳过1.0版本的脚本,从1.1开始执行,避免了1.0脚本执行报错的情况,
  2. 如果是新部署的环境,flyway判断数据库为空,会直接从1.0版本顺序往下执行,此时设置的基线迁移和基线版本不会生效(数据库为空的情况下)
  3. 如果存在很多个不同的环境,每个环境的数据库表都不一致,例如A环境有1,2两张表,B环境有1,2,3张表,C环境是1,2,4,但是最新的数据库表为1,2,3,4,此时如果你将1,2,3,4定义为基线版本,那么ABC环境就需要手动执行缺失的脚本,将其补齐到和基线脚本相同的位置

编写的脚本可以被反复执行,例如建表前判断表和字段时判断是否已经存在,insert数据时使用replace等等,但是不同的数据库支持的功能不一样,尽量写出可以重复执行的脚本,那么就可以减少手动补齐的工作量。

3.6、程序启动问题

程序的启动有时候会依赖于数据库的表,如果表没有创建完成,此时程序启动会出错,因此需要确保我们的程序中的数据库操作,在flyway处理完成后再执行,此处推荐两种方式避免这个问题:

第一是向spring注入bean时,代码里面会操作数据库,可以使用如下注解,等待flyway初始化完成后再进行

第二是使用ApplicationRunner或者CommandLineRunner的方式来进行程序初始化操作

3.7、其他问题

1、基线迁移有时会失效?

基线迁移生效的必须满足几个前提:

  • 开启了基线迁移配置,设置了基线版本
  • flyway运行前,数据库中已经存在其他表
  • flyway_schema_history表不存在(首次执行)

所以需要确保代码中其他创建表的操作的顺序,有的生成表在flyway之前或者之后,会导致执行不满足预期

遇到一个问题,调试基线版本功能,flyway之前已经执行过,但是本次清除了表中的所有数据,启动程序发现基线迁移未生效,根据测试发现,就是不满足上面第三个条件,必须要把flyway_schema_history删除才可以

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/774641.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IO体系

IO,即in和out,也就是输入和输出,指应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。 Java 中是通过流处理IO 的,那么什么是流? 流(Stream),是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。 当程序…

Java JDK8新增时间类

生成的都是不可变对象,改动内容会存入新对象,原对象不变

C#使用Spire.Doc打印时候出现红字Evaluation Warning: The document was created with Spire.Doc for .NET.

问题 1.使用Spire.Doc 功能输出word 时,文件头部出现 解决方案 2.使用免费版的包 FreeSpire.Docdotnet add package FreeSpire.Doc

记一次 信息服务平台 “维权”

记录一次找寻网站售后客服的“小苦难”记录一次 爱合租 网站找寻客服的小艰辛太长不看: 最后借助于 风鸟 查询企业名, 从而联系上了相关人员 背景 涉及平台是一个软件服务"发车"平台, 简单来说就是为其用户提供各家软件会员合订的一个平台 我自己在上面当了一段时间的…

Kubernetes externalIPs 类型服务

背景:在k8s中用到的比较到多的是ClusterIP和NodePort类型的service,externalIPs则很少使用。我们现在了解一下其用法和它的一些优缺点。 官方定义: 如果有路由到一个或多个集群节点的外部 IP,则可以在这些 IP 上公开 Kubernetes 服务。在服务端口上使用外部 IP(作为目标 I…

智慧校园:定义未来教育的蓝图

智慧校园管理平台,是集信息技术与教育教学深度融合的产物,它以大数据、云计算、物联网、人工智能等前沿技术为核心,构建了一个高度信息化、智能化的校园生态系统。这一平台不仅仅是技术的堆砌,更是教育理念与管理模式的全面升级,旨在打造安全、高效、个性化的学习环境,促…

Jmeter简单接口测试

说明:Jmeter和对应jdk的下载及安装说明,在笔者的其他文章里面可以找到,这里不再赘述,笔者使用的是Jmeter5.3和jdk1.8 1.在开始讲之前,先讲一下如何看接口信息,一般情况下,开发会有接口文档,如果没有的话,可以在谷歌浏览器按F12抓包,看接口的信息,包括服务器地址,端…

前端部署工具

前端部署工具 用electron写了一个通用的前端部署工具,支持SSH的理论上都可以使用该工具,使用nodejs实现模拟登陆以及上传文件到服务器并解压 链接: https://pan.baidu.com/s/1rGnAO4X_xfv90UecuAMFkA?pwd=2mte 提取码: 2mte本文来自博客园,作者:小万子呀,转载请注明原文链…

Go--创建以当前时间命名的excel文件

下载依赖包go get -u github.com/xuri/excelize/v2 代码package mainimport ("fmt""github.com/xuri/excelize/v2""time" )func main() {// 获取当前时间now := time.Now()filename := fmt.Sprintf("%s.xlsx", now.Format("2006…

今日迷惑行为大赏

多测的意义何在?是好兄弟就 一起不换行我本地真的过编了1 我本地真的过编了2本地-LOCAL确实能过编,但你交上去呢

hostapd 配置文件示例

b模式:2.4G 20MHz# 接口和驱动程序设置 interface=wlan0 driver=nl80211 ctrl_interface=/var/run/hostapd​# 基本网络设置 ssid=TestAP hw_mode=b channel=11​# WPA身份验证设置 wpa=2 wpa_key_mgmt=WPA-PSK wpa_passphrase=12345678​# 加密算法设置 wpa_pairwise=CCMP rs…

SQL实战从在职到离职(1) 如何处理连续查询

书接上回,最近离职在家了实在无聊,除了看看考研的书,打打dnf手游,也就只能写写代码,结果昨晚挂在某平台的一个技术出售有人下单了,大概业务是需要帮忙辅导一些面试需要用到的SQL。 回想了下,在该平台接单SQL也超过3w元了,考察的也就是那几大类,我准备开一个新的专题,…