如何在 Spring Boot 启动时为反应式 r2dbc 驱动程序运行 flyway 迁移
Posted
技术标签:
【中文标题】如何在 Spring Boot 启动时为反应式 r2dbc 驱动程序运行 flyway 迁移【英文标题】:How to run flyway migration for reactive r2dbc driver on sprintboot stratup 【发布时间】:2020-04-20 13:06:48 【问题描述】:我正在使用非阻塞数据库驱动程序 r2dbc 开发 springboot webflux 项目,
但是当 Springboot 应用启动时 Flyway 不运行迁移。 下面是我的spring-boot pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>r2dbmigration</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>r2dbmigration</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-test-autoconfigure-r2dbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-bom-r2dbc</artifactId>
<version>0.1.0.M3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
我的迁移文件是:V1.1__create_file_import_table.sql 内容为
DROP TABLE IF EXISTS file_import;
CREATE TABLE file_import
(
id BIGINT GENERATED ALWAYS AS IDENTITY,
file_key CHARACTER VARYING(255) NOT NULL,
created_at TIMESTAMP without time zone NOT NULL,
created_by BIGINT NOT NULL,
PRIMARY KEY (id)
);
application.properties
spring.r2dbc.url= r2dbc:postgresql://localhost:5432/import
spring.r2dbc.username=postgres
spring.r2dbc.password=password
我的应用程序启动顺利,但没有运行迁移。
有人可以帮帮我吗?
Github URL
谢谢
【问题讨论】:
Flyway 需要 JDBC 驱动,您的项目中是否也添加了 JDBC 驱动?另外,请提供minimal reproducible example,以便我们了解您的配置、可用库等。 @MarkRotteveel 我已经添加了 pom.xml 和迁移相关的属性和 .sql 文件 你没有为JDBC配置任何连接信息,你只有R2DBC。 Flyway 不能使用 R2DBC。 您可能想在github.com/flyway/flyway/issues/2502 上留下您的投票,让您希望将它与 R2DBC 一起使用的 Flyway 维护人员。 @AlpeshJikadra 正在使用 'Maven Flyway 插件' 选项?见答案。 【参考方案1】:以下 Java 实现基于 @Sim 的 Kotlin 示例。
例如pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RC1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>flyway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>flyway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
</project>
例如application.yml
---
spring:
r2dbc:
url: r2dbc:pool:mysql://localhost:3306/defaultdb
username: <user>
password: <pass>
flyway:
url: jdbc:mysql://localhost:3306/defaultdb
user: $spring.r2dbc.username
password: $spring.r2dbc.password
baseline-on-migrate: true
添加以下 Spring Boot 配置 - FlywayConfig.java
package com.example.flyway;
import org.flywaydb.core.Flyway;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
// https://***.com/a/61412233
@Configuration
public class FlywayConfig
private final Environment env;
public FlywayConfig(final Environment env)
this.env = env;
@Bean(initMethod = "migrate")
public Flyway flyway()
return new Flyway(Flyway.configure()
.baselineOnMigrate(true)
.dataSource(
env.getRequiredProperty("spring.flyway.url"),
env.getRequiredProperty("spring.flyway.user"),
env.getRequiredProperty("spring.flyway.password"))
);
第一次执行:
: Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
: Finished Spring Data repository scanning in 7ms. Found 0 R2DBC repository interfaces.
: Flyway Community Edition 6.4.1 by Redgate
: Database: jdbc:mysql://localhost:3306/defaultdb (MySQL 5.5)
: Successfully validated 1 migration (execution time 00:00.006s)
: Creating Schema History table `defaultdb`.`flyway_schema_history` ...
: DB: Name 'flyway_schema_history_pk' ignored for PRIMARY key. (SQL State: 42000 - Error Code: 1280)
: Current version of schema `defaultdb`: << Empty Schema >>
: Migrating schema `defaultdb` to version 1.0.001 - Initialise database tables
: Successfully applied 1 migration to schema `default` (execution time 00:00.036s)
: Netty started on port(s): 8080
:Started Application in 1.829 seconds (JVM running for 2.343)
第二次执行:
: No active profile set, falling back to default profiles: default
: Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
: Finished Spring Data repository scanning in 11ms. Found 0 R2DBC repository interfaces.
: Flyway Community Edition 6.4.1 by Redgate
: Database: jdbc:mysql://localhost:3306/defaultdb (MySQL 5.5)
: Successfully validated 1 migration (execution time 00:00.009s)
: Current version of schema `defaultdb`: 1.0.001
: Schema `defaultdb` is up to date. No migration necessary.
: Netty started on port(s): 8080
: Started Application in 1.273 seconds (JVM running for 1.695)
【讨论】:
【参考方案2】:回答有点晚,但您也可以手动设置 Spring bean 来处理迁移。
将 flyway 配置添加到 application.yml(或 .properties):
spring:
flyway:
url: jdbc:postgresql://localhost:5432/<db-name>
user: <user>
password: <password>
这段代码是 Kotlin,但可以很容易地翻译成 Java:
import org.flywaydb.core.Flyway
import org.springframework.boot.autoconfigure.flyway.FlywayProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class FlywayConfig(private val flywayProperties: FlywayProperties)
@Bean(initMethod = "migrate")
fun flyway(): Flyway?
return Flyway(Flyway.configure()
.baselineOnMigrate(true)
.dataSource(flywayProperties.url, flywayProperties.user, flywayProperties.password)
)
【讨论】:
FlywayProperties 不是由 Spring Boot 自动配置创建的。可能是因为缺少一些依赖。我不得不直接注入值【参考方案3】:这个问题似乎还没有“官方”解决方案,但有一个临时解决方案可以解决这个问题:“R2DBC 迁移工具”。
@Nikita Konev 为我们提供了一个很好的临时解决方案来解决这个问题。我一直在使用它并且效果很好。
请检查:
R2DBC 支持 - Flyway:https://github.com/flyway/flyway/issues/2502 R2DBC 迁移工具:https://github.com/nkonev/r2dbc-migrate 示例项目(你可以随便玩):https://github.com/nkonev/r2dbc-migrate-example感谢尼基塔·科涅夫
【讨论】:
【参考方案4】:这可能是延迟响应,但就个人而言,我更喜欢只添加 spring starter。 这对我有用:
// build.gradle.kts
implementation("org.flywaydb:flyway-core:7.9.1")
runtimeOnly("org.springframework.boot:spring-boot-starter-jdbc")
runtimeOnly("org.postgresql:postgresql:42.2.20")
那么您的 application.yml 文件可能如下所示:
# application.yml
spring:
r2dbc:
url: r2dbc:postgresql://user:pass@host/db
flyway:
enabled: true
validate-on-migrate: true
user: user
password: pass
url: jdbc:postgresql://host:5432/db
schemas: ["schema"]
【讨论】:
【参考方案5】:通过结合使用 Flyway + JDBC 和 R2DBC 进行其余 DB 交互来解决此问题。
application.properties
my.database.url=postgresql://localhost:5432/my_database
spring.r2dbc.url=r2dbc:$my.database.url
spring.r2dbc.username=user
spring.r2dbc.password=pass
spring.flyway.locations=classpath:db/migration
spring.flyway.enabled=true
spring.flyway.validate-on-migrate=true
spring.flyway.user=$spring.r2dbc.username
spring.flyway.password=$spring.r2dbc.password
spring.flyway.url=jdbc:$my.database.url
build.gradle
dependencies
...
implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
runtimeOnly 'org.flywaydb:flyway-core:7.9.1'
runtimeOnly 'org.postgresql:postgresql:42.2.20'
runtimeOnly 'io.r2dbc:r2dbc-postgresql'
runtimeOnly 'org.springframework.boot:spring-boot-starter-jdbc'
...
flyway迁移SQL脚本的位置:
src/main/resources/db/migration/V1__create_my_table.sql
【讨论】:
【参考方案6】:如 cmets 中所述:R2DBC 和 Flyway 尚不兼容。
一种解决方法是使用 Maven Flyway 插件执行 Flyway 迁移,注意您需要提供 JDBC-URL。
在您的情况下,您可以通过
触发迁移mvn flyway:migrate -Dflyway.url=jdbc:postgresql://localhost:5432/import -Dflyway.user=postgres -Dflyway.password=password
或通过
mvn flyway:migrate
如果您在pom.xml
中添加插件+配置
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>6.1.3</version>
<configuration>
<url>jdbc:postgresql://localhost:5432/import</url>
<user>postgres</user>
<password>password</password>
</configuration>
</plugin>
通过
执行迁移【讨论】:
好吧,这不是完全的解决方法,尤其是当构建工具无法访问数据库或在启动阶段运行迁移时。 @PeterJurkovic 不是在这里谈论构建工具的插件...如果您的构建工具可以执行任何 mvn build,它可以执行 mvn flyway:migrate以上是关于如何在 Spring Boot 启动时为反应式 r2dbc 驱动程序运行 flyway 迁移的主要内容,如果未能解决你的问题,请参考以下文章
从 Spring Boot 1.5 升级时为 Spring Boot 2.0 acuator 框架配置安全性
Maven 反应器:使用 Spring Boot 启动器 pom 的 pom