middle
Что такое R2DBC и как он связан с реактивным программированием?
R2DBC (Reactive Relational Database Connectivity) — спецификация для реактивного, неблокирующего доступа к реляционным базам данных, решающая проблему блокирующего JDBC в реактивном стеке.
Проблема JDBC в реактивном контексте
JDBC полностью блокирующий — каждый запрос к БД блокирует поток до получения ответа. В реактивных приложениях (WebFlux + Netty) это блокирует event-loop поток и нивелирует все преимущества реактивности.
Архитектура
Пример
Приложение -> Spring Data R2DBC -> R2DBC SPI -> R2DBC Driver -> База данных
Поддерживаемые базы данных
- PostgreSQL (
r2dbc-postgresql) - MySQL / MariaDB (
r2dbc-mysql,r2dbc-mariadb) - Microsoft SQL Server (
r2dbc-mssql) - H2 (
r2dbc-h2) - Oracle (
oracle-r2dbc)
Настройка и использование Spring Data R2DBC
spring:
r2dbc:
url: r2dbc:postgresql://localhost:5432/mydb
username: user
password: secret
// Сущность
@Table("users")
public record User(
@Id Long id,
String name,
String email
) {}
// Реактивный репозиторий
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
Flux<User> findByName(String name);
@Query("SELECT * FROM users WHERE email = :email")
Mono<User> findByEmail(String email);
}
// Использование в сервисе
@Service
public class UserService {
private final UserRepository userRepository;
public Mono<User> createUser(User user) {
return userRepository.save(user);
}
public Flux<User> findAll() {
return userRepository.findAll();
}
}
DatabaseClient — низкоуровневый доступ
@Service
public class CustomUserRepository {
private final DatabaseClient databaseClient;
public Flux<User> searchUsers(String query) {
return databaseClient.sql("SELECT * FROM users WHERE name LIKE :query")
.bind("query", "%" + query + "%")
.map(row -> new User(
row.get("id", Long.class),
row.get("name", String.class),
row.get("email", String.class)))
.all();
}
}
R2DBC vs JDBC
| Критерий | JDBC | R2DBC |
|---|---|---|
| Модель | Блокирующая | Неблокирующая, реактивная |
| Возвращаемые типы | ResultSet, List | Mono, Flux |
| Пул соединений | HikariCP | r2dbc-pool |
| ORM | Hibernate/JPA | Spring Data R2DBC (без lazy loading) |
| Зрелость | 25+ лет | ~5 лет |
| Транзакции | @Transactional | @Transactional (реактивный менеджер) |
Частые ошибки
- Ожидать от R2DBC возможностей JPA — нет lazy loading, entity graph, dirty checking
- Использовать блокирующие драйверы рядом с R2DBC — один блокирующий вызов нивелирует реактивность
- Не настраивать пул соединений — без r2dbc-pool создаётся новое соединение на каждый запрос
- Сложные JOIN-запросы — R2DBC не маппит связи автоматически; нужен DatabaseClient или проекции
Как используется в 2026
- R2DBC зрелый для PostgreSQL и MySQL
- С Virtual Threads (Java 21) обычный JDBC + HikariCP — конкурентная альтернатива
- R2DBC оправдан в полностью реактивных приложениях (WebFlux + reactive messaging + R2DBC)
- Тренд: для новых проектов без жёстких требований к реактивности — JDBC + Virtual Threads проще
На собеседовании: ключевое — объяснить, зачем нужен R2DBC (JDBC блокирует event-loop в WebFlux) и чем он отличается от JPA (нет lazy loading, нет каскадирования). Частая ошибка — не упомянуть альтернативу: JDBC + Virtual Threads, которая проще и покрывает большинство сценариев.