flyway实现java 自动升级SQL脚本的问题及解决方法

为什么要用Flyway

在日常开发中,我们经常会遇到下面的问题:

  1. 自己写的SQL忘了在所有环境执行;
  2. 别人写的SQL我们不能确定是否都在所有环境执行过了;
  3. 有人修改了已经执行过的SQL,期望再次执行;
  4. 需要新增环境做数据迁移;
  5. 每次发版需要手动控制先发DB版本,再发布应用版本;

其它场景...

由于项目需求的变化,或者前期设计缺陷,导致在后期需要修改数据库,这应该是一个比较常见的事情,如果项目还没上线,你可能把表删除了重新创建,但是如果项目已经上线了,就不能这样简单粗暴了,每次运维部署项目,还得手动执行一遍SQL文件。我们需要通过 SQL 脚本在已有数据表的基础上进行升级。

有了flyway,这些问题都能得到很好的解决。

使用了 Flyway 之后,如果再想进行数据库版本升级,就不用该以前的数据库脚本了,直接创建新的数据库脚本,项目在启动时检测了有新的更高版本的脚本,就会自动执行,这样,在和其他同事配合工作时,也会方便很多。因为正常我们都是从 Git 上拉代码下来,不拉数据库脚本,这样要是有人更新了数据库,其他同事不一定能够收到最新的通知,使用了 Flyway 就可以有效避免这个问题了。

所有的脚本,一旦执行了,就会在 flyway_schema_history 表中有记录,如果你不小心搞错了,可以手动从 flyway_schema_history 表中删除记录,然后修改 SQL 脚本后再重新启动(生产环境不建议)。

Flyway是如何工作的

Flyway工作流程如下:

1、项目启动,应用程序完成数据库连接池的建立后,Flyway自动运行。

2、初次使用时,Flyway会创建一个flyway_schema_history表,用于记录sql执行记录。

3、Flyway会扫描项目指定路径下(默认是classpath:db/migration)的所有sql脚本,与flyway_schema_history表脚本记录进行比对。如果数据库记录执行过的脚本记录,与项目中的sql脚本不一致,Flyway会报错并停止项目执行。

4、如果校验通过,则根据表中的sql记录最大版本号,忽略所有版本号不大于该版本的脚本。再按照版本号从小到大,逐个执行其余脚本。

项目中使用Flyway

首先,在pom文件中引入flyway的核心依赖包:

1、引入核心依赖包:

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

这里使用5.2.4版本。经测试7.0.0版本与目前我们使用的springboot版本有冲突,会导致flyway不执行。因此我们尽量不要使用高版本的flyway。

2、配置文件:

简单配置一个属性即可使用

# flyway 配置
 spring:
   flyway:
     # 启用或禁用 flyway
     enabled: true
     # flyway 的 clean 命令会删除指定 schema 下的所有 table, 生产务必禁掉。这个默认值是 false 理论上作为默认配置是不科学的。
     clean-disabled: true
     # SQL 脚本的目录,多个路径使用逗号分隔 默认值 classpath:db/migration
     locations: classpath:db/migration
     #  metadata 版本控制信息表 默认 flyway_schema_history
     table: flyway_schema_history
     # 如果没有 flyway_schema_history 这个 metadata 表, 在执行 flyway migrate 命令之前, 必须先执行 flyway baseline 命令
     # 设置为 true 后 flyway 将在需要 baseline 的时候, 自动执行一次 baseline。
     baseline-on-migrate: true
     # 指定 baseline 的版本号,默认值为 1, 低于该版本号的 SQL 文件, migrate 时会被忽略
     baseline-version: 1
     # 字符编码 默认 UTF-8
     encoding: UTF-8
     # 是否允许不按顺序迁移 开发建议 true  生产建议 false
     out-of-order: false
     # 需要 flyway 管控的 schema list,这里我们配置为flyway  缺省的话, 使用spring.datasource.url 配置的那个 schema,
     # 可以指定多个schema, 但仅会在第一个schema下建立 metadata 表, 也仅在第一个schema应用migration sql 脚本.
     # 但flyway Clean 命令会依次在这些schema下都执行一遍. 所以 确保生产 spring.flyway.clean-disabled 为 true
     schemas: flyway
     # 执行迁移时是否自动调用验证   当你的 版本不符合逻辑 比如 你先执行了 DML 而没有 对应的DDL 会抛出异常
     validate-on-migrate: true

flyway的properties配置清单(属性未测试):

# 对执行迁移时基准版本的描述.
 flyway.baseline-description
 #当迁移时发现目标schema非空,而且带有没有元数据的表时,是否自动执行基准迁移,默认false.
 flyway.baseline-on-migrate =false
 #开始执行基准迁移时对现有的schema的版本打标签,默认值为1.
 flyway.baseline-version=1
 #检查迁移脚本的位置是否存在,默认false.
 flyway.check-location=false
 #当发现校验错误时是否自动调用clean,默认false.
 flyway.clean-on-validation-error=false
 #是否开启flywary,默认true.
 flyway.enabled=true
 #设置迁移时的编码,默认UTF-8.
 flyway.encoding
 #当读取元数据表时是否忽略错误的迁移,默认false.
 flyway.ignore-failed-future-migration
 #当初始化好连接时要执行的SQL.
 flyway.init-sqls
 #迁移脚本的位置,默认db/migration.
 flyway.locations
 #是否允许无序的迁移,默认false.
 flyway.out-of-order
 #目标数据库的密码.
 flyway.password
 #设置每个placeholder的前缀,默认${.
 flyway.placeholder-prefix
 #是否要被替换,默认true.
 flyway.placeholder-replacementplaceholders
 #设置每个placeholder的后缀,默认}.
 flyway.placeholder-suffix
 #设置placeholder的value
 flyway.placeholders.[placeholder name]
 #设定需要flywary迁移的schema,大小写敏感,默认为连接默认的schema.
 flyway.schemas
 #迁移文件的前缀,默认为V.
 flyway.sql-migration-prefix
 #迁移脚本的文件名分隔符,默认__
 flyway.sql-migration-separator
 #迁移脚本的后缀,默认为.sql
 flyway.sql-migration-suffix
 #使用的元数据表名,默认为schema_version
 flyway.tableflyway
 #迁移时使用的目标版本,默认为latest version
 flyway.target
 #迁移时使用的JDBC URL,如果没有指定的话,将使用配置的主数据源
 flyway.url
 #迁移数据库的用户名
 flyway.user
 #迁移时是否校验,默认为true
 flyway.validate-on-migrate

flyway的yml配置清单(已测试,没问题,推荐使用yml格式的配置文件)

# flyway 配置
 spring:
   flyway:
     # 启用或禁用 flyway
     enabled: true
     # flyway 的 clean 命令会删除指定 schema 下的所有 table, 生产务必禁掉。这个默认值是 false 理论上作为默认配置是不科学的。
     clean-disabled: true
     # SQL 脚本的目录,多个路径使用逗号分隔 默认值 classpath:db/migration
     locations: classpath:db/migration
     #  metadata 版本控制信息表 默认 flyway_schema_history
     table: flyway_schema_history
     # 如果没有 flyway_schema_history 这个 metadata 表, 在执行 flyway migrate 命令之前, 必须先执行 flyway baseline 命令
     # 设置为 true 后 flyway 将在需要 baseline 的时候, 自动执行一次 baseline。
     baseline-on-migrate: true
     # 指定 baseline 的版本号,默认值为 1, 低于该版本号的 SQL 文件, migrate 时会被忽略
     baseline-version: 1
     # 字符编码 默认 UTF-8
     encoding: UTF-8
     # 是否允许不按顺序迁移 开发建议 true  生产建议 false
     out-of-order: false
     # 需要 flyway 管控的 schema list,这里我们配置为flyway  缺省的话, 使用spring.datasource.url 配置的那个 schema,
     # 可以指定多个schema, 但仅会在第一个schema下建立 metadata 表, 也仅在第一个schema应用migration sql 脚本.
     # 但flyway Clean 命令会依次在这些schema下都执行一遍. 所以 确保生产 spring.flyway.clean-disabled 为 true
     schemas: flyway
     # 执行迁移时是否自动调用验证   当你的 版本不符合逻辑 比如 你先执行了 DML 而没有 对应的DDL 会抛出异常
     validate-on-migrate: true

spring.flyway.clean-disabled:这个属性非常关键,它表示是否要清除已有库下的表,如果执行的脚本是 V1__xxx.sql,那么会先清除已有库下的表,然后再执行脚本,这在开发环境下还挺方便,但是在生产环境下就要命了,而且它默认就是要清除,生产环境一定要自己配置设置为 true。

3、创建db/migration

因为flyway默认是读取resources/db/migration下的文件夹,如果我们需要修改这个路径,可以在配置文件中实现

4、编写sql文件

此处的SQL语句命名需要遵从一定的规范,否则运行的时候flyway会报错。命名规则主要有两种:

  1. 仅需要被执行一次的SQL命名以大写的"V"开头,后面跟上"0~9"数字的组合,数字之间可以用“.”或者下划线"_"分割开,然后再以两个下划线 __ 分割,其后跟文件名称,最后以.sql结尾。比如,V20210707__create_user.sqlV20210707__add_user.sql
  2. 可重复运行的SQL,则以大写的“R”开头,后面再以两个下划线分割,其后跟文件名称,最后以.sql结尾。。比如,R__truncate_user_dml.sql

其中,V开头的SQL执行优先级要比R开头的SQL优先级高。

V:固定大写

20210707.01:20210707是日期,后面用.01代表序号

因为flyway的执行是有个顺序的,比如你执行了V2021__create_user,又执行V2020_update_user。就会报错,原因就是2020<2021。所以我们要保证序号是依次增大。

Flyway 是如何比较两个 SQL 文件的先后顺序呢?它采用 采用左对齐原则, 缺位用 0 代替 。举几个例子:

1.0.1.1 比 1.0.1 版本高。
 ​
 1.0.10 比 1.0.9.4 版本高。
 ​
 1.0.10 和 1.0.010 版本号一样高, 每个版本号部分的前导 0 会被忽略。

__:这个是两个 _

create_user是一个简单的sql描述

.sql:以.sql结尾的文件后缀是约定

我们只要在数据库中创建flyway这个数据库,启动项目,flyway就会执行sql文件,创建user表,并且会自动生成一个flyway_schema_history表

从这段启动日志中,我们可以看到 Flyway 的执行信息,数据库脚本的执行执行,同时这里还说了,Flyway 还给创建了一个 flyway_schema_history 表,这个表用来记录数据库的更新历史。

flyway_schema_history里面会去记录sql文件的执行记录,每次启动项目,都会去flyway_schema_history看sql是否执行过,如果没有执行过,说明这个sql是新的sql,就会自动执行,并且记录到表里面。

有了这条记录,下次再启动项目,V20210707.01、V20210707.02、V20210708.01 这个三个脚本文件就不会执行了,因为系统知道这个脚本已经执行过了,如果你还想让这些脚本再执行一遍,需要手动删除 flyway_schema_history 表中的对应记录,那么项目启动时,这个脚本就会被执行了。

R开头的文件和V开头的文件略有不同,R开头的文件只要发送修改,都会执行一遍。V开头的文件如果执行过一般,在发送修改,就会报错。为了控制版本,我们尽量使用V开头的文件,这样我们也可以很清楚的看到每个版本中的sql文件。

常见问题

问题1

flyway遇到的问题Caused by: java.lang.ClassNotFoundException: org.flywaydb.core.api.callback.FlywayCallbac

原因:springboot版本和flyway版本不一致,一般是flyway版本过高。

解决办法:将flyway的版本降到5.2.4就ok了

问题2

springboot 整合flyway 但是不生效,flyway不会自动执行sql

原因:如上

原因2:项目中没有配置数据库,没有引入sq依赖或者配置

解决办法:如上

解决办法2:引入sql依赖,在yml文件中配置sql信息

问题3

flyway出错 FlywayException: Validate failed: Detected failed migration to version

原因:sql脚本和数据库中有冲突,需要检查sql脚本哪里错了。简单的说就是V开头的sql文件,已经执行过了,在 flyway_schema_history 表里面有这个数据,但是你又改动了sql文件,导致再次执行的时候报错。

解决办法:新建一个sql文件,不要修改原来以V开头的文件或者在flyway_schema_history表中找到文件相关执行记录,删掉重新执行。

作者:天下没有收费的bug出处:https://www.cnblogs.com/LoveBB/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。

到此这篇关于flyway实现java 自动升级SQL脚本的问题及解决方法的文章就介绍到这了,更多相关java 自动升级SQL脚本内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java调用SQL脚本执行常用的方法示例

    前言 大家都知道,在Java中调用SQL脚本的方式有多种,在这里只记录一种自己常用的方式,个人觉得挺实用方便的. 运用ScriptRunner这个类. ScriptRunner类用于执行SQL语句,例如创建数据库schema,或传入缺省或测试数据库等等. 示例代码如下: import org.apache.ibatis.io.Resources; import org.apache.ibatis.jdbc.ScriptRunner; import java.io.IOException; imp

  • Java执行SQL脚本文件到数据库详解

    本文实例为大家分享了Java执行SQL脚本文件到数据库的具体方式,供大家参考,具体内容如下 方式一:直接读取SQL脚本文件的内容,然后传递到SQL中. 代码:RunSqlService: @Autowired private RunSqlDao runSqlDao; /** * 读取文件内容到SQL中执行 * @param sqlPath SQL文件的路径:如:D:/TestProject/web/sql/脚本.Sql */ public void runSqlByReadFileContent

  • java如何导出insert语句并生成sql脚本

    看代码吧~ package com.mtpc.admin.controller.exportSql; import ch.qos.logback.classic.db.names.ColumnName; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.sql.*; import java.util.Array

  • flyway实现java 自动升级SQL脚本的问题及解决方法

    为什么要用Flyway 在日常开发中,我们经常会遇到下面的问题: 自己写的SQL忘了在所有环境执行: 别人写的SQL我们不能确定是否都在所有环境执行过了: 有人修改了已经执行过的SQL,期望再次执行: 需要新增环境做数据迁移: 每次发版需要手动控制先发DB版本,再发布应用版本; 其它场景... 由于项目需求的变化,或者前期设计缺陷,导致在后期需要修改数据库,这应该是一个比较常见的事情,如果项目还没上线,你可能把表删除了重新创建,但是如果项目已经上线了,就不能这样简单粗暴了,每次运维部署项目,还得

  • Spring Boot中自动执行sql脚本的实现

    说明:所有的代码基于SpringBoot 2.0.3版本 背景 在应用程序启动后,可以自动执行建库.建表等SQL脚本.下文中以要自动化执行people.sql脚本为例说明,脚本在SpringBoot工程中的路径为:classpath:people.sql,脚本的具体内容如下: CREATE TABLE IF NOT EXISTS people( persion_id BIGINT NOT NULL AUTO_INCREMENT, first_name VARCHAR(20), last_name

  • SpringBoot启动时自动执行sql脚本的方法步骤

    需要配置项目下的yml文件: 在文件下加如如下配置: data: classpath:code-generator-data.sql initialization-mode: always spring.datasource.initialization-mode: 初始化模式(springboot2.0),其中有三个值: always为始终执行初始化 embedded只初始化内存数据库(默认值),如h2等 never为不执行初始化 spring.datasource.data: 数据初始化,默

  • Spring Boot中自动执行sql脚本的方法实例

    目录 背景 实现核心 实现方法 注意 总结 说明:所有的代码基于SpringBoot 2.0.3版本 背景 在应用程序启动后,可以自动执行建库.建表等SQL脚本.下文中以要自动化执行people.sql脚本为例说明,脚本在SpringBoot工程中的路径为:classpath:people.sql,脚本的具体内容如下: CREATE TABLE IF NOT EXISTS people( persion_id BIGINT NOT NULL AUTO_INCREMENT, first_name

  • Mac OS X 下 IntelliJ IDEA、jEdit 等 Java 程序中文标点输入无效的完美解决方法

    Mac OS X 下基于 Java 的程序(如 IntelliJ IDEA.jEdit 等)会出现中文标点输入无效的问题,在中文输入法状态,可以输入中文字,但输入中文标点最后上去的是英文标点.查阅了相关资料,原来这是 Java 自己的 bug.从 Java 8u51 版本开始就出现了这个 bug,一直到现在最新的 Java 8u72 仍然如此,但是老版本 Java 8u45 是没有这个问题的.所以,可以采取变通的方法,在 Mac OS X 上同时装一个老版本的 JDK 8u45,不会影响已经安装

  • 使用AJAX实现UTF8编码表单提交到GBK编码脚本无乱码的解决方法

    1.表单项目1 GBK编码格式 用户采集单是GBK编码,通过GET/POST提交上来的数据也是GBK格式,无需做任何处理 测试源码 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="GB2312"> </head> <body> <li><a href="gbkForm.html" rel=&

  • Java内存各部分OOM出现原因及解决方法(必看)

    一,jvm内存区域 1,程序计数器 一块很小的内存空间,作用是当前线程所执行的字节码的行号指示器. 2,java栈 与程序计数器一样,java栈(虚拟机栈)也是线程私有的,其生命周期与线程相同.通常存放基本数据类型,对象引用(一个指向对象起始地址的引用指针或一个代表对象的句柄),reeturnAddress类型(指向一条字节码指令的地址) 栈区域有两种异常类型:如果线程请求的栈深度大于虚拟机所允许的深度,将抛StrackOverflowError异常:如果虚拟机栈可以动态扩展(大部分虚拟机都可动

  • Java基于Runtime调用外部程序出现阻塞的解决方法

    本文实例讲述了Java基于Runtime调用外部程序出现阻塞的解决方法, 是一个很实用的技巧.分享给大家供大家参考.具体分析如下: 有时候在java代码中会调用一些外部程序,比如SwfTools来转换swf.ffmpeg来转换视频等.如果你的代码这样写:Runtime.getRuntime().exec(command),会发现程序一下就执行完毕,而在命令行里要执行一会,是因为java没有等待外部程序的执行完毕,此时就需要使用阻塞,来等待外部程序执行结果: InputStream stderr

  • java中double类型运算结果异常的解决方法

    问题: 对两个double类型的值进行运算,有时会出现结果值异常的问题.比如: System.out.println(19.99+20); System.out.println(1.0-0.66); System.out.println(0.033*100); System.out.println(12.3/100); 输出: 39.989999999999995 0.33999999999999997 3.3000000000000003 0.12300000000000001 解决方法: J

  • windows下上传shell脚本不能运行的解决方法

    windows下上传shell脚本至linux,其格式将为dos.dos模式的shell脚本将不能再linux下正确运行,需要修改文件模式为unix. 1 查看文件模式方法 linux服务器上,用vi编辑器打开shell脚本,随后进入命令行模式,输入"set ff",即可查看文件模式. 查看文件指令: 显示的文件格式: 2 修改文件模式的方法 在命令行模式下,输入"set ff=unix",即可将dos模式修改为unix模式. 修改文件模式命令: 查看修改后的文件模

随机推荐