拿到一个新需求,要给一个开发中的项目集成flyway进行数据库版本控制,对于flyway没有什么了解,就得先搭建一个demo练习一下,先知道怎么用,再集成到老项目上;

一、新建SpringBoot项目

1、idea使用Spring Initializr创建新项目

flyway 使用 Java 代码 spring flyway_flyway 使用 Java 代码

2、对新项目进行设置,设置后点击next

flyway 使用 Java 代码 spring flyway_flyway 使用 Java 代码_02

 3、按图选择组件,选择后点击next

flyway 使用 Java 代码 spring flyway_数据库_03

4、选择目录和项目名,然后点击完成

flyway 使用 Java 代码 spring flyway_flyway 使用 Java 代码_04

5、设置maven,新项目的maven都是idea自带的那个,大家一般用的都不是idea自带的,要改成自己配置的。

flyway 使用 Java 代码 spring flyway_sql_05

6、新建的项目,里面的文件可能是红色的,这是由于idea自动给项目添加了版本控制

flyway 使用 Java 代码 spring flyway_spring_06

 settings里面找到version control 找到当前项目或目录 将VCS设置成<none>

flyway 使用 Java 代码 spring flyway_数据库_07

7、springboot项目测试,建立controller层及文件

flyway 使用 Java 代码 spring flyway_flyway 使用 Java 代码_08

IndexController.java

package com.voiceking.flywaydemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class IndexController {

    @RequestMapping("/")
    @ResponseBody
    public String index(){
        return "hello world";
    }
}

8、启动启动类文件 ,访问8080端口(默认8080)

flyway 使用 Java 代码 spring flyway_数据库_09

 

flyway 使用 Java 代码 spring flyway_java_10

至此,SpringBoot项目搭建完成。

 二、测试数据源(使用mybatis-plus)

项目搭建完成后,肯定要先测一下crud的,我要处理的这个项目的持久层框架用的是mybatis-plus,所以这里我也选用mybatis-plus,用其他的框架或者原始JDBC都没什么区别,主要就是为了确定可以连接到数据库。

1、造假数据,建表,插入数据(这个就不提供insert语句了 都是简单数据 手动插一下就ok)

flyway 使用 Java 代码 spring flyway_java_11

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2、maven添加依赖

flyway 使用 Java 代码 spring flyway_flyway 使用 Java 代码_12

<!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.2</version>
        </dependency>

如果密码包含特殊字符,要给整个密码用单引号包裹,不然会报错,此时先停用flyway,不然会报错,测试不了。

flyway 使用 Java 代码 spring flyway_java_13

# 数据源设置
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://ip:port/flyway_test
    username: user
    password: ****

  flyway:
    enabled: false

 4、建立实体类(主要就是这三个属性,加上get\set 构造方法 toString 不贴代码了)

flyway 使用 Java 代码 spring flyway_sql_14

5、建立mapper接口,如果BaseMapper找不到就重新导入一下maven依赖

flyway 使用 Java 代码 spring flyway_sql_15

package com.voiceking.flywaydemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.voiceking.flywaydemo.entity.User;

public interface UserMapper extends BaseMapper<User> {
}

 6、在启动类上添加MapperScan注解,value值是mapper包的路径

flyway 使用 Java 代码 spring flyway_sql_16

7、在idea自动生成的测试类中添加代码,测试数据库是否连通,测试类中的userMapper可能报红,注意这是不影响运行的,可以忽略

flyway 使用 Java 代码 spring flyway_sql_17

@Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
    }

 8、运行测试类,可以看到如下输出

flyway 使用 Java 代码 spring flyway_sql_18

9、可能会有的坑;

至此,数据源测试也OK了,中间有几个地方可能会有问题,提醒一下

1)启动的是测试类的方法,不是Application启动类的main方法

2)启动类上MapperScan的包名千万不能错,建议复制,不要手敲

3)配置中要通过flyway.enable = false 先关掉flyway,不然这个测试跑不了

4)配置中url记得是jdbc:开头,别直接复制数据库的地址就粘过来

5)导包找不到、注解找不到等问题都重新导入一下maven基本都可以解决

 三、测试集成flyway

1、之前我们将flyway.enable 设置为false,先将其设置为true,由于上一步的操作,数据库中已经存在了一些表和初始化数据,此时启动SpringBoot时 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.

通过报错,我能能看出来,需要在配置文件中修改一个属性:

flyway.baseline-on-migrate = true

查阅资料发现这个告诉 Flyway 在执行 sql 脚本之前,数据库是非空状态,以这个非空状态作为一个基线。

之前配置了spring的数据源,所以flyway会直接使用,不需要再配置flyway的url、user、password

配置文件如下: 

# 数据源设置
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://rm-2ze40g8r4l952gp64yo.mysql.rds.aliyuncs.com:3306/flyway_test
    username: voiceking
    password: '@zjh13614581909'

  flyway:
    enabled: true
    baseline-on-migrate: true

2、此时运行还是失败的,因为flyway需要在resource/db/migration中存在文件才能执行,否则会报错,文件命名的格式是:

V开头(必须是大写),后接版本号(数字,小数),再接两个下划线,接上自定义的名字,文件后缀名是”.sql”,例如:V1.0.1__init-create-account.sql

R开头(必须是大写),后接两个下划线,接上自定义的名字,文件后缀名是”.sql”,例如:R__insert_data_to_account.sql

当SpringBoot启动时,这些文件会自动运行。以V开头的文件只会被使用一次,以R开头的文件,在内容发生变化后,再次启动SpringBoot就会被再次执行。

我们先建立一个V1.0.1__init-create-account.sql进行测试;

flyway 使用 Java 代码 spring flyway_sql_19

-- 创建账户表
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userid` int(11) DEFAULT NULL,
  `money` double DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3、完成上述操作后,执行Application启动类,可以发现启动成功,数据库中多出了两张表

flyway 使用 Java 代码 spring flyway_sql_20

flyway_schema_history是flyway自动生成的,用来进行版本控制的表

account 表是根据V1.0.1__init-create-account.sql文件生成的表

执行到这一步,flyway的初步配置已经成功了

4、测试一下R开头的可重复执行文件;

新建一个文件:R__insert_data_to_account.sql

flyway 使用 Java 代码 spring flyway_flyway 使用 Java 代码_21

内容如下 :

-- 插入一条数据
insert into account (userid,money) values (1,800.0);

 执行后控制台相关输出为:

flyway 使用 Java 代码 spring flyway_sql_22

数据库中的数据也插入了

flyway 使用 Java 代码 spring flyway_flyway 使用 Java 代码_23

注意 R开头的文件不能加版本号,加版本号会导致无法执行。

此时反复重启SpringBoot,R__insert_data_to_account.sql也不会再次执行,只有当R__insert_data_to_account.sql文件内容发生变化时,文件才会再次执行;

flyway 使用 Java 代码 spring flyway_java_24

flyway 使用 Java 代码 spring flyway_flyway 使用 Java 代码_25

5、对 V1.0.1__init-create-account.sql 做修改,修改后再次重启SpringBoot,则会报错,直到将其修改成修改前的内容,方可正常执行

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.exception.FlywayValidateException: Validate failed: Migrations have failed validation

关于flyway执行的一些重点

1、V开头的文件会比R开头的文件先执行,执行出错后,后续的文件就不会执行。

2、V开头的文件,会比较版本,按照版本排序后,按顺序执行

     R开头的文件,会比较文件名,按文件名排序后,按顺序执行

3、一个文件是否执行过是通过表flyway_schema_history中的记录来判断的

四、flyway配置文件

flyway:
  #是否开启flyway
  enabled: true

  #是否开启基线迁移 当数据库已有数据或表时,设置为true才可以执行,以已存在的数据作为一个基础版本,基础版本的版本号通过baseline-version设置
  baseline-on-migrate: true

  #指定基线对应的版本号,默认是1
  baseline-version: 0

  #可以在DataSource的url中直接指定当前要操作的库,也可以只配置到端口号,通过schemas指定要操作的模式(在mysql中,模式就是数据库),如果指定的模式不存在,flyway会自动创建;如test-777数据库在mysql中不存在,则会自动创建test-777数据库
  schemas: test-777

五、已有项目集成flyway

需要注意老项目的SpringBoot版本

我的SpringBoot版本是 2.2.4.RELEASE,这个版本对应的flyway版本是6.0.8

如果版本不符的话,启动是必报错的

六、flyway搭建源码测试平台

flyway是开源的,如果需要对flyway进行自定义的调整,或者深入研究源码,可以考虑搭建测试平台进行测试,这个方案已经有大佬写好了,我在这里贴一个连接,直接去原贴看就OK了