拿到一个新需求,要给一个开发中的项目集成flyway进行数据库版本控制,对于flyway没有什么了解,就得先搭建一个demo练习一下,先知道怎么用,再集成到老项目上;
一、新建SpringBoot项目
1、idea使用Spring Initializr创建新项目
2、对新项目进行设置,设置后点击next
3、按图选择组件,选择后点击next
4、选择目录和项目名,然后点击完成
5、设置maven,新项目的maven都是idea自带的那个,大家一般用的都不是idea自带的,要改成自己配置的。
6、新建的项目,里面的文件可能是红色的,这是由于idea自动给项目添加了版本控制
settings里面找到version control 找到当前项目或目录 将VCS设置成<none>
7、springboot项目测试,建立controller层及文件
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)
至此,SpringBoot项目搭建完成。
二、测试数据源(使用mybatis-plus)
项目搭建完成后,肯定要先测一下crud的,我要处理的这个项目的持久层框架用的是mybatis-plus,所以这里我也选用mybatis-plus,用其他的框架或者原始JDBC都没什么区别,主要就是为了确定可以连接到数据库。
1、造假数据,建表,插入数据(这个就不提供insert语句了 都是简单数据 手动插一下就ok)
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添加依赖
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.2</version>
</dependency>
如果密码包含特殊字符,要给整个密码用单引号包裹,不然会报错,此时先停用flyway,不然会报错,测试不了。
# 数据源设置
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 不贴代码了)
5、建立mapper接口,如果BaseMapper找不到就重新导入一下maven依赖
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包的路径
7、在idea自动生成的测试类中添加代码,测试数据库是否连通,测试类中的userMapper可能报红,注意这是不影响运行的,可以忽略
@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、运行测试类,可以看到如下输出
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进行测试;
-- 创建账户表
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_schema_history是flyway自动生成的,用来进行版本控制的表
account 表是根据V1.0.1__init-create-account.sql文件生成的表
执行到这一步,flyway的初步配置已经成功了
4、测试一下R开头的可重复执行文件;
新建一个文件:R__insert_data_to_account.sql
内容如下 :
-- 插入一条数据
insert into account (userid,money) values (1,800.0);
执行后控制台相关输出为:
数据库中的数据也插入了
注意 R开头的文件不能加版本号,加版本号会导致无法执行。
此时反复重启SpringBoot,R__insert_data_to_account.sql也不会再次执行,只有当R__insert_data_to_account.sql文件内容发生变化时,文件才会再次执行;
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了