Start Learning
Javaneer
Back to stage
Module 6·Reactive & Messaging

Reactive Data with R2DBC

Talking to a database without blocking, so the whole request pipeline stays reactive end to end.

14 min readAdvanced
On this page

A reactive web layer is only half the story. If your controller returns a Flux but then calls blocking JPA, a database thread is tied up per query and the non-blocking benefit evaporates. To stay reactive from HTTP request to database and back, you need a non-blocking database driver: R2DBC (Reactive Relational Database Connectivity).

Reactive repositories

Spring Data R2DBC mirrors the Spring Data JPA model you know, but every method returns a publisher:

interface BookRepository extends ReactiveCrudRepository<Book, Long> {
    Mono<Book> findByIsbn(String isbn);          // one, asynchronously
    Flux<Book> findByAuthor(String author);      // many, streamed
}

save, findById, findAll all return Mono/Flux instead of blocking and returning a value directly. The database call never parks a thread; results flow back as they're ready.

A conveyor belt, not a bucket

Blocking JDBC is fetching water in a bucket: you walk to the well, wait for it to fill, and carry it back - standing idle the whole time. R2DBC is a conveyor belt from the well: you start it and walk away, and water arrives continuously without anyone standing and waiting. The thread isn't held hostage by the round trip.

The catch: no JPA

R2DBC is not JPA/Hibernate. There's no entity manager, no lazy loading, no automatic relationship graphs, no @OneToMany navigation. You get a lighter, more explicit mapping and you handle relationships yourself. That's a real cost - you give up a lot of ORM convenience to gain non-blocking access.

Only go reactive if the whole chain does

The rule from the last lesson applies at the data layer too: mixing blocking JPA into a reactive pipeline blocks the event loop. If you commit to WebFlux, commit to R2DBC (or another reactive driver) as well - and accept trading Hibernate's conveniences for non-blocking I/O. If you don't need that, stay on JPA.

Complete the reactive chain

A team builds a WebFlux endpoint that returns Flux<Book> but fetches data with a normal JPA BookRepository. Under high load it performs no better than the blocking version - and sometimes worse. Explain why, and what single change fixes it (and what they give up).

Why does a reactive (WebFlux) pipeline need R2DBC rather than JPA/JDBC?

Key takeaways

  • To be reactive end to end, the data layer must be non-blocking too - that's R2DBC.
  • Spring Data R2DBC mirrors Spring Data JPA but repository methods return Mono/Flux.
  • R2DBC is not JPA: no entity manager, lazy loading, or automatic relationship graphs - you map more explicitly.
  • Mixing blocking JPA into a reactive pipeline blocks the event loop and can perform worse than plain MVC.
  • Adopt R2DBC only when you've committed to reactive; otherwise JPA's conveniences win.
Was this lesson helpful?
Edit this page on GitHub