The Specification pattern helps you build flexible and reusable database queries step-by-step without writing complex SQL. It makes filtering data easier and cleaner.
Specification pattern for dynamic queries in Spring Boot
public class YourEntitySpecification implements Specification<YourEntity> { private SearchCriteria criteria; public YourEntitySpecification(SearchCriteria criteria) { this.criteria = criteria; } @Override public Predicate toPredicate(Root<YourEntity> root, CriteriaQuery<?> query, CriteriaBuilder builder) { switch (criteria.getOperation()) { case ">": return builder.greaterThan(root.get(criteria.getKey()), criteria.getValue().toString()); case "<": return builder.lessThan(root.get(criteria.getKey()), criteria.getValue().toString()); case ":": if (root.get(criteria.getKey()).getJavaType() == String.class) { return builder.like(root.get(criteria.getKey()), "%" + criteria.getValue() + "%"); } else { return builder.equal(root.get(criteria.getKey()), criteria.getValue()); } default: return null; } } }
The Specification interface requires implementing the toPredicate method.
Use CriteriaBuilder to build conditions like equals, greater than, or like.
public class ProductSpecification implements Specification<Product> { private SearchCriteria criteria; public ProductSpecification(SearchCriteria criteria) { this.criteria = criteria; } @Override public Predicate toPredicate(Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder builder) { if (criteria.getOperation().equalsIgnoreCase(":")) { return builder.like(root.get(criteria.getKey()), "%" + criteria.getValue() + "%"); } return null; } }
Specification<Product> spec = Specification.where(new ProductSpecification(new SearchCriteria("price", ">", 100))) .and(new ProductSpecification(new SearchCriteria("category", ":", "Books")));
This code defines a reusable specification for filtering products by name containing 'phone' and price greater than 500.
You can pass this specification to Spring Data JPA repository methods to get filtered results.
import org.springframework.data.jpa.domain.Specification; import jakarta.persistence.criteria.*; public class SearchCriteria { private String key; private String operation; private Object value; public SearchCriteria(String key, String operation, Object value) { this.key = key; this.operation = operation; this.value = value; } public String getKey() { return key; } public String getOperation() { return operation; } public Object getValue() { return value; } } public class ProductSpecification implements Specification<Product> { private SearchCriteria criteria; public ProductSpecification(SearchCriteria criteria) { this.criteria = criteria; } @Override public Predicate toPredicate(Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder builder) { return switch (criteria.getOperation()) { case ">" -> builder.greaterThan(root.get(criteria.getKey()), criteria.getValue().toString()); case "<" -> builder.lessThan(root.get(criteria.getKey()), criteria.getValue().toString()); case ":" -> { if (root.get(criteria.getKey()).getJavaType() == String.class) { yield builder.like(root.get(criteria.getKey()), "%" + criteria.getValue() + "%"); } else { yield builder.equal(root.get(criteria.getKey()), criteria.getValue()); } } default -> null; }; } } // Usage example in a service method Specification<Product> spec = Specification.where( new ProductSpecification(new SearchCriteria("name", ":", "phone"))) .and(new ProductSpecification(new SearchCriteria("price", ">", 500))); // Then pass 'spec' to your repository method like findAll(spec)
Make sure your entity fields match the keys used in SearchCriteria.
You can combine multiple specifications with and and or for complex queries.
Use this pattern to keep your query logic clean and easy to maintain.
The Specification pattern helps build flexible database queries step-by-step.
It uses simple criteria objects to define conditions like equals, greater than, or contains.
You can combine multiple specifications to create complex filters easily.
Practice
Specification pattern in Spring Boot?Solution
Step 1: Understand the role of Specification pattern
The Specification pattern is designed to help build database queries that can be combined and reused dynamically.Step 2: Compare with other options
Options A, C, and D relate to other Spring Boot features like configuration, routing, and REST APIs, not query building.Final Answer:
To build flexible and reusable database queries dynamically -> Option DQuick Check:
Specification pattern = flexible queries [OK]
- Confusing Specification with REST controller features
- Thinking it manages app configuration
- Assuming it handles HTTP routing
Solution
Step 1: Recall Specification interface method
The Specification interface requires implementing thetoPredicatemethod with parametersRoot,CriteriaQuery, andCriteriaBuilder.Step 2: Eliminate incorrect options
Options A, B, and C do not match the Specification interface method signature.Final Answer:
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> Option AQuick Check:
Specification method = toPredicate(...) [OK]
- Using methods unrelated to Specification interface
- Missing correct method parameters
- Confusing with repository methods
Specification<User> spec = (root, query, builder) -> builder.equal(root.get("status"), "ACTIVE");
List<User> users = userRepository.findAll(spec);Solution
Step 1: Analyze the Specification lambda
The lambda creates a predicate that filters users where the 'status' field equals 'ACTIVE'.Step 2: Understand the repository call
ThefindAll(spec)method applies this filter, returning only users matching the predicate.Final Answer:
Returns all users with status 'ACTIVE' -> Option CQuick Check:
Predicate filters status = ACTIVE [OK]
- Assuming it returns all users without filtering
- Thinking lambda causes syntax error
- Confusing status values
public class UserSpec implements Specification<User> {
public Predicate toPredicate(Root<User> root, CriteriaQuery query) {
return builder.equal(root.get("age"), 30);
}
}Solution
Step 1: Check toPredicate method signature
The Specification interface requires three parameters: Root, CriteriaQuery, and CriteriaBuilder.Step 2: Identify missing parameter
The method is missing the CriteriaBuilder parameter, so it cannot build the predicate correctly.Final Answer:
Missing CriteriaBuilder parameter in toPredicate method -> Option AQuick Check:
toPredicate needs CriteriaBuilder parameter [OK]
- Omitting CriteriaBuilder parameter
- Returning wrong type
- Wrong method name
spec1 and spec2 to create a dynamic query that matches records satisfying both conditions?Solution
Step 1: Recall Specification combination methods
Spring Data JPA Specification provideswhere(),and(), andor()methods to combine Specifications logically.Step 2: Combine for AND condition
To match records satisfying both conditions, useSpecification.where(spec1).and(spec2).Final Answer:
Use Specification.where(spec1).and(spec2) -> Option BQuick Check:
Combine specs with where().and() for AND logic [OK]
- Using or() instead of and() for AND condition
- Using non-existent add() or merge() methods
- Not wrapping with Specification.where()
Ready to prove it?
Pass the test and this pattern moves unknown
