Unravel › Java concepts › Persistence and JPA
Reactive data access (R2DBC and reactive repositories)
Reactive repositories return Mono and Flux instead of blocking until the database answers, so a few threads can serve many concurrent requests while the database is working.
Learn these first
At a glance
A waiter who hands the order to the kitchen and serves other tables while waiting, instead of standing at the kitchen door until the dish is ready.
- 1A ReactiveCrudRepository<User, Long> works like CrudRepository but returns Mono<User> and Flux<User>Derived queries such as findByEmail work the same way.
- 2R2DBC is the reactive counterpart of JDBC: a non-blocking driver per databaseIt is not JPA: no lazy loading, no entity graph, no automatic joins; relations are loaded by explicit queries.
- 3Transactions use @Transactional on methods returning Mono/Flux, or TransactionalOperator programmaticallyThe transaction is attached to the subscription context, not to the thread.
- 4DatabaseClient and R2dbcEntityTemplate allow hand-written SQL and fluent queries when derived methods are not enough
- Mixing blocking JPA or JDBC calls into a reactive chain defeats the purpose and can freeze the event loop
- Streaming a large Flux straight to the client keeps memory flat, but a slow client holds a database connection open for its whole download
- R2DBC drivers use non-blocking sockets and a small connection pool
public interface UserRepository extends ReactiveCrudRepository<User, Long> { Mono<User> findByEmail(String email); Flux<User> findByActiveTrue();}Mono<User> created = users.findByEmail(email) .switchIfEmpty(users.save(new User(email)));- line 1 uses ReactiveCrudRepository, the core of Reactive data access (R2DBC and reactive repositories)
Read the full explanation (about 1 min)
Reactive repositories return Mono and Flux instead of blocking until the database answers, so a few threads can serve many concurrent requests while the database is working.
A mental model
A waiter who hands the order to the kitchen and serves other tables while waiting, instead of standing at the kitchen door until the dish is ready.
How it works
A ReactiveCrudRepository<User, Long> works like CrudRepository but returns Mono<User> and Flux<User>. Derived queries such as findByEmail work the same way.
R2DBC is the reactive counterpart of JDBC: a non-blocking driver per database. It is not JPA: no lazy loading, no entity graph, no automatic joins; relations are loaded by explicit queries.
Transactions use @Transactional on methods returning Mono/Flux, or TransactionalOperator programmatically. The transaction is attached to the subscription context, not to the thread.
DatabaseClient and R2dbcEntityTemplate allow hand-written SQL and fluent queries when derived methods are not enough.
Going deeper
Mixing blocking JPA or JDBC calls into a reactive chain defeats the purpose and can freeze the event loop. Pick one model per service.
Streaming a large Flux straight to the client keeps memory flat, but a slow client holds a database connection open for its whole download. Page or limit large exports.
Because there is no session, there is no dirty checking: you must call save explicitly, and updates replace fields you give them.
Under the hood
R2DBC drivers use non-blocking sockets and a small connection pool. The Spring Data layer maps rows to objects with the same mapping context idea as JDBC, and every repository method builds a lazy publisher that executes the SQL only on subscription.
Common mistakes
- Ignoring the returned Mono so the save never happens.
- Expecting JPA features (lazy loading, cascades) from R2DBC.
- Blocking calls inside the reactive chain.
- Assuming a transaction spans separate subscriptions.
How Unravel recognises it
When you paste code, Unravel looks for these signs of reactive data access (r2dbc and reactive repositories). Spotting them yourself is a good habit when you read unfamiliar code:
- Types:
ReactiveCrudRepositoryR2dbcRepositoryReactiveMongoRepositoryR2dbcEntityTemplateDatabaseClientReactiveMongoTemplateReactiveRedisTemplateTransactionalOperatorReactiveTransactionManagerReactiveElasticsearchRepositoryReactiveSortingRepository
Part of a learning path
- Reactive Java · previous: Reactive programming (Reactor Mono and Flux) · next: API gateways (Spring Cloud Gateway)
Related concepts
Frequently asked questions
What is Reactive data access (R2DBC and reactive repositories)?
Reactive repositories return Mono and Flux instead of blocking until the database answers, so a few threads can serve many concurrent requests while the database is working.
What are the most common mistakes with Reactive data access (R2DBC and reactive repositories)?
Ignoring the returned Mono so the save never happens. Expecting JPA features (lazy loading, cascades) from R2DBC. Blocking calls inside the reactive chain. Assuming a transaction spans separate subscriptions.
How can I see Reactive data access (R2DBC and reactive repositories) in real code?
Paste your own Java into Unravel and it will point out where reactive data access (r2dbc and reactive repositories) appears, explain how it works at the detail level you choose, and draw the flow of the code around it.