A Quick Look at R2DBC with Spring Data – 快速了解带有Spring Data的R2DBC

最后修改: 2019年 7月 17日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

R2DBC (Reactive Relational Database Connectivity) is an effort presented by Pivotal during Spring One Platform 2018. It intends to create a reactive API to SQL databases.

R2DBC(反应式关系数据库连接)是Pivotal在Spring One Platform 2018期间提出的一项努力。它打算为SQL数据库创建一个反应式API。

In other words, this effort creates a database connection using fully non-blocking drivers.

换句话说,这项工作使用完全无阻塞的驱动程序创建了一个数据库连接。

In this tutorial, we’ll have a look at an example of an application using Spring Data R2BDC. For a guide to the more low-level R2DBC API, have a look at our previous article.

在本教程中,我们将看看一个使用Spring Data R2BDC的应用实例。关于更低级别的R2DBC API的指南,请看我们之前的文章

2. Our First Spring Data R2DBC Project

2.我们的第一个Spring数据R2DBC项目

To begin with, the R2DBC project is very recent. At this moment, only PostGres, MSSQL, and H2 have R2DBC drivers. Further, we can’t use all Spring Boot functionality with it. Therefore there’re some steps that we’ll need to manually add. But, we can leverage projects like Spring Data to help us.

首先,R2DBC项目非常新。目前,只有PostGres、MSSQL和H2有R2DBC驱动。此外,我们不能用它来使用所有Spring Boot功能。因此,有一些步骤是我们需要手动添加的。但是,我们可以利用Spring Data等项目来帮助我们。

We’ll create a Maven project first. At this point, there are a few dependency issues with R2DBC, so our pom.xml will be bigger than it’d normally be.

我们先创建一个Maven项目。此时,R2DBC存在一些依赖性问题,所以我们的pom.xml会比平时大一些。

For the scope of this article, we’ll use H2 as our database and we’ll create reactive CRUD functions for our application.

在本文的范围内,我们将使用H2作为我们的数据库,我们将为我们的应用程序创建反应式CRUD函数。

Let’s open the pom.xml of the generated project and add the appropriate dependencies as well as some early-release Spring repositories:

让我们打开生成的项目的pom.xml,添加适当的依赖项以及一些早期发布的Spring仓库。

<dependencies>
     <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-r2dbc</artifactId>
        <version>1.0.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>io.r2dbc</groupId>
        <artifactId>r2dbc-h2</artifactId>
        <version>0.8.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.199</version>
    </dependency>
</dependencies>

Other required artifacts include Lombok, Spring WebFlux and a few others that complete our project dependencies.

其他所需的工件包括Lombok, Spring WebFlux和其他一些完成我们项目依赖性的工件。

3. Connection Factory

3.连接工厂

When working with a database, we need a connection factory. So, of course, we’ll need the same thing with R2DBC.

当与数据库一起工作时,我们需要一个连接工厂。所以,当然,我们在使用R2DBC时也需要同样的东西。

So we’ll now add the details to connect to our instance:

所以我们现在要添加细节,以连接到我们的实例。

@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()
        );
    }
}

The first thing we notice in the code above is the @EnableR2dbcRepositories. We need this annotation to use Spring Data functionality. In addition, we’re extending the AbstractR2dbcConfiguration since it’ll provide a lot of beans that we’d need later on.

我们在上面的代码中首先注意到的是@EnableR2dbcRepositories。我们需要这个注解来使用Spring Data的功能。此外,我们正在扩展AbstractR2dbcConfiguration,因为它将提供很多我们以后需要的bean。

4. Our First R2DBC Application

4.我们的第一个R2DBC应用

Our next step is to create the repository:

我们的下一步是创建资源库。

interface PlayerRepository extends ReactiveCrudRepository<Player, Integer> {}

The ReactiveCrudRepository interface is very useful. It provides, for example, basic CRUD functionality.

ReactiveCrudRepository接口非常有用。例如,它提供了基本的CRUD功能。

Finally, we’ll define our model class. We’ll use Lombok to avoid boilerplate code:

最后,我们将定义我们的模型类。我们将使用Lombok来避免模板代码。

@Data
@NoArgsConstructor
@AllArgsConstructor
class Player {
    @Id
    Integer id;
    String name;
    Integer age;
}

5. Testing

5.测试

It’s time to test our code. So, let’s start by creating a few test cases:

现在是时候测试我们的代码。因此,让我们从创建几个测试案例开始。

@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();
}

6. Custom Queries

6.自定义查询

We can also generate custom queries. In order to add it, we’ll need to change our PlayerRepository:

我们也可以生成自定义查询。为了添加它,我们需要改变我们的PlayerRepository

@Query("select id, name, age from player where name = $1")
Flux<Player> findAllByName(String name);

@Query("select * from player where age = $1")
Flux<Player> findByAge(int age);

In addition to the existing tests, we’ll add tests to our recently updated repository:

除了现有的测试,我们还将在最近更新的资源库中添加测试。

@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<Player> 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();
}

7. Batches

7.批次

Another feature of R2DBC is to create batches. A batch is useful when executing multiple SQL statements as they’ll perform better than individual operations.

R2DBC的另一个功能是创建批处理。在执行多个SQL语句时,批处理是很有用的,因为它们会比单独的操作表现得更好。

To create a Batch we need a Connection object:

要创建一个Batch,我们需要一个Connection对象。

Batch batch = connection.createBatch();

After our application creates the Batch instance, we can add as many SQL statements as we want. To execute it, we’ll invoke the execute() method. The result of a batch is a Publisher that’ll return a result object for each statement.

在我们的应用程序创建了Batch 实例之后,我们可以添加我们想要的许多SQL语句。为了执行它,我们将调用execute() 方法。批处理的结果是一个Publisher,它将为每个语句返回一个结果对象。

So let’s jump into the code and see how we can create a Batch:

因此,让我们跳进代码,看看我们如何创建一个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();
}

8. Conclusion

8.结语

To summarize, R2DBC is still in an early stage. It’s an attempt to create an SPI that will define a reactive API to SQL databases. When used with Spring WebFlux, R2DBC allows us to write an application that handles data asynchronously from the top and all the way down to the database.

总而言之,R2DBC仍然处于早期阶段。它试图创建一个SPI,以定义SQL数据库的反应式API。当与Spring WebFlux一起使用时,R2DBC允许我们编写一个应用程序,从顶部异步处理数据并一直到数据库。

As always the code is available at GitHub.

一如既往,代码可在GitHub上获得。