life is too short for a diary




Calling Stored Procedure in Spring using jdbc vs r2dbc

Tags: spring-boot jdbc r2dbc java

Author
Written by: Tushar Sharma

Calling stored procedures from Spring Boot? You have two paths: JDBC (blocking) or R2DBC (reactive).

What's a stored procedure

e.g.

The Two Paths: JDBC vs R2DBC

There are two distinct stacks for database access in Java:

  JDBC (Blocking) R2DBC (Reactive)
Driver JDBC Driver R2DBC Driver
Connection DataSource ConnectionFactory
Client API JdbcTemplate, SimpleJdbcCall DatabaseClient
ORM Spring Data JPA Spring Data R2DBC
Annotation @Procedure @Query

Key insight: These are separate stacks. You can't mix them - JdbcTemplate only works with JDBC drivers, and DatabaseClient only works with R2DBC drivers.

Where Does Hibernate Fit?

In the JDBC stack, there's an important layer between JdbcTemplate and Spring Data JPA:

JPA (Java Persistence API) is just a specification - interfaces that define what an ORM should do. No actual code.

Hibernate is an implementation of JPA - the actual code that does the work. EclipseLink is another implementation.

Hibernate does NOT work with R2DBC. It's built on JDBC (blocking). That's why @Procedure only exists in the JDBC stack.

ORM vs Repository

These are different concepts:

Repository = A pattern for abstracting data access (find, save, delete). Both stacks have this.

ORM (Object-Relational Mapping) = Maps Java objects ↔ database tables with advanced features:

Feature JDBC + Hibernate R2DBC
Repository JpaRepository ReactiveCrudRepository
ORM Hibernate (full) None
Entity mapping @Entity, @OneToMany @Table, @Id (basic)
Lazy loading Yes No
Relationships Automatic Manual
Caching Yes No

R2DBC is more "direct" - it talks to the database through drivers without a heavy ORM layer. Less magic, more control, but more manual work for complex scenarios.

How to Tell Which Stack You're Using

Quick ways to identify the stack in code:

See this? You're using
DataSource JDBC
ConnectionFactory R2DBC
@Entity JPA/Hibernate (JDBC)
@Table (without @Entity) R2DBC
JdbcTemplate JDBC
DatabaseClient R2DBC
JpaRepository JPA/Hibernate (JDBC)
ReactiveCrudRepository R2DBC
@Procedure JPA (JDBC only)
Returns Mono/Flux R2DBC

Connection management is different:

You cannot use DataSource with DatabaseClient - they're from different worlds.

DriverManager vs DataSource

Both are ways to get a database connection, but DataSource is the modern choice.

DriverManager (JDBC 1.0) - the old way:

DataSource (JDBC 2.0+) - the preferred way:

Why DataSource is preferred:

In Spring Boot, just add spring.datasource.* properties and you get a HikariDataSource automatically.

ConnectionFactory (R2DBC)

For R2DBC, you use ConnectionFactory instead of DataSource:

In Spring Boot, just add spring.r2dbc.* properties:

Spring Boot auto-configures ConnectionFactory and DatabaseClient for you.

JDBC Stack (Blocking)

Everything in this stack blocks the thread while waiting for the database.

Raw JdbcTemplate

Full control, but verbose:

Why registerOutParameter? The JDBC driver needs to know which parameters are outputs before execution. After the call completes, it retrieves the value from that registered position.

SimpleJdbcCall

Declarative, less boilerplate:

Spring Data JPA @Procedure

The cleanest - but only works with JDBC (blocking):

@Procedure is a JPA annotation. JPA is built on JDBC, so it's inherently blocking. There's no reactive equivalent.

R2DBC Stack (Reactive)

Everything in this stack is non-blocking and returns Mono/Flux.

Two ways to execute queries - same pattern as JDBC:

  Programmatic (Client) Declarative (Repository)
JDBC JdbcTemplate.query() @Query in JpaRepository
R2DBC DatabaseClient.sql() @Query in ReactiveCrudRepository

DatabaseClient.sql() - Programmatic

The reactive equivalent of JdbcTemplate. You write code to build and execute queries manually:

Use this when you need full control - dynamic queries, complex mappings, or operations that don't fit the repository pattern.

@Query in Repository - Declarative

You annotate a method and Spring generates the implementation:

Same SQL, but Spring handles the binding and execution. You just call mathRepository.addNumbers(2, 3).

No @Procedure in R2DBC - Spring Data R2DBC doesn't have a @Procedure annotation because R2DBC drivers don't have standardized stored procedure support like JDBC's CallableStatement. You must use @Query with native SQL instead.

Summary

Want to… Use Driver
Call stored procedures with annotations @Procedure (JPA) JDBC (blocking)
Call stored procedures with full control JdbcTemplate / SimpleJdbcCall JDBC (blocking)
Reactive database access DatabaseClient / @Query R2DBC (non-blocking)
Reactive stored procedures DatabaseClient with native SQL R2DBC (limited support)

If you need @Procedure, you're using JDBC. If you need reactive/non-blocking, you're using R2DBC and will need @Query with native SQL instead.


comments powered by Disqus