Calling Stored Procedures from Spring Data JPA: The Real Deal
Learn how to call stored procedures from Spring Data JPA repositories with real production examples, debugging tips, and gotchas the official docs won't tell you..
20+ years shipping production Java in banking & fintech. Notes here come from systems that actually shipped.
- ✓Basic knowledge of Spring Data JPA repositories
- ✓Understanding of stored procedures in your database
- ✓Spring Boot project with JPA and database dependencies
- Use @Procedure annotation in Spring Data JPA repositories to call stored procedures.
- For dynamic queries, use EntityManager with StoredProcedureQuery.
- Always handle multiple result sets and output parameters carefully.
- Test with real database to avoid Hibernate caching issues.
- Prefer JDBC template for complex procedures with multiple result sets.
Think of a stored procedure as a pre-written recipe in a cookbook (the database). Instead of telling the chef (the application) each step, you just say 'make me dish #42' and the chef follows the recipe. Spring Data JPA gives you a special way to call these recipes from your Java code, but sometimes the recipe returns multiple dishes or has special instructions that the official manual doesn't mention.
If you've been working with Spring Data JPA for a while, you've probably hit the wall where simple CRUD operations aren't enough. Maybe you need to execute a complex financial calculation that's already implemented as a stored procedure in your Oracle database, or you're migrating a legacy system where business logic lives in SQL Server stored procedures. I've been there—debugging a production outage at 3 AM because a stored procedure call returned unexpected results. In this article, I'll show you how to call stored procedures from Spring Data JPA repositories the right way, with the gotchas that only come from years of experience. We'll cover the @Procedure annotation, EntityManager-based calls, handling multiple result sets, and most importantly, what the official documentation conveniently leaves out. By the end, you'll be able to integrate stored procedures into your Spring Boot application without the pain.
The Basics: @Procedure Annotation
Spring Data JPA provides the @Procedure annotation to call stored procedures directly from repository interfaces. This is the cleanest approach when your procedure returns a single result set or a simple value. Here's the real deal: the annotation has three ways to specify the procedure name: implicit mapping (method name), explicit procedureName, and the value attribute (alias for procedureName). I've seen teams get burned by relying on implicit mapping because Hibernate's naming strategy might not match the database's case sensitivity. Always use explicit procedureName.
Let's look at a realistic example. Suppose we have a payment reconciliation system with a stored procedure that calculates daily totals:
Handling Output Parameters Beyond Simple Returns
Stored procedures often have OUTPUT parameters that return values via the parameter list, not as a result set. The @Procedure annotation supports this with the outputParameterName attribute, but there's a catch: it only works if the procedure has exactly one output parameter and returns it as the method's return value. If you have multiple output parameters, you're out of luck with this approach. In that case, you need to use EntityManager directly.
Here's a procedure that updates a payment status and returns both a success flag and a message:
Dealing with Multiple Result Sets
Some stored procedures return multiple result sets—for example, a report that returns summary data and detail data. The @Procedure annotation cannot handle this; it only expects a single result set. If you try, you'll get a ClassCastException or just the first result set. The correct approach is to use StoredProcedureQuery and iterate through the result sets.
Here's a procedure that returns both a list of payments and a summary row:
execute() and hasMoreResults() to handle multiple result sets.What the Official Docs Won't Tell You
The Spring Data JPA documentation makes it look easy, but here are the gotchas I've learned the hard way:
- Hibernate Caching: When you call a stored procedure via @Procedure, Hibernate might cache the result in its second-level cache. This can lead to stale data if the procedure is not deterministic. Always mark the query as non-cacheable if needed: @ProcedureHints({@QueryHint(name = "org.hibernate.cacheable", value = "false")}).
- Transaction Boundaries: Stored procedures often manage their own transactions. If your procedure commits internally, and then your Spring transaction rolls back, you'll have inconsistent state. Use REQUIRES_NEW propagation to isolate the procedure call.
- Database-Specific SQL: @Procedure generates a JDBC call like {call procedure_name(?,?)}. This works on most databases, but some (like PostgreSQL) require a different syntax. Test on your target database.
- Null Parameters: Passing null to a stored procedure parameter can cause unexpected behavior. Some databases treat null differently than an empty string. Always validate inputs before calling.
- Multiple Result Sets and JPA: The @Procedure annotation simply cannot handle multiple result sets. The docs don't mention this limitation clearly. You must use EntityManager.
- Output Parameters Order: The order of output parameters in registerStoredProcedureParameter must match the procedure definition exactly. If the procedure has IN, OUT, INOUT parameters, the order matters. I've seen bugs where the developer mixed up the order.
- Timeout Settings: By default, there's no timeout on stored procedure calls. If your procedure hangs, it will block the connection pool. Always set a timeout: @Procedure(timeout = 30) or via JPA properties.
When to Use JDBC Template Instead
Sometimes JPA is overkill for stored procedure calls. If you're dealing with complex procedures that return multiple result sets, have many output parameters, or need fine-grained control over the JDBC connection, consider using JdbcTemplate or even plain JDBC. JPA adds overhead and abstraction that can get in the way.
- The procedure returns multiple result sets with different structures.
- The procedure uses database-specific features like REF CURSORS (Oracle).
- I need to call the procedure outside of a transaction context.
- Performance is critical and I want to avoid Hibernate's overhead.
Example with JdbcTemplate:
Testing Stored Procedure Calls
Testing stored procedure calls is tricky because you need a real database. Unit tests with mocked repositories are useless—they don't validate the actual SQL. Use Testcontainers to spin up a real database in your tests. This catches issues like parameter mismatches, case sensitivity, and missing procedures early.
Here's a test example with Testcontainers:
The Midnight Outage: When Stored Procedure Calls Went Silent
- Always specify procedureName explicitly, don't rely on default naming.
- Check database case sensitivity for procedure names.
- Handle multiple result sets explicitly with @SqlResultSetMapping or use JPA 2.1 StoredProcedureQuery.
- Set a query timeout to avoid blocking the connection pool.
- Test with the exact same database version as production.
StoredProcedureQuery.getResultList() and iterate through results with hasMoreResults(). Don't rely on @Procedure for multiple result sets.Call procedure directly in DB client to verify it works.Check @Procedure annotation: procedureName vs method name.| File | Command / Code | Purpose |
|---|---|---|
| PaymentRepository.java | public interface PaymentRepository extends JpaRepository | The Basics |
| PaymentService.java | @Repository | Handling Output Parameters Beyond Simple Returns |
| ReportService.java | public class ReportService { | Dealing with Multiple Result Sets |
| TimeoutExample.java | @Procedure(procedureName = "long_running_proc", timeout = 30) | What the Official Docs Won't Tell You |
| JdbcTemplateExample.java | @Repository | When to Use JDBC Template Instead |
| PaymentRepositoryTest.java | @SpringBootTest | Testing Stored Procedure Calls |
Key takeaways
Interview Questions on This Topic
How do you call a stored procedure from a Spring Data JPA repository?
Frequently Asked Questions
20+ years shipping production Java in banking & fintech. Notes here come from systems that actually shipped.
That's Hibernate & JPA. Mark it forged?
3 min read · try the examples if you haven't