
Accès aux données réactif (Spring Data R2DBC, MongoDB Reactive, Redis Reactive)
Apprenez à interagir avec vos bases de données relationnelles (R2DBC), NoSQL (MongoDB Reactive) et caches (Redis Reactive) de manière non bloquante dans Spring WebFlux.
Le défi : l'accès aux données dans un monde réactif
Adopter Spring WebFlux pour construire des applications réactives et non bloquantes est une excellente première étape. Cependant, pour réaliser pleinement les bénéfices de ce modèle, l'ensemble de la chaîne de traitement, y compris l'accès aux données, doit également être non bloquant. Utiliser des API traditionnelles d'accès aux données comme JDBC (Java Database Connectivity) ou même JPA (Java Persistence API) dans un environnement WebFlux est problématique.
JDBC et la plupart des implémentations JPA sont intrinsèquement bloquantes. Lorsqu'une requête est envoyée à la base de données, le thread de l'application attend activement la réponse. Dans un modèle WebFlux basé sur une boucle d'événements (event loop) avec un nombre limité de threads, bloquer l'un de ces threads précieux pour une opération I/O peut rapidement entraîner des goulots d'étranglement et anéantir les avantages de performance et de scalabilité recherchés.
Pour surmonter ce défi, il est nécessaire d'utiliser des pilotes (drivers) de base de données et des abstractions d'accès aux données qui sont eux-mêmes asynchrones et non bloquants. Spring Data, fidèle à sa philosophie d'abstraction, propose plusieurs modules dédiés à l'accès réactif aux données pour différentes technologies de stockage.
Spring Data R2DBC : L'accès réactif aux bases relationnelles
Pour les bases de données relationnelles (SQL), la réponse réactive est R2DBC (Reactive Relational Database Connectivity). R2DBC n'est pas un remplacement direct de JDBC, mais plutôt une spécification distincte définissant une API réactive pour interagir avec les bases SQL. Plusieurs fournisseurs de bases de données proposent désormais des pilotes compatibles R2DBC (par exemple, pour PostgreSQL, MySQL/MariaDB, MS SQL Server, H2).
Spring Data R2DBC s'appuie sur cette spécification pour offrir une expérience familière aux développeurs Spring Data. Il permet de définir des interfaces `Repository` qui retournent des types réactifs (`Mono` et `Flux`) de Project Reactor.
Pour commencer, ajoutez le starter `spring-boot-starter-data-r2dbc` et le pilote R2DBC spécifique à votre base de données :
Maven (exemple avec PostgreSQL) :
org.springframework.boot
spring-boot-starter-data-r2dbc
org.postgresql
r2dbc-postgresql
runtime
Configurez ensuite la connexion dans `application.properties`/`yml` en utilisant le préfixe `spring.r2dbc.*` :
# application.properties
spring.r2dbc.url=r2dbc:postgresql://localhost:5432/mydatabase
spring.r2dbc.username=user
spring.r2dbc.password=password
Définissez une entité simple et un repository réactif :
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Table("products") // Annotation R2DBC
record Product(@Id Long id, String name, double price) {}
interface ProductRepository extends ReactiveCrudRepository {
Flux findByNameContaining(String name);
}
Notez que les méthodes du `ReactiveCrudRepository` (comme `findById`, `findAll`, `save`) et les méthodes de requête dérivées retournent `Mono` ou `Flux`. Spring Data R2DBC gère également la gestion des transactions réactives via l'annotation `@Transactional` (utilisant un `ReactiveTransactionManager`).
Spring Data MongoDB Reactive : Accès non bloquant à MongoDB
Pour MongoDB, l'écosystème réactif est plus mature. MongoDB fournit officiellement un Reactive Streams Driver, qui permet une interaction asynchrone et non bloquante avec la base de données.
Spring Data MongoDB Reactive utilise ce pilote pour offrir une API réactive cohérente avec les autres modules Spring Data. Pour l'utiliser, incluez le starter `spring-boot-starter-data-mongodb-reactive` :
Maven :
org.springframework.boot
spring-boot-starter-data-mongodb-reactive
La configuration se fait via les propriétés `spring.data.mongodb.*` habituelles :
# application.properties
spring.data.mongodb.uri=mongodb://localhost:27017/mydatabase
La définition des documents et des repositories est très similaire à la version impérative, mais utilise des interfaces de repository réactives et retourne des `Mono`/`Flux` :
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Document(collection = "customers")
record Customer(@Id String id, String firstName, String lastName) {}
interface CustomerRepository extends ReactiveMongoRepository {
Flux findByLastName(String lastName);
Mono findByFirstNameAndLastName(String firstName, String lastName);
}
Spring Data MongoDB Reactive prend en charge un large éventail de fonctionnalités MongoDB, y compris les requêtes dérivées, les requêtes annotées (`@Query`), les opérations de `ReactiveMongoTemplate`, etc., le tout de manière non bloquante.
Spring Data Redis Reactive : Interaction réactive avec Redis
Pour interagir avec Redis de manière réactive, Spring Boot s'appuie par défaut sur le client Lettuce, qui est un client Redis avancé offrant des API synchrones, asynchrones et réactives. Le starter `spring-boot-starter-data-redis-reactive` inclut Lettuce et configure les composants nécessaires.
Maven :
org.springframework.boot
spring-boot-starter-data-redis-reactive
La configuration se fait via les propriétés `spring.redis.*` classiques :
# application.properties
spring.redis.host=localhost
spring.redis.port=6379
Spring Data Redis Reactive fournit principalement deux templates pour interagir avec Redis :
- `ReactiveRedisTemplate
` : Offre des opérations réactives pour travailler avec des objets sérialisés (par exemple, via Jackson). - `ReactiveStringRedisTemplate` : Une spécialisation pour travailler directement avec des clés et des valeurs de type String.
Exemple d'utilisation dans un service :
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
@Service
public class RedisCacheService {
private final ReactiveStringRedisTemplate redisTemplate;
public RedisCacheService(ReactiveStringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public Mono cacheValue(String key, String value) {
return redisTemplate.opsForValue().set(key, value);
}
public Mono getValue(String key) {
return redisTemplate.opsForValue().get(key);
}
public Mono deleteValue(String key) {
return redisTemplate.delete(key);
}
}
Toutes les opérations retournent des `Mono` ou `Flux`, garantissant une interaction non bloquante avec Redis. Il est également possible d'utiliser des repositories réactifs pour Redis dans certains cas, bien que l'utilisation des templates soit plus courante.
Intégration dans la logique applicative (Services et Contrôleurs)
L'utilisation de ces repositories et templates réactifs dans vos services et contrôleurs WebFlux suit les principes de la programmation réactive. Les méthodes de service retournent des `Mono` ou `Flux` et composent les opérations en utilisant les opérateurs de Reactor (`map`, `flatMap`, `filter`, `zip`, etc.).
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
public class ProductService {
private final ProductRepository productRepository; // Injection du repository R2DBC
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public Flux findAvailableProductsByName(String name) {
return productRepository.findByNameContaining(name)
.filter(product -> product.price() > 0); // Composition réactive
}
public Mono getProduct(Long id) {
return productRepository.findById(id);
}
public Mono createProduct(Mono productMono) {
// flatMap pour enchaîner les opérations asynchrones
return productMono.flatMap(productRepository::save);
}
}
// Contrôleur WebFlux utilisant le service
@RestController
@RequestMapping("/products")
class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) { this.productService = productService; }
@GetMapping("/{id}")
public Mono getProductById(@PathVariable Long id) {
return productService.getProduct(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono create(@RequestBody Mono product) {
return productService.createProduct(product);
}
}
L'élément clé est de maintenir la nature non bloquante de bout en bout. Chaque appel à un repository réactif retourne immédiatement un `Mono` ou `Flux`, et la logique est définie sous forme de pipeline d'opérations qui seront exécutées de manière asynchrone lorsque les données deviendront disponibles.
Conclusion : Une pile réactive complète
Spring Data, avec ses modules réactifs pour R2DBC, MongoDB et Redis, fournit les briques essentielles pour construire une pile applicative entièrement non bloquante avec Spring WebFlux. En adoptant ces technologies, vous pouvez interagir avec vos sources de données de manière efficace et scalable, en parfaite harmonie avec le modèle de programmation réactive.
Bien que l'écosystème réactif, notamment R2DBC, soit encore en évolution par rapport à ses homologues bloquants (JDBC/JPA), il offre déjà des solutions robustes pour de nombreux cas d'usage et constitue une base solide pour les applications modernes nécessitant haute performance et faible latence.