How to Create Dynamic SQL Queries with Spring Boot

Transcript:

Hi friends! In this video, we will learn how to create dynamic SQL queries in Spring Boot with Spring Data JPA. I will use a project I created for this tutorial. You can find it on GitHub. It is a minimalistic bookshop application. The main entity class is ‘book,’ and we also have some auxiliary classes such as ‘category,’ ‘format,’ and ‘language.’

They also have the book repository and book service. In a real-world bookshelf application, users apply multiple filters to find the book they need. But we don't want to write separate queries for various combinations of these filters, right? This is where dynamic SQL queries come into play.

As the name suggests, they enable us to create SQL queries dynamically based on the user input. To build dynamic SQL queries with JPA, we can use the ‘Specification’ interface. It helps us build multiple predicates over the entity. These predicates can be combined to create queries with flexible WHERE clauses.

To make use of JPA specification, let's make our book repository interface extend the JPA specification executor. Here it is. Our book repository extends both the JPA repository and specification. Now, we need to create a specification class. Let's call it BookSpecification.

Here, we will create our filters, which are going to be custom specifications. Let’s start with the first method and define a filter for category. We need to return the specification for the Book entity class and call this method hasCategoryName. We pass a String for the category name and write a return statement. We return the root, which is our book, along with the query and criteriaBuilder. The criteriaBuilder is the most important part as it provides numerous methods.

We use equal() and pass the root (our book), get the category, and then extract its name. This will be our filter. The book entity has a category field, and the category has a name field. We extract the category and then the name we are looking for.

But in most cases, we need multiple filters, not just one. So let’s add additional filters: price, language, and format. We start with the price filter by creating a method called hasPriceLessThanOrEqualTo and passing the price parameter. The logic is similar—we retrieve the price field from the root and use the lessThanOrEqualTo method from the criteriaBuilder.

Currently, we are using strings to reference entity fields, such as category name or price. While this works, it's not the safest approach because changes in field names can break the specification. Instead, we could use the JPA static model generator for type-safe queries, but we will keep it simple in this demo.

Our filters are ready. Now, let's move to the book service and write a method for finding books based on the filters. Users may apply none, some, or all filters, but we only need one method to handle all these cases. We pass String categoryName, String languageName, String formatName, and a price parameter.

We create a specification called spec, initially set to null to handle cases where no filters are applied. Then, we check each parameter—if it's not null, we add it to the specification using the and method with the corresponding filter from BookSpecification.

Finally, we return the list of books using bookRepository.findAll(spec). If the specification is empty, we return all books. Otherwise, we return a filtered list based on the applied criteria.

Now, it’s time to test it. In the book service test, I have pre-written some tests to speed up the demo. The setup is simple: we first find books by category while passing null for other filters. We test different filter combinations, including category and format, category and language, and all four filters together. We also check that an empty list is returned when there are no matches and verify that all books are returned when no filters are applied.

Let’s run the tests. All tests have passed! And that’s how we create dynamic SQL queries in Spring Boot. Don't forget to like this video, subscribe to our channel, and see you next time!

Summary

This video demonstrates how to create dynamic SQL queries in Spring Boot using Spring Data JPA and the Specification interface for flexible filtering. Instead of writing multiple separate queries for different filter combinations, conditions like category, price, language, and format can be dynamically combined. Finally, tests confirm that the method correctly filters data or returns all books if no filters are applied.

About Catherine

Java developer passionate about Spring Boot. Writer. Developer Advocate at BellSoft

Social Media

Videos
card image
Jan 20, 2026
JDBC vs ORM vs jOOQ: Choose the Right Java Database Tool

Still unsure what is the difference between JPA, Hibernate, JDBC, or jOOQ and when to use which? This video clarifies the entire Java database access stack with real, production-oriented examples. We start at the foundation, which is JDBC, a low-level API every other tool eventually relies on for database communication. Then, we go through the ORM concept, JPA as a specification of ORM, Hibernate as the implementation and extension of JPA, and Blaze Persistence as a powerful upgrade to JPA Criteria API. From there, we take a different path with jOOQ: a database-first, SQL-centric approach that provides type-safe queries and catches many SQL errors at compile time instead of runtime. You’ll see when raw JDBC makes sense for small, focused services, when Hibernate fits CRUD-heavy domains, and when jOOQ excels at complex reporting and analytics. We discuss real performance pitfalls such as N+1 queries and lazy loading, and show practical combination strategies like “JPA for CRUD, jOOQ for reports.” The goal is to equip you with clarity so that you can make informed architectural decisions based on domain complexity, query patterns, and long-term maintainability.

Videos
card image
Jan 13, 2026
Hibernate: Ditch or Double Down? When ORM Isn't Enough

Every Java team debates Hibernate at some point: productivity champion or performance liability? Both are right. This video shows you when to rely on Hibernate's ORM magic and when to drop down to SQL. We walk through production scenarios: domain models with many-to-many relations where Hibernate excels, analytical reports with window functions where JDBC dominates, and hybrid architectures that use both in the same Spring Boot codebase. You'll see real code examples: the N+1 query trap that kills performance, complex window functions and anti-joins that Hibernate can't handle, equals/hashCode pitfalls with lazy loading, and practical two-level caching strategies. We also explore how Hibernate works under the hood—translating HQL to database-specific SQL dialects, managing sessions and transactions through JDBC, implementing JPA specifications. The strategic insight: modern applications need both ORM convenience for transactional business logic and SQL precision for data-intensive analytics. Use Hibernate for CRUD and relationship management. Use SQL where ORM abstractions leak or performance demands direct control.

Further watching

Videos
card image
Feb 6, 2026
Backend Developer Roadmap 2026: What You Need to Know

Backend complexity keeps growing, and frameworks can't keep up. In 2026, knowing React or Django isn't enough. You need fundamentals that hold up when systems break, traffic spikes, or your architecture gets rewritten for the third time.I've been building production systems for 15 years. This roadmap covers three areas that separate people who know frameworks from people who can actually architect backend systems: data, architecture, and infrastructure. This is about how to think, not what tools to install.

Videos
card image
Jan 29, 2026
JDBC Connection Pools in Microservices. Why They Break Down (and What to Do Instead)

In this livestream, Catherine is joined by Rogerio Robetti, the founder of Open J Proxy, to discuss why traditional JDBC connection pools break down when teams migrate to microservices, and what is a more efficient and reliable approach to organizing database access with microservice architecture.

Videos
card image
Jan 27, 2026
Sizing JDBC Connection Pools for Real Production Load

Many production outages start with connection pool exhaustion. Your app waits seconds for connections while queries take milliseconds; yet, most teams run default settings that collapse under load. This video shows how to configure connection pools that survive real production traffic: sizing based on database limits and thread counts, setting timeouts that prevent cascading failures, and implementing an open source database proxy Open J Proxy for centralized connection management with virtual connection handles, client-side load balancing, and slow query segregation. For senior Java developers, DevOps engineers, and architects who need database performance that holds under pressure.