このガイドでは、Spring Data R2DBC を使用して、リアクティブデータベースドライバーを使用してリレーショナルデータベースにデータを格納および取得するアプリケーションを構築するプロセスについて説明します。

構築する

Customer POJO(Plain Old Java Objects)をメモリベースのデータベースに保存するアプリケーションを構築します。

必要なもの

このガイドを完了する方法

ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。

最初から始めるには、Spring Initializr から開始に進みます。

基本スキップするには、次の手順を実行します。

完了したときは、gs-accessing-data-r2dbc/complete のコードに対して結果を確認できます。

Spring Initializr から開始

すべての Spring アプリケーションでは、Spring Initializr (英語) から始める必要があります。Initializr は、アプリケーションに必要なすべての依存関係をすばやく取り込む方法を提供し、多くのセットアップを行います。この例では、R2DBC と H2 の依存関係が必要です。

必要な依存関係を含む Maven ビルドファイルを Spring Initializr (英語) から直接取得できます。

次のリストは、Maven を選択したときに作成される pom.xml ファイルを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>accessing-data-r2dbc</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>accessing-data-r2dbc</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-r2dbc</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>io.r2dbc</groupId>
			<artifactId>r2dbc-h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>io.projectreactor</groupId>
			<artifactId>reactor-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

必要な依存関係を含む Gradle ビルドファイルを Spring Initializr (英語) から直接取得できます。次のリストは、Gradle を選択したときに作成される build.gradle ファイルを示しています。

plugins {
	id 'org.springframework.boot' version '2.4.2'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
	runtimeOnly 'com.h2database:h2'
	runtimeOnly 'io.r2dbc:r2dbc-h2'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'io.projectreactor:reactor-test'
}

test {
	useJUnitPlatform()
}

スキーマを定義する

この例では、それぞれ R2DBC エンティティとしてアノテーションが付けられた Customer オブジェクトを保存します。次のリストは、SQL スキーマクラス(src/main/resources/schema.sql 内)を示しています。

CREATE TABLE customer (id SERIAL PRIMARY KEY, first_name VARCHAR(255), last_name VARCHAR(255));

ここに、3 つの列 idfirst_name と last_name を持つ customer テーブルがあります。id 列は自動インクリメントされ、他の列はデフォルトのスネークケースの命名スキームに従います。その後、アプリケーションの起動時に ConnectionFactoryInitializer を登録して schema.sql ファイルを取得し、データベーススキーマを初期化する必要があります。H2 ドライバーはクラスパス上にあり、接続 URL を指定していないため、Spring Boot は組み込み H2 データベースを起動します。

単純なエンティティを定義する

この例では、それぞれ R2DBC エンティティとしてアノテーションが付けられた Customer オブジェクトを保存します。次のリストは、Customer クラス(src/main/java/com/example/accessingdatar2dbc/Customer.java 内)を示しています。

package com.example.accessingdatar2dbc;

import org.springframework.data.annotation.Id;

public class Customer {

    @Id
    private Long id;

    private final String firstName;

    private final String lastName;

    public Customer(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return this.firstName;
    }

    public String getLastName() {
        return this.lastName;
    }

    @Override
    public String toString() {
        return String.format(
            "Customer[id=%d, firstName='%s', lastName='%s']",
            id, firstName, lastName);
    }
}

ここに、idfirstName と lastName の 3 つの属性を持つ Customer クラスがあります。Customer クラスには最小限のアノテーションが付けられています。id プロパティには @Id のアノテーションが付けられているため、Spring Data R2DBC は主キーを識別できます。デフォルトでは、主キーは INSERT 上のデータベースによって生成されると想定されています。

他の 2 つのプロパティ firstName と lastName にはアノテーションが付けられていません。これらは、プロパティ自体と同じ名前を共有する列にマップされると想定されています。

便利な toString() メソッドは、顧客のプロパティを出力します。

単純なクエリを作成する

Spring Data R2DBC は、リレーショナルデータベースにデータを格納するための基盤となるテクノロジーとして R2DBC を使用することに重点を置いています。その最も魅力的な機能は、実行時にリポジトリインターフェースからリポジトリ実装を作成する機能です。

これがどのように機能するかを確認するには、次のリスト(src/main/java/com/example/accessingdatar2dbc/CustomerRepository.java 内)が示すように、Customer エンティティで動作するリポジトリインターフェースを作成します。

package com.example.accessingdatar2dbc;

import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;

import reactor.core.publisher.Flux;

public interface CustomerRepository extends ReactiveCrudRepository<Customer, Long> {

    @Query("SELECT * FROM customer WHERE last_name = :lastname")
    Flux<Customer> findByLastName(String lastName);

}

CustomerRepository は ReactiveCrudRepository インターフェースを継承します。動作するエンティティのタイプと ID Customer および Long は、ReactiveCrudRepository のジェネリックパラメーターで指定されます。ReactiveCrudRepositoryCustomerRepository を継承することにより、リアクティブ型を使用して Customer エンティティを保存、削除、検索する方法など、Customer 永続性を操作するためのいくつかの方法を継承します。

Spring Data R2DBC では、@Query でこれらにアノテーションを付けることにより、他のクエリメソッドを定義することもできます。例: CustomerRepository には findByLastName() メソッドが含まれます。

典型的な Java アプリケーションでは、CustomerRepository を実装するクラスを作成することを期待できます。ただし、それが Spring Data R2DBC を非常に強力にしている理由です。リポジトリインターフェースの実装を記述する必要はありません。Spring Data R2DBC は、アプリケーションの実行時に実装を作成します。

これで、この例を接続して、その外観を確認できます!

アプリケーションクラスを作成する

Spring Initializr は、アプリケーションの単純なクラスを作成します。次のリストは、Initializr がこの例(src/main/java/com/example/accessingdatar2dbc/AccessingDataR2dbcApplication.java 内)で作成したクラスを示しています。

package com.example.accessingdatar2dbc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AccessingDataR2dbcApplication {

	public static void main(String[] args) {
		SpringApplication.run(AccessingDataR2dbcApplication.class, args);
	}

}

@SpringBootApplication は、次のすべてを追加する便利なアノテーションです。

  • @Configuration: アプリケーションコンテキストの Bean 定義のソースとしてクラスにタグを付けます。

  • @EnableAutoConfiguration: クラスパス設定、他の Bean、さまざまなプロパティ設定に基づいて Bean の追加を開始するよう Spring Boot に指示します。例: spring-webmvc がクラスパスにある場合、このアノテーションはアプリケーションに Web アプリケーションとしてフラグを立て、DispatcherServlet のセットアップなどの主要な動作をアクティブにします。

  • @ComponentScan: Spring に、com/example パッケージ内の他のコンポーネント、構成、サービスを探して、コントローラーを検出させるように指示します。

main() メソッドは、Spring Boot の SpringApplication.run() メソッドを使用してアプリケーションを起動します。XML が 1 行もないことに気付きましたか? web.xml ファイルもありません。この Web アプリケーションは 100% 純粋な Java であり、接続機能やインフラストラクチャの構成に対処する必要はありませんでした。

ここで、Initializr が作成した単純なクラスを変更する必要があります。(この例ではコンソールに)出力を取得するには、ロガーを設定する必要があります。次に、スキーマと一部のデータをセットアップし、それを使用して出力を生成するために、イニシャライザをセットアップする必要があります。次のリストは、完成した AccessingDataR2dbcApplication クラス(src/main/java/com/example/accessingdatar2dbc/AccessingDataR2dbcApplication.java 内)を示しています。

package com.example.accessingdatar2dbc;

import io.r2dbc.spi.ConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.r2dbc.connectionfactory.init.ConnectionFactoryInitializer;
import org.springframework.data.r2dbc.connectionfactory.init.ResourceDatabasePopulator;

import java.time.Duration;
import java.util.Arrays;

@SpringBootApplication
public class AccessingDataR2dbcApplication {

    private static final Logger log = LoggerFactory.getLogger(AccessingDataR2dbcApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(AccessingDataR2dbcApplication.class, args);
    }

    @Bean
    ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {

        ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
        initializer.setConnectionFactory(connectionFactory);
        initializer.setDatabasePopulator(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));

        return initializer;
    }

    @Bean
    public CommandLineRunner demo(CustomerRepository repository) {

        return (args) -> {
            // save a few customers
            repository.saveAll(Arrays.asList(new Customer("Jack", "Bauer"),
                new Customer("Chloe", "O'Brian"),
                new Customer("Kim", "Bauer"),
                new Customer("David", "Palmer"),
                new Customer("Michelle", "Dessler")))
                .blockLast(Duration.ofSeconds(10));

            // fetch all customers
            log.info("Customers found with findAll():");
            log.info("-------------------------------");
            repository.findAll().doOnNext(customer -> {
                log.info(customer.toString());
            }).blockLast(Duration.ofSeconds(10));

            log.info("");

            // fetch an individual customer by ID
			repository.findById(1L).doOnNext(customer -> {
				log.info("Customer found with findById(1L):");
				log.info("--------------------------------");
				log.info(customer.toString());
				log.info("");
			}).block(Duration.ofSeconds(10));


            // fetch customers by last name
            log.info("Customer found with findByLastName('Bauer'):");
            log.info("--------------------------------------------");
            repository.findByLastName("Bauer").doOnNext(bauer -> {
                log.info(bauer.toString());
            }).blockLast(Duration.ofSeconds(10));;
            log.info("");
        };
    }

}

AccessingDataR2dbcApplication クラスには、CustomerRepository をいくつかのテストにかける main() メソッドが含まれています。まず、Spring アプリケーションコンテキストから CustomerRepository をフェッチします。次に、Customer オブジェクトをいくつか保存し、save() メソッドのデモと使用するデータのセットアップを行います。次に、findAll() を呼び出して、データベースからすべての Customer オブジェクトをフェッチします。次に、findById() を呼び出して、その ID で単一の Customer をフェッチします。最後に、findByLastName() を呼び出して、姓が「Bauer」であるすべての顧客を検索します。

R2DBC は、リアクティブプログラミングテクノロジです。同時に、同期された必須のフローで使用しているため、各コールを block(…) メソッドのバリアントと同期する必要があります。通常のリアクティブアプリケーションでは、結果の Mono または Flux は、呼び出しスレッドをブロックせずにリアクティブシーケンスにサブスクライブする Web コントローラーまたはイベントプロセッサーに返されるオペレーターのパイプラインを表します。

デフォルトでは、Spring Boot は R2DBC リポジトリサポートを有効にし、@SpringBootApplication が配置されているパッケージ(およびそのサブパッケージ)を検索します。構成の R2DBC リポジトリインターフェース定義が非表示のパッケージに配置されている場合、@EnableR2dbcRepositories とそのタイプセーフ basePackageClasses=MyRepository.class パラメーターを使用して代替パッケージを指摘できます。

実行可能 JAR を構築する

コマンドラインから Gradle または Maven を使用してアプリケーションを実行できます。必要なすべての依存関係、クラス、リソースを含む単一の実行可能 JAR ファイルを構築して実行することもできます。実行可能な jar を構築すると、開発ライフサイクル全体、さまざまな環境などで、アプリケーションとしてサービスを簡単に提供、バージョン管理、デプロイできます。

Gradle を使用する場合、./gradlew bootRun を使用してアプリケーションを実行できます。または、次のように、./gradlew build を使用して JAR ファイルをビルドしてから、JAR ファイルを実行できます。

java -jar build/libs/gs-accessing-data-r2dbc-0.1.0.jar

Maven を使用する場合、./mvnw spring-boot:run を使用してアプリケーションを実行できます。または、次のように、./mvnw clean package で JAR ファイルをビルドしてから、JAR ファイルを実行できます。

java -jar target/gs-accessing-data-r2dbc-0.1.0.jar
ここで説明する手順は、実行可能な JAR を作成します。クラシック WAR ファイルを作成することもできます。

アプリケーションを実行すると、次のような出力が表示されます。

== Customers found with findAll():
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=2, firstName='Chloe', lastName='O'Brian']
Customer[id=3, firstName='Kim', lastName='Bauer']
Customer[id=4, firstName='David', lastName='Palmer']
Customer[id=5, firstName='Michelle', lastName='Dessler']

== Customer found with findOne(1L):
Customer[id=1, firstName='Jack', lastName='Bauer']

== Customer found with findByLastName('Bauer'):
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=3, firstName='Kim', lastName='Bauer']

要約

おめでとう!具体的なリポジトリ実装を作成せずに、Spring Data R2DBC を使用してオブジェクトをデータベースに保存したり、データベースからフェッチしたりする単純なアプリケーションを作成しました。

関連事項

次のガイドも役立ちます。

新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください: GitHub (英語)

すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の Attribution、NoDerivatives クリエイティブコモンズライセンス (英語) でリリースされています。