«1. Введение

R2DBC (Reactive Relational Database Connectivity) — это проект, представленный Pivotal во время Spring One Platform 2018. Он предназначен для создания реактивного API для баз данных SQL.

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.

2. Наш первый проект Spring Data R2DBC

Начнем с того, что проект R2DBC появился совсем недавно. На данный момент только PostGres, MSSQL и H2 имеют драйверы R2DBC. Кроме того, мы не можем использовать с ним все функции Spring Boot. Поэтому есть некоторые шаги, которые нам нужно будет добавить вручную. Но мы можем использовать такие проекты, как Spring Data, чтобы помочь нам.

Сначала мы создадим проект Maven. На данный момент у R2DBC есть несколько проблем с зависимостями, поэтому наш pom.xml будет больше, чем обычно.

В рамках этой статьи мы будем использовать H2 в качестве нашей базы данных и создадим реактивные функции CRUD для нашего приложения.

Давайте откроем 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>

Другие необходимые артефакты включают Lombok, Spring WebFlux и несколько других, которые завершают наш проект. зависимости.

3. Фабрика соединений

При работе с базой данных нам нужна фабрика соединений. Так что, конечно же, нам понадобится то же самое с 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. Нам нужна эта аннотация, чтобы использовать функциональность Spring Data. Кроме того, мы расширяем AbstractR2dbcConfiguration, так как он предоставит много bean-компонентов, которые нам понадобятся позже.

4. Наше первое приложение R2DBC

Следующим шагом будет создание репозитория:

interface PlayerRepository extends ReactiveCrudRepository<Player, Integer> {}

Интерфейс ReactiveCrudRepository очень полезен. Он обеспечивает, например, базовую функциональность CRUD.

Наконец, мы определим класс нашей модели. Мы будем использовать Lombok, чтобы избежать шаблонного кода:

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

5. Тестирование

Пришло время протестировать наш код. Итак, давайте начнем с создания нескольких тестовых случаев:

@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. Пользовательские запросы

Мы также можем создавать собственные запросы. Чтобы добавить его, нам нужно изменить наш 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);

В дополнение к существующим тестам мы добавим тесты в наш недавно обновленный репозиторий:

@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. Пакеты ~~ ~ Еще одна особенность R2DBC — создание пакетов. Пакет полезен при выполнении нескольких операторов SQL, поскольку они будут выполняться лучше, чем отдельные операции.

Для создания пакета нам нужен объект Connection:

После того, как наше приложение создаст экземпляр пакета, мы можем добавить столько операторов SQL, сколько захотим. Чтобы выполнить его, мы вызовем метод execute(). Результатом пакета является издатель, который возвращает объект результата для каждого оператора.

Batch batch = connection.createBatch();

Итак, давайте перейдем к коду и посмотрим, как мы можем создать пакет:

8. Заключение

@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, который будет определять реактивный API для баз данных SQL. При использовании с Spring WebFlux R2DBC позволяет нам написать приложение, которое асинхронно обрабатывает данные сверху и до самой базы данных.

Как всегда, код доступен на GitHub.

«