How to Use Specification in Spring Data for Flexible Queries
Use
Specification in Spring Data by implementing its toPredicate method to define query conditions. Then pass the specification to repository methods like findAll(Specification) to execute flexible, dynamic queries.Syntax
The Specification interface requires implementing the toPredicate method, which builds query conditions using the CriteriaBuilder and Root objects. You then pass the specification to repository methods like findAll(Specification) to fetch filtered results.
Root<T>: Represents the entity root in the query.CriteriaQuery<?>: The query being built.CriteriaBuilder: Used to create predicates (conditions).Predicate: Represents a single condition in the query.
java
public interface Specification<T> { Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder); } // Usage in repository List<T> findAll(Specification<T> spec);
Example
This example shows how to create a specification to filter Product entities by minimum price and category, then use it with a Spring Data JPA repository.
java
import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.*; public class ProductSpecifications { public static Specification<Product> hasMinPrice(Double minPrice) { return (Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> { if (minPrice == null) { return cb.conjunction(); // no filtering } return cb.greaterThanOrEqualTo(root.get("price"), minPrice); }; } public static Specification<Product> hasCategory(String category) { return (root, query, cb) -> { if (category == null || category.isEmpty()) { return cb.conjunction(); } return cb.equal(root.get("category"), category); }; } } // In your service or controller Specification<Product> spec = Specification.where(ProductSpecifications.hasMinPrice(100.0)) .and(ProductSpecifications.hasCategory("Electronics")); List<Product> results = productRepository.findAll(spec);
Output
List of Product objects with price >= 100.0 and category 'Electronics'
Common Pitfalls
- Not handling
nullvalues in specifications can cause errors or unwanted filtering. - Forgetting to combine specifications with
andororleads to incomplete queries. - Using entity field names incorrectly in
root.get()causes runtime errors. - Returning
nullinstead ofcb.conjunction()when no condition is needed breaks the query.
java
/* Wrong: returns null when no filter, causes error */ return (root, query, cb) -> { if (minPrice == null) { return null; // wrong } return cb.greaterThanOrEqualTo(root.get("price"), minPrice); }; /* Right: returns conjunction (always true) when no filter */ return (root, query, cb) -> { if (minPrice == null) { return cb.conjunction(); // correct } return cb.greaterThanOrEqualTo(root.get("price"), minPrice); };
Quick Reference
Use these tips when working with Specification in Spring Data:
- Implement
toPredicateto define query conditions. - Use
cb.conjunction()to represent no filtering (always true). - Combine specifications with
andandormethods. - Pass the specification to repository methods like
findAll. - Always use correct entity field names in
root.get().
Key Takeaways
Implement Specification by overriding toPredicate to build query conditions.
Use cb.conjunction() to safely handle optional filters without breaking queries.
Combine multiple specifications with and() and or() for flexible queries.
Pass Specification instances to repository methods like findAll() to execute queries.
Always use correct entity field names and handle nulls to avoid runtime errors.