概述
在这个教程中,我们将探讨如何在Spring R2DBC应用中使用Flyway进行迁移,Flyway是一个常用于数据库迁移的开源工具。尽管目前它还没有对R2DBC的原生支持,我们将探讨在启动应用程序时如何以替代方式迁移表和数据。
1. 引言
在本篇文章中,我们将使用一个简单的Spring R2DBC应用,配合Flyway进行迁移,目标是在PostgreSQL数据库中创建表并插入数据。
2. 基础Spring R2DBC应用
2.1. R2DBC
R2DBC是Reactive Relational Database的缩写。它基于Reactive Streams规范,提供了与SQL数据库交互的全响应式非阻塞API。然而,尽管有许多优点,但像Flyway这样的工具目前还不支持R2DBC,这意味着在使用Flyway进行迁移时,需要一个阻塞的JDBC驱动。
数据库迁移允许应用程序在启动时根据新版本升级数据库模式。这些迁移是为了确保数据库结构与应用程序需求匹配。由于这些更改在应用程序启动前是必需的,因此同步阻塞的迁移是可以接受的。
2.2. 依赖项
为了让R2DBC应用与Flyway兼容,我们需要一些依赖项。
首先,我们需要添加spring-boot-starter-data-r2dbc
依赖,它提供了核心的Spring R2DBC数据抽象以及PostgreSQL R2DBC驱动:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<version>1.0.5.RELEASE</version>
</dependency>
为了配置数据库迁移,我们需要Flyway
:
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>10.12.0</version>
</dependency>
由于Flyway尚未支持R2DBC驱动,我们还需要添加标准的PostgreSQL JDBC驱动:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.3</version>
</dependency>
3. 在Spring R2DBC应用上使用Flyway迁移
现在来看看配置和示例脚本的要求。
3.1. Spring R2DBC和Flyway的配置
由于Flyway不支持R2DBC,我们需要创建一个带有migrate()
初始化方法的Flyway
Bean,这将提示Spring在创建Bean时立即运行我们的迁移:
@Configuration
@EnableConfigurationProperties({ R2dbcProperties.class, FlywayProperties.class })
class DatabaseConfig {
@Bean(initMethod = "migrate")
public Flyway flyway(FlywayProperties flywayProperties, R2dbcProperties r2dbcProperties) {
return Flyway.configure()
.dataSource(
flywayProperties.getUrl(),
r2dbcProperties.getUsername(),
r2dbcProperties.getPassword()
)
.locations(flywayProperties.getLocations()
.stream()
.toArray(String[]::new))
.baselineOnMigrate(true)
.load();
}
}
我们可以通过合并R2DBC属性,并为Flyway提供特定于其的重置,如URL(必须是JDBC连接URL)和迁移脚本的执行位置来指向数据库。
在这个例子中,Spring能够根据类路径中的依赖自动配置PostgreSQL R2DBC,但值得注意的是,R2DBC支持其他SQL数据库驱动。既然我们在应用中添加了R2DBC启动器,我们只需要设置Spring R2DBC的适当属性,不需要额外的配置。
让我们看一个包含JDBC驱动所需的数据库URL的R2DBC PostgreSQL和Flyway设置示例的属性文件:
spring:
r2dbc:
username: local
password: local
url: r2dbc:postgresql://localhost:8082/flyway-test-db
flyway:
url: jdbc:postgresql://localhost:8082/flyway-test-db
locations: classpath:db/postgres/migration
迁移脚本的默认目录是db/migration
,但上述配置指定我们的迁移脚本位于db/postgres/migration
目录下。
R2DBC URL格式如下:
jdbc/r2dbc
- 连接策略postgresql
- 数据库类型localhost:8082
- PostgreSQL数据库的主机flyway-test-db
- 数据库名称
3.2. 迁移脚本
让我们创建两个表(部门和学生)以及向部门表中插入一些数据的迁移脚本。
我们的第一个脚本将创建department
和student
表,我们将其命名为V1_1_create_tables.sql
,遵循文件命名约定,以便Flyway可以识别它为第一个执行的脚本。Flyway会跟踪所有运行过的脚本,确保每个脚本只执行一次:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE IF NOT EXISTS department
(
ID uuid PRIMARY KEY UNIQUE DEFAULT uuid_generate_v4(),
NAME varchar(255)
);
CREATE TABLE IF NOT EXISTS student
(
ID uuid PRIMARY KEY UNIQUE DEFAULT uuid_generate_v4(),
FIRST_NAME varchar(255),
LAST_NAME varchar(255),
DATE_OF_BIRTH DATE NOT NULL,
DEPARTMENT uuid NOT NULL CONSTRAINT student_foreign_key1 REFERENCES department (ID)
);
下一个脚本将向我们的表中插入数据,我们将其命名为V1_2_insert_department.sql
,以便它第二个执行:
insert into department(NAME) values ('Computer Science');
insert into department(NAME) values ('Biomedical');
3.3. 测试Spring R2DBC迁移
让我们快速测试一下设置是否有效。
首先,编写一个简单的docker-compose.yml
来启动PostgreSQL DB:
version: '3.9'
networks:
obref:
services:
postgres_db_service:
container_name: postgres_db_service
image: postgres:11
ports:
- "8082:5432"
hostname: postgres_db_service
environment:
- POSTGRES_PASSWORD=local
- POSTGRES_USER=local
- POSTGRES_DB=flyway-test-db
当第一次启动应用时,我们应该看到日志确认迁移已应用:
INFO 95740 --- [ restartedMain] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 9.14.1 by Redgate
INFO 95740 --- [ restartedMain] o.f.c.internal.license.VersionPrinter : See what's new here: https://flywaydb.org/documentation/learnmore/releaseNotes#9.14.1
INFO 95740 --- [ restartedMain] o.f.c.internal.license.VersionPrinter :
INFO 95740 --- [ restartedMain] o.f.c.i.database.base.BaseDatabaseType : Database: jdbc:postgresql://localhost:8082/flyway-test-db (PostgreSQL 11.16)
INFO 95740 --- [ restartedMain] o.f.core.internal.command.DbValidate : Successfully validated 2 migrations (execution time 00:00.007s)
INFO 95740 --- [ restartedMain] o.f.c.i.s.JdbcTableSchemaHistory : Creating Schema History table "public"."flyway_schema_history" ...
INFO 95740 --- [ restartedMain] o.f.core.internal.command.DbMigrate : Current version of schema "public": << Empty Schema >>
<strong>INFO 95740 --- [ restartedMain] o.f.core.internal.command.DbMigrate : Migrating schema "public" to version "1.1 - create tables"
INFO 95740 --- [ restartedMain] o.f.core.internal.command.DbMigrate : Migrating schema "public" to version "1.2 - insert department"
</strong>INFO 95740 --- [ restartedMain] o.f.core.internal.command.DbMigrate : Successfully applied 2 migrations to schema "public", now at version v1.2 (execution time 00:00.045s)
INFO 95740 --- [ restartedMain] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port 8080
INFO 95740 --- [ restartedMain] c.b.e.r.f.SpringWebfluxFlywayApplication : Started SpringWebfluxFlywayApplication in 1.895 seconds (JVM running for 2.28)
从上述日志可以看出,迁移脚本按照预期顺序执行。
如果flyway_schema_history
表不存在,Flyway会创建它并存储迁移状态的详细信息。脚本完成后,我们可以从这个表中查看迁移的详细信息,包括文件名和执行时间。
4. 总结
在这篇文章中,我们创建了一个基础的Spring R2DBC应用,并探讨了如何使用Flyway在R2DBC应用中进行数据迁移。我们也采用了一些策略来验证Flyway按预期执行了迁移。
本文的代码示例可在GitHub上找到。