SpringBoot中使用flyway做好数据库版本控制

为什么需要数据库版本控制

​ 在真实的项目开发中,我们一般有三套环境:开发、测试、生产。在开发阶段我们一般都是在开发环境中进行操作,项目开发肯定不止一个人,在开发的过程中,我们肯定会对数据库的库表进行一些修改操作。并且这些操作需要同步到这三套环境中。但是多人协同,人为操作难免会出现疏忽,有时候修改了开发环境忘记了去修改其他环境。很多情况下都需要对数据库的变化做跟踪,以便于我们回退到某个版本。因此我们需要有这样一个工具来帮助我们管理数据库版本,做好各个环境同步更新。

为什么选择Flyway

​ 现在比较常用的数据库版本管理工具有Flyway和Liquibase。Spring Boot提供了这两者的内建支持,可以很快应用到产品中。

​ 使用Flyway的好处在于使用简单,直接书写我们比较熟悉的sql脚本即可,不需要进行额外的学习。Liquibase的优点在于它能跨平台、跨库,但是需要我们花时间去学习他的脚本编写规则。如果你的项目中没有跨数据库的需要,那么flyway完全够用了。

Flyway的工作模式

​ flyway在项目运行的时候会判断你的数据库中是否存在flyway_schema_history表,如果没有就会创建。这个表主要用于记录数据库的状态,Flyway的版本控住主要也是依赖这张表的。

​ 当flyway_schema_history这张表存在,。当检测到你有新的版本需要迁移的时候,Flyway会逐一对比flyway_schema_history表中的已存在的版本记录,如果有未应用的Migrations,Flyway会获取这些Migrations并按版本号次序迁移到数据库中。

TkDIKR

应用每个迁移时,flyway_schema_history表将相应更新:

GC3nO8

​ flyway在升级数据库的时候,会检查已经执行过的版本对应的脚本是否发生变化,包括脚本文件名,以及脚本内容。如果flyway检测到发生了变化,则抛出错误,并终止升级。

​ 如果已经执行过的脚本没有发生变化,flyway会跳过这些脚本,依次执行后续版本的脚本,并在记录表中插入对应的升级记录。

​ 所以,flyway总是幂等的,而且可以支持跨版本的升级。

​ Migrations就是我们用SQL编写的脚本。为了让我们编写的SQL脚本生效,还需要按照Flyway指定的方式进行命名。

nuNib6

  • Prefix: V代表版本化,U代表撤销,R代表可重复迁移。
  • Version: 带点或下划线的版本,可以随意分隔多个部分(不适合重复迁移)。
  • Separator: __ (两个下划线)
  • Description: 下划线或空格分隔单词
  • Suffix: .sql

如何使用

1、添加依赖

1
2
3
4
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>

2、配置application.yml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false&allowMultiQueries=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver

# 数据库版本控制
flyway:
# 启用或禁用 flyway
enabled: true
# 字符编码
encoding: utf-8
# 对执行迁移时基准版本的描述
baseline-description: test
# 若连接的数据库非空库,是否初始化
# 当迁移时发现目标schema非空,而且带有没有元数据的表时,是否自动执行基准迁移,默认false.
baseline-on-migrate: true
# 指定 baseline 的版本号,缺省值为 1, 低于该版本号的 SQL 文件, migrate 的时候被忽略
# 开始执行基准迁移时对现有的schema的版本打标签,默认值为1.
baseline-version: 0
# 是否开启校验
# 迁移时是否校验,默认为 true
validate-on-migrate: true
# 默认脚本加载路径:/db/migration
# locations: ["classpath:/db/migration"]
# flyway 的 clean 命令会删除指定 schema 下的所有 table,默认 false
clean-disabled: false
# 发环境最好开启 outOfOrder, 生产环境关闭 outOfOrder
# 是否允许无序的迁移,默认 false
out-of-order: false
# 检查迁移脚本的位置是否存在,默认false
check-location: false
# 当读取元数据表时是否忽略错误的迁移,默认false
ignore-future-migrations: false
# 当初始化好连接时要执行的SQL
init-sqls: show tables;
# 迁移时使用的JDBC URL,如果没有指定的话,将使用配置的主数据源
# url:
# 迁移数据库的用户名
# user:
# 目标数据库的密码
# password:
# 设置每个placeholder的前缀,默认${
#placeholder-prefix:
# 是否要被替换,默认true
#placeholder-replacement:
# 设置每个placeholder的后缀,默认}
#placeholder-suffix:
# 设置placeholder的value
#placeholders.[placeholder name]
# 设定需要flywary迁移的schema,大小写敏感,默认为连接默认的schema
#schemas: flyway
# 迁移文件的前缀,默认为V
#sql-migration-prefix:
# 迁移脚本的文件名分隔符,默认__
#sql-migration-separator:
# 迁移脚本的后缀,默认为.sql
#sql-migration-suffix:
# 使用的元数据表名,默认为schema_version
#table: flyway_schema_history
# 迁移时使用的目标版本,默认为latest version
#target:

flyway下没有配置url、user、password的话将会使用springboot的数据源。

3、在db.migration包下新建V1__initialization_table.sql文件。

1
2
3
4
5
6
DROP TABLE IF EXISTS user;
CREATE TABLE `user` (
`id` bigint(20) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`user_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '姓名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户表';

flyway脚本默认放在classpath:/db/migration目录下面,如果想换位置,可以自定义一个位置spring.flyway.locations即为脚本存放的位置。

4、开始验证

目前只有test库中没有任何表:

JEo9XF

我们开始启动项目看看:

NQVITI

通过日志信息,我们可以看到已经迁移成功了,我们去数据库看看。

PI8774

表结构:

UTMMNl

现在我们来给user表新增一个字段试试。

V1.1__addField_col.sql

1
alter table user add mail varchar(128) comment '用户邮箱';

运行项目看看

hIlzSt

表结构:

IDeE9S