前回、AWS LambdaとSpring Cloud Functionを使ったサーバーレス環境へのデプロイを学びました。これらのノンブロッキングな環境でシステムの性能を最大限に引き出すためには、データアクセス層もリアクティブである必要があります。
これまでの講座で使ってきた Spring Data JPA は、裏側でJDBC(Java Database Connectivity)を使っており、これは本質的に**ブロッキング(同期I/O)**処理です。ControllerやService層がノンブロッキング(WebFlux)であっても、DBアクセスでスレッドがブロックされてしまうと、その性能上の利点は失われてしまいます。
今回は、このボトルネックを解消するための新しいデータアクセス技術 **R2DBC(Reactive Relational Database Connectivity)**と、Spring Bootでの使い方を学びます。
1. R2DBCとは?
R2DBC は、リレーショナルデータベース(RDB)接続のためのリアクティブな(ノンブロッキングな)ドライバー仕様です。これは、従来の JDBC をリアクティブ環境に適応させるためにゼロから設計されました。
- 課題: JDBCは設計上スレッドをブロックするように作られており、WebFlux(第48回)環境で利用すると、そのノンブロッキングのメリットを失ってしまいます。
- R2DBCの解決策: R2DBCは、データアクセス中にスレッドをブロックせず、データが利用可能になったらイベントとして通知する仕組みを採用しています。これにより、WebFlux環境全体で一貫したノンブロッキングなデータフローが実現し、スレッド利用効率が大幅に向上します。
2. Spring Data R2DBCの導入
Spring BootアプリケーションでR2DBCを利用するには、専用のスターターと対応するデータベースドライバーが必要です。
2-1. 依存関係の追加
プロジェクトに、Spring Data R2DBCのスターターと、対象DB(例:PostgreSQL)のR2DBCドライバーを追加します。
Groovy
dependencies {
// WebFluxのスターター(リアクティブ環境の基盤)
implementation 'org.springframework.boot:spring-boot-starter-webflux'
// Spring Data R2DBCのスターター
implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
// PostgreSQLのR2DBCドライバー
runtimeOnly 'io.r2dbc:r2dbc-postgresql'
// ...
}
2-2. 設定の変更
従来のJDBC接続設定(spring.datasource.urlなど)の代わりに、R2DBC用の接続URLを設定します。
YAML
spring:
r2dbc:
# URLのスキーマが 'r2dbc' で始まる
url: r2dbc:postgresql://localhost:5432/myapp_db
username: user
password: password
3. リアクティブなデータアクセスの実装
Spring Data R2DBCでは、従来のJPAと同様にRepositoryインターフェースを定義しますが、使用するインターフェースと戻り値の型が異なります。
3-1. エンティティの定義
エンティティクラスはJPAとほぼ同じですが、jakarta.persistence の代わりに org.springframework.data.annotation.Id を使います。
Java
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import lombok.Data;
@Data
@Table("items") // R2DBCでは @Table を使用
public class Item {
@Id // R2DBCでは @Id を使用
private Long id;
private String name;
private Integer stock;
// ...
}
3-2. リアクティブなRepository
JpaRepository の代わりに R2dbcRepository を継承します。すべてのメソッドの戻り値は、非同期なデータフローを表現する Mono または Flux(第48回)になります。
Java
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface ItemRepository extends R2dbcRepository<Item, Long> {
// findByIdの戻り値は Mono<Item>
Mono<Item> findByName(String name);
// findAllの戻り値は Flux<Item>
Flux<Item> findByStockGreaterThan(Integer stock);
}
4. Service層とController層での連携
Service層では、Repositoryから返されるMono/Fluxをメソッドチェーンで繋ぎ、非同期処理を継続させます。
Java
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
@Service
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
public Mono<Item> createNewItem(String name) {
// Mono<Item> を返し、DB処理をブロックしない
Item newItem = new Item(name, 0);
return itemRepository.save(newItem);
}
public Mono<Item> getItem(Long id) {
// DBから取得し、結果が返ってくるまでスレッドはブロックされない
return itemRepository.findById(id)
.switchIfEmpty(Mono.error(new ItemNotFoundException("商品が見つかりません")));
}
}
Controller層でもMono/Fluxをそのまま返せば、WebFluxがノンブロッキングな形でレスポンスを組み立て、クライアントに返送します。
5. まとめ:未来志向のデータアクセス
R2DBCは、Spring Data JPAと比べて歴史は浅いですが、WebFluxを採用するアプリケーションにとっては、性能のボトルネックを完全に解消する必須の技術です。高性能、高スループットを求めるモダンなシステム設計において、R2DBCはデータベースアクセスにおける未来の標準となるでしょう。
✅ 本日のまとめ
- R2DBCは、従来のJDBCが持つ**ブロッキング(同期I/O)の課題を解決し、RDB接続をリアクティブな(ノンブロッキングな)**データフローにするための仕様である。
- R2DBCを利用することで、WebFlux環境全体でスレッドをブロックすることなく、高スループットを実現できる。
- Spring Data R2DBCでは
R2dbcRepositoryを継承し、すべてのデータアクセスメソッドは Mono または Flux を返す。 - R2DBC環境でのデータアクセスは、Service層でMono/Fluxのメソッドチェーンを繋げる形で実装される。
【Spring Framework 50回講座 完】 📖✨


コメント