Flyway 是一款开源的数据库版本管理工具,它更倾向于规约优于配置的方式。Flyway 可以独立于应用实现管理并跟踪数据库变更,支持数据库版本自动升级,并且有一套默认的规约,不需要复杂的配置,Migrations 可以写成 SQL 脚本,也可以写在 Java 代码中,不仅支持 Command Line 和 Java API,还支持 Build 构建工具和 Spring Boot 等,同时在分布式环境下能够安全可靠地升级数据库,同时也支持失败恢复等。
Flyway 大受欢迎是因为它具有以下优点:
- 简单 非常容易安装和学习,同时迁移的方式也很容易被开发者接受。
- 专一 专注于搞数据库迁移、版本控制而并没有其它副作用。
- 强大 专为连续交付而设计。让Flyway在应用程序启动时迁移数据库。
关于Flyway我就不多介绍了,感兴趣的可以参考文章最下面的参考链接。
我这里直接开始介绍怎么整合Flyway。
Spring Boot 提供了对 Flyway 的自动配置 。使我们可以开箱即用 Flyway 进行数据库版本控制。
<dependency>
<groupId>org.flywaydbgroupId>
<artifactId>flyway-coreartifactId>
dependency>
<build>
<plugins>
<plugin>
<groupId>org.flywaydbgroupId>
<artifactId>flyway-maven-pluginartifactId>
<configuration>
<url>jdbc:mysql://192.168.99.100:3306/spring_cloud_demourl>
<user>rootuser>
<password>rootpassword>
configuration>
plugin>
plugins>
build>
添加插件的目的是为了使用Flyway的一些命令、
可以参考:Flyway提供的六种命令
可以通过mvn flyway:info命令来查看数据库的版本和和需要执行的sql脚本。通过mvn flyway:clean来清空数据库,这条命令对于测试很方便,但是千万要小心生产环境!!!此外,还有一个较常用的命令:mvn flyway:validate可以验证是否所有的sql都已经在数据库上运行完毕了。
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,这里我们配置为spring_cloud_demo 缺省的话, 使用spring.datasource.url 配置的那个 schema,
# 可以指定多个schema, 但仅会在第一个schema下建立 metadata 表, 也仅在第一个schema应用migration sql 脚本.
# 但flyway Clean 命令会依次在这些schema下都执行一遍. 所以 确保生产 spring.flyway.clean-disabled 为 true
schemas: spring_cloud_demo
# 执行迁移时是否自动调用验证 当你的 版本不符合逻辑 比如 你先执行了 DML 而没有 对应的DDL 会抛出异常
validate-on-migrate: true
use `spring_cloud_demo`;
create table MEMBERS (
id integer not null auto_increment,
create_by varchar(255),
create_date datetime,
update_by varchar(255),
update_date datetime,
jpa_version bigint,
member_name varchar(20) not null,
primary key (id)
)ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
INSERT INTO spring_cloud_demo.MEMBERS (id, create_by, create_date, update_by, update_date, jpa_version, member_name) VALUES (1, 'admin', '2019-11-02 06:52:05', 'system', '2019-11-02 06:52:17', 1, 'system');
请注意命名规则。脚本名称为 V1.0.1__insert_init_member_data.sql
Flyway的命名规范如下:以V
开头,.sql
结尾,版本号可以使用.
或者_
,版本号和描述之间用两个下划线__
分开。
启动项目之后发现报错java.sql.SQLException: sql injection violation
Caused by: java.sql.SQLException: sql injection violation, comment not allow : CREATE TABLE `spring_cloud_demo`.`flyway_schema_history` (
`installed_rank` INT NOT NULL,
`version` VARCHAR(50),
`description` VARCHAR(200) NOT NULL,
`type` VARCHAR(20) NOT NULL,
`script` VARCHAR(1000) NOT NULL,
`checksum` INT,
`installed_by` VARCHAR(100) NOT NULL,
`installed_on` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`execution_time` INT NOT NULL,
`success` BOOL NOT NULL,
-- Add the primary key as part of the CREATE TABLE statement in case `innodb_force_primary_key` is enabled
CONSTRAINT `flyway_schema_history_pk`PRIMARY KEY (`installed_rank`)
) ENGINE=InnoDB
at com.alibaba.druid.wall.WallFilter.checkInternal(WallFilter.java:800) ~[druid-1.1.10.jar:1.1.10]
at com.alibaba.druid.wall.WallFilter.check(WallFilter.java:780) ~[druid-1.1.10.jar:1.1.10]
at com.alibaba.druid.wall.WallFilter.statement_execute(WallFilter.java:413) ~[druid-1.1.10.jar:1.1.10]
at com.alibaba.druid.filter.FilterChainImpl.statement_execute(FilterChainImpl.java:2956) ~[druid-1.1.10.jar:1.1.10]
at com.alibaba.druid.filter.FilterAdapter.statement_execute(FilterAdapter.java:2473) ~[druid-1.1.10.jar:1.1.10]
at com.alibaba.druid.filter.FilterEventAdapter.statement_execute(FilterEventAdapter.java:188) ~[druid-1.1.10.jar:1.1.10]
at com.alibaba.druid.filter.FilterChainImpl.statement_execute(FilterChainImpl.java:2956) ~[druid-1.1.10.jar:1.1.10]
at com.alibaba.druid.proxy.jdbc.StatementProxyImpl.execute(StatementProxyImpl.java:147) ~[druid-1.1.10.jar:1.1.10]
at com.alibaba.druid.pool.DruidPooledStatement.execute(DruidPooledStatement.java:619) ~[druid-1.1.10.jar:1.1.10]
at org.flywaydb.core.internal.jdbc.JdbcTemplate.executeStatement(JdbcTemplate.java:235) ~[flyway-core-5.2.3.jar:na]
at org.flywaydb.core.internal.sqlscript.StandardSqlStatement.execute(StandardSqlStatement.java:42) ~[flyway-core-5.2.3.jar:na]
at org.flywaydb.core.internal.sqlscript.DefaultSqlScriptExecutor.executeStatement(DefaultSqlScriptExecutor.java:189) ~[flyway-core-5.2.3.jar:na]
... 30 common frames omitted
原因是Flyway通过 SQL 脚本来执行数据库的建立与更新。当同时集成了 Druid 和 Flyway 之后,Druid 的 wall 防火墙极可能直接干预 SQL 脚本的操作,继而导致 Flyway 执行中断。
我的解决方法就是去掉filters:wall。
还有一点要注意的就是需要把我们hibernate的ddl-auto设置为ddl-auto: none
。
之后重新启动就可以了。
启动完毕之后我们用flyway:info
就可以看到我们的脚本已经执行状态是成功了。
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building cloud-service-member 1.0
[INFO] ------------------------------------------------------------------------
[WARNING] The POM for com.cc.cloud:cloud-test-common:jar:1.0 is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details
[INFO]
[INFO] --- flyway-maven-plugin:5.2.3:info (default-cli) @ cloud-service-member ---
[INFO] Flyway Community Edition 5.2.3 by Boxfuse
[INFO] Database: jdbc:mysql://192.168.99.100:3306/spring_cloud_demo (MySQL 8.0)
[INFO] Schema version: 1.0.1
[INFO]
[INFO] +-----------+---------+-------------------------+------+---------------------+---------+
| Category | Version | Description | Type | Installed On | State |
+-----------+---------+-------------------------+------+---------------------+---------+
| Versioned | 1.0.1 | insert init member data | SQL | 2019-11-10 21:09:48 | Success |
+-----------+---------+-------------------------+------+---------------------+---------+
这里我直接给出我的配置文件,想知道具体怎么搭建集成测试环境的可以看我之前的博文。
spring:
datasource:
druid:
# jdbc:h2:mem:指定databaseName; 内存模式
# DB_CLOSE_DELAY=-1 关闭连接后数据库将被清空,适合测试环境
# MODE=MYSQL 兼容模式为MYSQL
# DB_CLOSE_ON_EXIT=FALSE VM存在时不关闭数据库
url: jdbc:h2:mem:member_service_db;MODE=MYSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password: sa
driver-class-name: org.h2.Driver
platform: h2
# schema: classpath:schema.sql //程序运行时,使用schema.sql来创建数据库中的表
# data: classpath:/db/init-data.sql # 程序运行时,使用db/init-data.sql来创建初始数据
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
database: h2
show-sql: true
hibernate:
ddl-auto: none # create-drop 每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
properties:
hibernate:
show_sql: true # 操作数据库时显示sql语句
use_sql_comments: true # SQL 语句中输出便于调试的注释信息
format_sql: true
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
jdbc:
time_zone: UTC
flyway:
# 启用或禁用 flyway
enabled: true
# flyway 的 clean 命令会删除指定 schema 下的所有 table, 生产务必禁掉。这个默认值是 false 理论上作为默认配置是不科学的。
clean-disabled: true
# SQL 脚本的目录,多个路径使用逗号分隔 默认值 classpath:db/migration
locations: classpath:test/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
# 这里的schemas需要使用PUBLIC,使用H2 默认的schema,防止找不到报错Caused by: org.h2.jdbc.JdbcSQLException: Table “xxxx” not found
schemas: PUBLIC
# 执行迁移时是否自动调用验证 当你的 版本不符合逻辑 比如 你先执行了 DML 而没有 对应的DDL 会抛出异常
validate-on-migrate: true
h2: # 开启h2的控制台
console:
enabled: true
path: /console
settings:
trace: false
web-allow-others: true
cloud:
bus:
enabled: false # 关闭Spring Cloud Bus,停止连接RabbitMQ
config:
enabled: false # disable config
discovery:
enabled: false # disable config
discovery: # disable eureka
enabled: false
eureka:
client:
enabled: false # disable eureka
这里唯一值得注意的就是schemas: PUBLIC
,使用的是H2 默认的schema,防止找不到报错Caused by: org.h2.jdbc.JdbcSQLException: Table “xxxx” not found
然后还有添加sql脚本
create table MEMBERS (
id integer not null auto_increment,
create_by varchar(255),
create_date datetime,
update_by varchar(255),
update_date datetime,
jpa_version bigint,
member_name varchar(20) not null,
primary key (id)
)ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
INSERT INTO MEMBERS(id, create_by, create_date, update_by, update_date, jpa_version, member_name) VALUES (1, 'admin', '2019-11-02 06:52:05', 'system', '2019-11-02 06:52:17', 1, 'system');
还有一定要注意的是sql脚本的路径一定要对,是test/db/migration
,不是test.db.migration
,在IDEA显示的时候可能都一样,但是要注意真实路径下的文件夹路径是不是对的。不然会报找不到sql脚本。
最后是运行测试。
Flyway的使用
快速学会像Git一样管理数据库业务版本变更
轻松上手数据库版本管理工具Flyway
Spring Boot中使用Flyway来管理数据库版本
Flyway 简单入门教程
Flyway官网
Flyway提供的六种命令
内存中的Spring boot flyway H2:引起:org.h2.jdbc.JdbcSQLException:表“xxxx”未找到; SQL语句:
Spring boot + flyway + H2 in memory :Caused by: org.h2.jdbc.JdbcSQLException: Table “xxxx” not found; SQL statement:
https://gitee.com/cckevincyh/spring-cloud-demo/tree/flyway/