QueryDSL Dynamic Queries
Write one search API that handles any combination of filters.
The Hyderabadi Irani Cafe Custom Order Analogy:
At an Irani Cafe, the menu is fixed. But if you want to customize your order (“Make the chai less sweet, but add more milk, and toast the bun extra crispy”), the waiter writes down a dynamic list of instructions for the chef.
JPA Specifications allow you to write code that adds SQL conditions dynamically based on what the user actually requested.
- Specification<T>: An interface that builds a
Predicate(a SQL WHERE clause). - JpaSpecificationExecutor: Your repository must extend this to enable Specification execution.
- CriteriaBuilder: The Java tool used to write the SQL logic (e.g.,
cb.equal(),cb.greaterThan()).
1. The Entity (Book.java)
package com.devinhyderabad;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class Book {
@Id
private Long id;
private String title;
private String author;
private double price;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
}2. The Dynamic Specification (BookSpecifications.java)
package com.devinhyderabad;
import org.springframework.data.jpa.domain.Specification;
import jakarta.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
public class BookSpecifications {
public static Specification<Book> filterBooks(String author, Double minPrice) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (author != null && !author.isEmpty()) {
predicates.add(criteriaBuilder.equal(root.get("author"), author));
}
if (minPrice != null) {
predicates.add(criteriaBuilder.greaterThan(root.get("price"), minPrice));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}3. The Repository & Controller
package com.devinhyderabad;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
interface BookRepository extends JpaRepository<Book, Long>, JpaSpecificationExecutor<Book> {}
@RestController
@RequestMapping("/api/books")
class BookController {
private final BookRepository repo;
public BookController(BookRepository repo) { this.repo = repo; }
// Example URL: /api/books/search?author=Deva&minPrice=100.0
@GetMapping("/search")
public List<Book> searchBooks(
@RequestParam(required = false) String author,
@RequestParam(required = false) Double minPrice) {
return repo.findAll(BookSpecifications.filterBooks(author, minPrice));
}
}The dynamic specification pattern works like this:
- BookSpecifications.filterBooks() receives optional parameters from the controller. It returns a lambda that uses the
CriteriaBuilderto build SQL conditions only for parameters that are actually present. - If the user calls
/api/books/search?author=Deva, the generated SQL is:WHERE author = ‘Deva’. - If the user calls
/api/books/search?author=Deva&minPrice=100.0, the SQL becomes:WHERE author = ‘Deva’ AND price > 100.0. - JpaSpecificationExecutor provides the
findAll(Specification)method for this purpose.
Interview Question: “How do you handle complex, dynamic search criteria in Spring Data JPA?”
Answer: Instead of writing dozens of derived query methods (which become unreadable), I use the JPA Criteria API via Specification<T>. I create a method that checks if each @RequestParam is present, adds a Predicate to a list, and combines them with criteriaBuilder.and(). This dynamically generates the exact SQL needed.
Enterprise Note: Another popular alternative to Specifications is QueryDSL. QueryDSL generates type-safe fluent APIs, resulting in much cleaner Java code than the standard JPA Criteria API. However, it requires extra Maven annotation processing setup.
Key Takeaways
- ✅ JPA Specifications build SQL WHERE clauses dynamically at runtime
- ✅ Specification<T> uses CriteriaBuilder to construct Predicates
- ✅ Repository must extend JpaSpecificationExecutor to use Specifications
- ✅ Avoids dozens of derived query methods for every filter combination
- ✅ QueryDSL is an alternative with type-safe, cleaner syntax
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login