spring-bootspring-webfluxflyway

How to run flyway migration for reactive r2dbc driver on springboot startup


I am working on springboot webflux project with non-blocking database driver r2dbc,

But when Springboot application start Flyway does not run migrations. Below are my 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>

My migration file is : V1.1__create_file_import_table.sql with content of

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

My application starts smoothly but there is no migration run.

Can someone please help me ?

Github URL

Thanks


Solution

  • The following Java implementation is based on @Sim's Kotlin example.

    Example 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>
    

    Example 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
    

    Add the following Spring Boot configuration - 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://stackoverflow.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"))
            );
        }
    }
    

    First execution:

    : 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)
    

    Second execution:

    : 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)