一、为什么使用Flyway
最简单的一个项目是一个软件连接到一个数据库,但是大多数项目中我们不仅要处理我们开发环境的副本,还需要处理其他很多副本。例如:开发环境、测试环境、生产环境。想到数据库管理,我们立刻就能想到一系列问题
- 如何快速收集执行脚本的清单
- 执行的脚本总要人工执行,是否可以通过机器执行
- 执行的脚本是否已经在数据库执行过
- 执行的脚本是否全部在数据库中执行过
- 执行的脚本如何回退
- 如何初始化一个空数据库实例
Flyway是一款数据库版本控制管理工具,它可以简单的、可靠的升级你的数据库。它能帮助解决上面的问题。Flyway核心是记录所有版本演化和状态的MetaData,首次启动创建默认名为SCHEMA_VERSION的元素表。表中保存了版本,描述,要执行的sql脚本等信息。
Flyway已经支持数据库包括:Oracle, SQL Server, SQL Azure, DB2, DB2 z/OS, MySQL
(including Amazon RDS), MariaDB, Google Cloud SQL, PostgreSQL
(including Amazon RDS and Heroku), Redshift, Vertica, H2, Hsql, Derby,
SQLite, SAP HANA, solidDB, Sybase ASE and Phoeni
二、SpringBoot集成Flyway
2.1 简单示例
参考版本信息
示例信息 | 版本 |
---|---|
springboot | 2.7.5 |
flyway | 7.15.0 |
mysql | 8.0.28 |
2.1.1.创建SpringBoot应用,并添加flyway-core依赖,本例中将实现初始化脚本到mysql数据库,因此同时引入了驱动依赖 mysql-connector-java
java
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>7.15.0</version>
</dependency>
参考pom.xml依赖如下
java
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>7.15.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
2.1.2.在application.properties中设置flyway信息
java
server.port=7002
##是否启动,默认开启
spring.flyway.enabled = true
## 当迁移发现数据库非空且存在没有元数据的表时,自动执行基准迁移,新建schema_version表
spring.flyway.baseline-on-migrate=true
# 与 baseline-on-migrate: true 搭配使用
spring.baseline-version: 0
##脚本存放路径
spring.flyway.locations = classpath:db/migration
##当flyway第一次运行时,会在我们对应的数据库中新建一个记录脚本运行情况的
spring.flyway.table=sat_version
## 编码格式,默认UTF-8
spring.flyway.encoding=UTF-8
2.1.3.脚本整理
将脚本整理到resource/db.migration路径下,例如
参考SQL脚本信息如下
java
create table if not exists test_plan_project
(
test_plan_id varchar(50) null,
project_id varchar(50) null,
constraint test_plan_project_pk
unique (test_plan_id, project_id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_general_ci;
insert into test_plan_project(test_plan_id, project_id) select id test_plan_id, project_id project_id from test_plan;
- 版本化迁移 :执行一遍,版本号唯一,有重复会报错:格式:V+版本号 +双下划线+描述+结束符
- 重复的迁移 :不需要版本号,脚本发生变化启动就会执行:格式:R+双下划线+描述+结束符
- 撤消迁移 :格式:U+版本号 +双下划线+描述+结束符
2.1.4.运行启动主类,运行日志如下,从日志中可以看到如下信息
- 启动后正确链接到数据库
- 验证迁移脚本成功
- 使用命令行的方式创建了一张名称为sat_version 的记录表,这里要注意,所有脚本一旦执行了就会在sat_version中创建记录,如果出错引发问题,可以删除表中记录,反正启动的时候还会再执行,当然生产环境不建议此方法,但生产环境上部署的包都是验证过无问题的包也不会出现此问题
- 执行了resource/db.migration 目录下的脚本,并执行成功
2.2 常见问题
2.2.1.Caused by: org.flywaydb.core.api.FlywayException: Found non-empty schema(s)
java
Caused by: org.flywaydb.core.api.FlywayException: Found non-empty schema(s) `runoob` but no schema history table. Use baseline() or set baselineOnMigrate to true to initialize the schema history table.
at org.flywaydb.core.Flyway$1.execute(Flyway.java:200) ~[flyway-core-7.15.0.jar:na]
at org.flywaydb.core.Flyway$1.execute(Flyway.java:170) ~[flyway-core-7.15.0.jar:na]
at org.flywaydb.core.Flyway.execute(Flyway.java:586) ~[flyway-core-7.15.0.jar:na]
问题原因:第一执行的时候没有找到schema history table ,这张表其实就是application.properties文件中spring.flyway.table属性配置的表,因此要么使用命令创建一个或者在application.properties文件中设置 spring.flyway.baseline-on-migrate=true,
2.2.2.Caused by: org.flywaydb.core.api.FlywayException: Unsupported Database: MySQL 5.7
java
Caused by: org.flywaydb.core.api.FlywayException: Unsupported Database: MySQL 5.7
at org.flywaydb.core.internal.database.DatabaseTypeRegister.getDatabaseTypeForConnection(DatabaseTypeRegister.java:106) ~[flyway-core-8.4.2.jar:na]
at org.flywaydb.core.internal.jdbc.JdbcConnectionFactory.<init>(JdbcConnectionFactory.java:75) ~[flyway-core-8.4.2.jar:na]
at org.flywaydb.core.FlywayExecutor.execute(FlywayExecutor.java:143) ~[flyway-core-8.4.2.jar:na]
at org.flywaydb.core.Flyway.migrate(Flyway.java:124) ~[flyway-core-8.4.2.jar:na]
问题原因:flyway-core对数据库版本有要求,例如flyway-core的当前最高版本V8.4.3,不能使用 MySQL 5.7, 当flyway-core 降低到V7.15.0后 问题解决,所以匹配flyway-core和数据库版本后问题即可解决
三、总结
- 使用Flyway之前部署脚本方式一般为开发人员按照顺序汇总数据库的升级脚, 然后DBA或者售后在生产库中按照顺序执行升级脚本。
- 使用Flyway之后部署脚本方式就变更为开发人员将脚本构建到程序包中, 部署程序包后启动时Flyway自动执行脚本升级。