R2DBC(反应式关系数据库连接)是 Pivotal 在 2018 年第一季度平台期间提出的一项努力。它打算为 SQL 数据库创建一个反应式 API。
首先,R2DBC项目是最近才开始的。目前,只有部分数据库具有 R2DBC 驱动程序。此外,我们不能将所有弹簧启动功能都与它一起使用。因此,我们需要手动添加一些步骤。但是,我们可以利用像Spring Data这样的项目来帮助我们。
我们将首先创建一个 Maven 项目。在这一点上,R2DBC存在一些依赖性问题,因此我们的pom.xml将比平时更大。
在本文的范围内,我们将使用 H2 作为数据库,并将为我们的应用程序创建反应式 CRUD 函数。
让我们打开生成的项目的 pom.xml,并添加适当的依赖项以及一些早期发布的 Spring 存储库:
org.springframework.data
spring-data-r2dbc
1.0.0.RELEASE
io.r2dbc
r2dbc-h2
0.8.1.RELEASE
com.h2database
h2
1.4.199
其他必需的工件包括龙目岛,春季WebFlux和其他一些完成我们项目依赖关系的工件。
使用数据库时,我们需要一个连接工厂。所以,当然,我们需要R2DBC做同样的事情。
因此,我们现在将添加详细信息以连接到我们的实例:
@Configuration
@EnableR2dbcRepositories
class R2DBCConfiguration extends AbstractR2dbcConfiguration {
@Bean
public H2ConnectionFactory connectionFactory() {
return new H2ConnectionFactory(
H2ConnectionConfiguration.builder()
.url("mem:testdb;DB_CLOSE_DELAY=-1;")
.username("sa")
.build()
);
}
}
在上面的代码中,我们注意到的第一件事是@EnableR2dbcRepositories。我们需要此注释来使用弹簧数据功能。此外,我们正在扩展 AbstractR2dbc 配置,因为它将提供许多我们稍后需要的 bean。
我们的下一步是创建存储库:
interface PlayerRepository extends ReactiveCrudRepository {}
反应式回复存储库接口非常有用。例如,它提供了基本的 CRUD 功能。
最后,我们将定义模型类。我们将使用龙目岛来避免样板代码:
@Data
@NoArgsConstructor
@AllArgsConstructor
class Player {
@Id
Integer id;
String name;
Integer age;
}
是时候测试我们的代码了。因此,让我们从创建几个测试用例开始:
@Test
public void whenDeleteAll_then0IsExpected() {
playerRepository.deleteAll()
.as(StepVerifier::create)
.expectNextCount(0)
.verifyComplete();
}
@Test
public void whenInsert6_then6AreExpected() {
insertPlayers();
playerRepository.findAll()
.as(StepVerifier::create)
.expectNextCount(6)
.verifyComplete();
}
我们还可以生成自定义查询。为了添加它,我们需要更改我们的玩家存储库:
@Query("select id, name, age from player where name = $1")
Flux findAllByName(String name);
@Query("select * from player where age = $1")
Flux findByAge(int age);
除了现有的测试之外,我们还会将测试添加到最近更新的存储库中:
@Test
public void whenSearchForCR7_then1IsExpected() {
insertPlayers();
playerRepository.findAllByName("CR7")
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
}
@Test
public void whenSearchFor32YearsOld_then2AreExpected() {
insertPlayers();
playerRepository.findByAge(32)
.as(StepVerifier::create)
.expectNextCount(2)
.verifyComplete();
}
private void insertPlayers() {
List players = Arrays.asList(
new Player(1, "Kaka", 37),
new Player(2, "Messi", 32),
new Player(3, "Mbappé", 20),
new Player(4, "CR7", 34),
new Player(5, "Lewandowski", 30),
new Player(6, "Cavani", 32)
);
playerRepository.saveAll(players).subscribe();
}
R2DBC 的另一个功能是创建批处理。批处理在执行多个 SQL 语句时很有用,因为它们的性能优于单个操作。
要创建批处理,我们需要一个连接对象:
Batch batch = connection.createBatch();
在我们的应用程序创建 Batch 实例后,我们可以根据需要添加任意数量的 SQL 语句。为了执行它,我们将调用 execute() 方法。批处理的结果是一个发布服务器,它将为每个语句返回一个结果对象。
因此,让我们跳到代码中,看看如何创建 Batch:
@Test
public void whenBatchHas2Operations_then2AreExpected() {
Mono.from(factory.create())
.flatMapMany(connection -> Flux.from(connection
.createBatch()
.add("select * from player")
.add("select * from player")
.execute()))
.as(StepVerifier::create)
.expectNextCount(2)
.verifyComplete();
}
综上所述,R2DBC仍处于早期阶段。它尝试创建一个 SPI,该 SPI 将定义 SQL 数据库的反应性 API。当与Spring WebFlux一起使用时,R2DBC允许我们编写一个应用程序,该应用程序从顶部异步处理数据,一直到数据库。
与往常一样,代码可在 GitHub 上使用。
换句话说,此工作使用完全非阻塞驱动程序创建数据库连接。
在本教程中,我们将看一个使用弹簧数据R2BDC的应用程序示例。有关更低级 R2DBC API 的指南,请查看我们之前的文章。