简体   繁体   中英

Database migrations with R2DBC

I am new to R2DBC ( https://r2dbc.io/ ). I would like to know whether r2dbc's ecosystem has a database migration tool/framework.

It seems Liquibase & Flyway depend on JDBC. Is there a plan for allowing those frameworks to support a r2dbc driver?

Any input or feedback welcome.

Steve's answer is correct hat R2DBC is primarily about interaction with the actual data. I'd like to add a different perspective.

It's true that a reactive API does not provide any improvement during migrations. In fact, looking closely, migrations are part of the startup process which is typically synchronous, at least synchronized to some extent.

Requiring JDBC for migration adds to complexity in such an application arrangement. You are required to include a JDBC driver to an existing R2DBC setup and you need to configure another database connection that points to the same database as with R2DBC. Both requirements are error-prone as they need to be configured to do the exact same thing.

Nowadays, application configuration frameworks (Spring Boot, Micronaut, Quarkus) activate functionality when a certain library is available from the classpath. Having a JDBC driver configured boots functionality that isn't required for the application but required during bootstrapping and that is sort of a waste of resources.

Ideally, you configure a single database connection technology that is reused for schema migration and for later data interaction within your application.

Therefore it makes sense to ask Liquibase and Flyway to provide an R2DBC-based integration.

It doesn't seem to me (from an admittedly cursory glance at the front page of the R2DBC web page) that the goals of R2DBC really have anything to do with migrations. That page lists key features as:

  • Reactive Streams - R2DBC is founded on Reactive Streams providing a fully reactive non-blocking API.
  • Relational Databases - R2DBC engages SQL databases with a reactive API, something not possible with the blocking nature of JDBC.
  • Scalable Solutions - Reactive Streams makes it possible to move from the classic one thread per connection approach to a more powerful, more scalable approach.

There is nothing in there that warrants adding R2DBC support to a framework like Liquibase. The JDBC drivers currently in use don't suffer from their use of a non-blocking API, don't really need a "reactive API", and almost certainly don't need to have more than one thread per connection.

Migration tools are primarily concerned with the shape/structure of the database and not the contents, while R2DBC is aimed at applications that primarily care about the actual data.

In Summary, I don't see any reason why someone wouldn't use a migration tool like Liquibase that uses JDBC just because their application uses R2DBC, and I don't see any advantage to adding R2DBC support to a tool like Liquibase.

You can try my package r2dbc-migrate .

In minimal configuration (let's suppose that you are using Spring Boot 2.3.0.M3), just add

<dependency>
  <groupId>name.nkonev.r2dbc-migrate</groupId>
  <artifactId>r2dbc-migrate-spring-boot-starter</artifactId>
  <version>0.0.24</version>
</dependency>

to pom.xml

then add .sql files in classpath, for example in /db/migration/

then add

r2dbc.migrate.resourcesPath: classpath:/db/migration/*.sql

to your application.yml

Both answers above are correct - just wanted to add that if you are looking for fast and easy path and you are using maven then Flyway is probably the most convenient way to operate.

All you need to have is Flyway Maven plugin, two pom dependencies and migration sql scripts.

Eg. assuming spring - r2dbc - postgresql you can have migration infrastructure ready in just three simple steps:

(1) Add migration script to resources:

resources/db/migration/V1_init.sql

(2) add two dependencies to pom

    <dependency>
        <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
    </dependency>

(3) and one plugin definition in build section:

    <plugin>
        <groupId>org.flywaydb</groupId>
           <artifactId>flyway-maven-plugin</artifactId>
           <version>6.0.0-beta2</version>
    </plugin>

and now you have single maven CLI command to migrate:

    mvn flyway:migrate -Dflyway.url=jdbc:postgresql://localhost:5432/test -Dflyway.user=test -Dflyway.password=test

See more Flyway maven plugin docs here

If anyone face the same problem and doesn't want to use the maven-flyway-plugin , take a look at the FlywayAutoConfiguration class. It has @Conditional(FlywayDataSourceCondition.class) , which has @ConditionalOnBean(DataSource.class) inside. Therefore the bottom line is you should provide a non-reactive database environment to make Flyway work. The most straight forward solution is to do something like this:

@Configuration
public class DataBaseConfig extends AbstractR2dbcConfiguration {

    @Value("${spring.data.postgres.host}")
    private String host;
    @Value("${spring.data.postgres.port}")
    private int port;
    @Value("${spring.data.postgres.database}")
    private String database;
    @Value("${spring.data.postgres.username}")
    private String username;
    @Value("${spring.data.postgres.password}")
    private String password;

    @Bean
    public DatabaseClient databaseClient() {
        return DatabaseClient.create(connectionFactory());
    }

    @Bean
    @Override
    public PostgresqlConnectionFactory connectionFactory() {
        PostgresqlConnectionConfiguration config = PostgresqlConnectionConfiguration.builder()
                .host(host)
                .port(port)
                .database(database)
                .username(username)
                .password(password)
                .build();
        return new PostgresqlConnectionFactory(config);
    }

    @Bean
    public DataSource dataSource() {
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.driverClassName("org.postgresql.Driver");
        dataSourceBuilder.url("jdbc:postgresql://" + host + ":" + port + "/" + database);
        dataSourceBuilder.username(username);
        dataSourceBuilder.password(password);
        return dataSourceBuilder.build();
    }
}

I went this way as I didn't want to: 1) Run the plugin on each startup; 2) Pass database properties in the comand line

Please, there is a solution for that finally, at least a temporary solution, because there is no Flyway official solution yet (I believe soon we are gonna see a official version).

Please check my post: https://stackoverflow.com/a/62864838/7681696

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM