R2DBC vs JDBC — Reactive vs Blocking Database Access
Fast recall
Quick revision
Core answer
Mono / Flux and the calling thread is free while the network round-trip happens. JDBC has 25 years of tooling — JPA, Hibernate, Spring Data JPA, every connection pool, every migration tool, every monitoring agent. R2DBC has none of JPA's caching or relationship management. With virtual threads (JDK 21+), JDBC code runs on cheap virtual threads that park on blocking I/O without blocking carriers — closing much of the original concurrency gap. Today the choice is rarely about raw throughput — it's about whether you're running reactive end-to-end or whether you need JPA's ORM features.Whether the candidate can articulate the trade-offs in 2026 — including how virtual threads change the JDBC story
Assuming R2DBC is always faster than JDBC; ignoring the JPA-feature gap; forgetting that migrations still need JDBC even with R2DBC
Discuss connection pool tuning differences, streaming row patterns, transaction semantics, and how virtual threads close the gap
Interview-ready guide
Interview answer
JDBC and R2DBC speak the same SQL to the same databases — the difference is the threading model and the ecosystem around them.
JDBC
- Blocking; every query parks the calling thread
- 25 years of tooling — JPA, Hibernate, every connection pool, every migration tool
- Scales by adding threads; pool size and thread pool are the limits
- With virtual threads (JDK 21+), blocking calls park cheaply and free the carrier — concurrency is no longer the wall it used to be
R2DBC
- Reactive, non-blocking; queries return
Mono/Flux - Calling thread is free during the network round-trip
- Native streaming —
Fluxwith end-to-end backpressure - No JPA equivalent; relationships are manual; no first-level cache
- Migrations still use JDBC — Flyway and Liquibase have no reactive flavor
Transaction semantics
- JDBC —
ThreadLocal-bound; works with virtual threads - R2DBC — reactive-context-bound; only works inside the reactive chain
- Mixing blocking and reactive across a transaction is a footgun
When JDBC + virtual threads wins
- Standard CRUD with JPA features
- Complex transactional workflows
- Team familiar with blocking code
- Most modern services in 2026
When R2DBC wins
- Reactive WebFlux end-to-end
- Streaming millions of rows with backpressure
- Postgres LISTEN/NOTIFY pub/sub
- Reactive Kafka / Redis already in the pipeline
Pitfalls
- JDBC inside a WebFlux handler stalls the event loop
- Mixing JPA and R2DBC in one service is painful
- Believing R2DBC is faster — usually the gap is small at moderate concurrency
- Forgetting Flyway needs JDBC even on R2DBC
@Transactionalwith the wrong tx manager doesn't propagate across reactive chains
Strategic answer
- The choice is rarely about raw throughput — it's about reactive-all-the-way vs JPA features
- Virtual threads have tilted the default back toward JDBC for most services
- Pick R2DBC for specific reasons — streaming, LISTEN/NOTIFY, reactive end-to-end — not by default
R2DBC vs JDBC — Reactive vs Blocking Database Access
Direct Answer
JDBC is the classic blocking SQL API — every query parks the calling thread until the database responds. R2DBC is the reactive, non-blocking equivalent — queries return Mono / Flux and the calling thread is free while the network round-trip happens. JDBC has 25 years of tooling — JPA, Hibernate, Spring Data JPA, every connection pool, every migration tool, every monitoring agent. R2DBC has none of JPA's caching or relationship management. With virtual threads (JDK 21+), JDBC code runs on cheap virtual threads that park on blocking I/O without blocking carriers — closing much of the original concurrency gap. Today the choice is rarely about raw throughput — it's about whether you're running reactive end-to-end or whether you need JPA's ORM features.
Common Mistakes & Pitfalls
Assuming R2DBC is always faster than JDBC; ignoring the JPA-feature gap; forgetting that migrations still need JDBC even with R2DBC
Check your understanding
Answer these without looking back at the article.
- How do virtual threads change the JDBC vs R2DBC decision?
- What does Flyway need from JDBC when the runtime uses R2DBC?
- Why does mixing blocking and reactive break transactions?
- How do you stream a 10-million-row query with each API?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.