What is an R2dbcRepository, and what do its query methods return?
answer
- Extends ReactiveCrudRepository
- Mono = 0/1, Flux = 0..N
- Nothing runs until subscribe
- Non-blocking driver, no .block()
- No persistence context / lazy loading
basics
~10 sR2dbcRepository is Spring Data's reactive repository for relational databases. Its methods return reactive types: Mono<T> for zero-or-one result and Flux<T> for many, instead of blocking values or List.
solid answer
~40 sR2dbcRepository<T, ID> is the reactive equivalent of JpaRepository. It extends ReactiveCrudRepository (and ReactiveSortingRepository), so CRUD and derived query methods return Project Reactor publishers: Mono<T> for a single/optional result, Flux<T> for many, Mono<Void> for fire-and-forget, and Mono<Long> for counts. Nothing runs until something subscribes; in a WebFlux controller you return the publisher and the framework subscribes. It runs on a non-blocking R2DBC driver, so there is no JDBC thread-per-request blocking. You declare an interface extending R2dbcRepository and Spring generates the implementation. Derived queries (findByEmail) and @Query native SQL both work, but there is no JPA persistence context, no lazy loading, and no relationship mapping.
code
java · 15 linespublic interface UserRepository extends R2dbcRepository<User, Long> {
Flux<User> findByActiveTrue();
Mono<User> findByEmail(String email);
@Query("SELECT * FROM users WHERE created_at > :since")
Flux<User> createdAfter(Instant since);
}
// In a WebFlux controller you just return the publisher:
@GetMapping("/users/{id}")
public Mono<User> byId(@PathVariable Long id) {
return userRepository.findById(id); // framework subscribes
}go deeper
Know Mono vs Flux and that R2dbcRepository is the reactive repository.
Understand laziness/subscription and that there is no persistence context or lazy loading.
Can contrast with JPA feature-by-feature and reason about when R2DBC is the right choice.
Sets the policy: reactive end-to-end or not at all; weighs operational cost of losing ORM features against non-blocking throughput.
**R2DBC** (Reactive Relational Database Connectivity) is a specification for accessing relational databases with a fully non-blocking, back-pressure-aware driver — the reactive counterpart to JDBC. Spring Data R2DBC builds repositories on top of it. **The repository hierarchy.** You declare an interface such as `interface UserRepository extends R2dbcRepository<User, Long>`. `R2dbcRepository<T, ID>` extends `ReactiveCrudRepository<T, ID>` and `ReactiveSortingRepository<T, ID>`. Spring generates the implementation at startup. Because everything is reactive, method return types are **Project Reactor** publishers, never blocking values or `List`: - `Mono<T>` — a stream of 0 or 1 element (`findById`, `save`). - `Flux<T>` — a stream of 0..N elements (`findAll`, `findByStatus`). - `Mono<Void>` — completion signal only (`deleteById`). - `Mono<Long>` — a scalar such as `count()`. **Laziness.** A `Mono`/`Flux` is a description of work, not the work itself. No SQL is sent until something **subscribes**. In Spring WebFlux you return the publisher from the controller and the framework subscribes for you; if you call a repository and never subscribe (and never return it), nothing happens. Never call `.block()` on the event-loop thread — that defeats the purpose and can deadlock the small event-loop thread pool. **What backs it.** A non-blocking driver such as `r2dbc-postgresql`, `r2dbc-h2`, or `r2dbc-mysql`, usually behind `r2dbc-pool` for connection pooling. Configuration is via `spring.r2dbc.url`/`username`/`password`, and Spring Boot auto-configures a `ConnectionFactory`. **What you get.** Derived query methods (`findByEmailAndActiveTrue`), paging via `Pageable` (returning `Flux` + a separate `count`), sorting, and custom SQL via `@Query("SELECT ... WHERE email = :email")` (native SQL only — there is no JPQL in R2DBC). **What you do NOT get (vs JPA).** No persistence context / first-level cache, no dirty checking (you must call `save` explicitly), no lazy loading, and no automatic relationship mapping (`@OneToMany`/`@ManyToOne` do not exist here). Joins and aggregates are composed by hand or written as SQL. This makes R2DBC simpler and more predictable but shifts more mapping work to you. **When to use it.** Choose R2DBC only when your whole request path is reactive (WebFlux) and you need non-blocking DB access for high concurrency with few threads. If any layer blocks, or you rely on rich ORM features, classic JPA/JDBC is usually the better fit.
- Why must you avoid calling .block() inside a WebFlux request?WebFlux runs on a small event-loop thread pool. Blocking a loop thread stalls all requests it serves and can deadlock if it waits on work scheduled to the same pool. It reintroduces the thread-per-request cost R2DBC exists to avoid.
- If a repository method returns Flux<User> but no one subscribes, what happens?Nothing. The Flux is only a description of the query; without a subscriber no SQL is sent to the database. Returning it from a controller is what triggers the framework to subscribe.