The main difference is that JDBC/JPA uses blocking I/O which means each request needs a dedicated thread. In highly concurrent systems this can easily lead to scaling problems.
On the other hand R2DBC uses non-blocking I/O, which means it is able to handle requests with only a fixed, low number of threads, which makes scaling easier and cheaper.
Check the following article:
https://spring.io/blog/2018/12/07/reactive-programming-and-relational-databases
Java uses JDBC as the primary technology to integrate with relational
databases. JDBC is of a blocking nature – there’s nothing sensible one
could do to mitigate the blocking nature of JDBC. The first idea for
how to make calls non-blocking is offloading JDBC calls to an Executor
(typically Thread pool). While this approach somewhat works, it comes
with several drawbacks that neglect the benefits of a reactive
programming model.
Thread pools require – no surprise – threads to run. Reactive runtimes
typically use a limited number of threads that match the number of CPU
cores. Additional threads introduce overhead and reduce the effect of
thread limiting. Additionally, JDBC calls typically pile up in a
queue, and once the threads are saturated with requests, the pool will
block again. So, JDBC is right now not an option.