Gymterview
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, которая проще и покрывает большинство сценариев.