1. 序章

R2DBC (リアクティブリレーショナルデータベース接続)は、Spring OnePlatform2018中にPivotalによって提示された取り組みです。 SQLデータベースへのリアクティブAPIを作成することを目的としています。

つまり、この作業により、完全に非ブロッキングのドライバーを使用してデータベース接続が作成されます。

このチュートリアルでは、Spring DataR2BDCを使用したアプリケーションの例を見ていきます。 より低レベルのR2DBCAPIのガイドについては、以前の記事をご覧ください。

2. 私たちの最初のSpringDataR2DBCプロジェクト

そもそも、R2DBCプロジェクトはごく最近のものです。 現時点では、 PostGres、MSSQL、およびH2のみがR2DBCドライバーを備えています。さらに、すべてのSpring Boot機能を使用することはできません。 したがって、手動で追加する必要のあるいくつかの手順があります。 ただし、SpringDataなどのプロジェクトを活用して支援することができます。

最初に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機能を使用するには、このアノテーションが必要です。 さらに、後で必要になる多くのBeanを提供するため、AbstractR2dbcConfigurationを拡張しています。

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のもう1つの機能は、バッチを作成することです。 バッチは、個々の操作よりもパフォーマンスが高いため、複数のSQLステートメントを実行する場合に役立ちます。

バッチを作成するには、接続オブジェクトが必要です。

Batch batch = connection.createBatch();

アプリケーションがBatchインスタンスを作成した後、必要な数のSQLステートメントを追加できます。 これを実行するには、 execute()メソッドを呼び出します。 バッチの結果はPublisherであり、各ステートメントの結果オブジェクトを返します。

それでは、コードに飛び込んで、バッチを作成する方法を見てみましょう。

@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. 結論

要約すると、R2DBCはまだ初期段階にあります。 これは、SQLデータベースへのリアクティブAPIを定義するSPIを作成する試みです。 Spring WebFlux と併用すると、R2DBCを使用して、データを上からデータベースに至るまで非同期で処理するアプリケーションを作成できます。

いつものように、コードはGitHubで入手できます。