Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Spring Data JPA’s Specification<T> to express a reusable entity predicate, then combine small specifications when building a repository query. Add JpaSpecificationExecutor<T> to the repository to run them. This approach is most useful when filters are optional or their combinations vary; fixed queries are often clearer as derived query methods.

What a Specification represents

A Spring Data JPA Specification<T> expresses a predicate over an entity through the JPA Criteria API. It is a reusable condition—not a complete repository query. Spring describes the API as a focused way to express and reuse predicates, and relates the term to the Specification concept in Eric Evans’ Domain-Driven Design. See the Spring Data JPA Specifications reference and the Specification API documentation.

Set up the repository

Extend the repository with JpaSpecificationExecutor<T> alongside the usual JPA repository interface. The executor provides methods for querying with specifications.

public interface CustomerRepository
        extends JpaRepository<Customer, Long>,
                JpaSpecificationExecutor<Customer> {
}

Write small, reusable specifications

A specification factory can expose focused predicates for an entity. For example, this factory creates a case-insensitive substring match for a customer’s email:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
public final class CustomerSpecifications {
    private CustomerSpecifications() {}

    public static Specification<Customer> emailContains(String text) {
        return (root, query, cb) ->
            cb.like(cb.lower(root.get("email")), "%" + text.toLowerCase() + "%");
    }

    public static Specification<Customer> isActive() {
        return (root, query, cb) ->
            cb.isTrue(root.get("active"));
    }
}

The example uses CriteriaBuilder operations rather than concatenating input into a query string. In production, decide how to handle null or blank search text before constructing the predicate, and use locale-aware case normalization if the application’s language requirements call for it.

Combine specifications for a use case

Compose predicates where the application knows which criteria apply, then pass the result to the repository:

Specification<Customer> filter = Specification
        .where(CustomerSpecifications.emailContains(searchText))
        .and(CustomerSpecifications.isActive());

List<Customer> customers = repository.findAll(filter);

Composition methods include and, or, allOf, and anyOf. In current Spring Data JPA API versions, use Specification.unrestricted() for an optional criterion that is absent; it contributes no predicate and is elided during composition. Check the API version in your project before adopting this method, because older examples may use different nullable-where() patterns.

Specification<Customer> emailFilter = hasEmailCriterion
        ? CustomerSpecifications.emailContains(searchText)
        : Specification.unrestricted();

Specification<Customer> filter = emailFilter
        .and(CustomerSpecifications.isActive());
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Choose the query approach that fits

Specifications are particularly useful when the application needs to recombine a set of small predicates across use cases. Other query approaches can be simpler when the query shape is stable or the data access needs are different.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Approach Optionality and combinations Readability and reuse Joins and SQL control
Specifications Well suited to optional filters and many changing combinations. Small predicates can be reused and composed, though many abstractions can make a query harder to follow. Uses Criteria API; inspect generated SQL for complex joins. Does not guarantee a particular SQL shape or performance.
Derived query methods Best when the set of query conditions is fixed. Direct and concise for straightforward queries; method names become unwieldy as combinations grow. Less direct control than writing an explicit query.
Query by Example Useful for matching example-object values, but not a general substitute for arbitrary predicate logic. Can be approachable for simple, probe-based matching; reuse and expressiveness depend on the query needs. Does not provide the same explicit Criteria predicate composition.
Explicit JPQL or Criteria code Can express fixed or sophisticated query shapes; dynamic conditions require deliberate construction. Centralizes query logic, but may be more verbose than composing reusable specifications. Offers more direct query-shape control; still validate actual generated SQL and database behavior.

Watch for version and query-shape pitfalls

  • Confirm the Spring Data JPA version. The current API documents unrestricted() and collection composition methods such as allOf and anyOf; do not assume these are available in older dependencies.
  • Keep predicates focused. Prefer one condition per factory method, and combine them at the use-case boundary so the chosen filters are visible.
  • Be deliberate with joins and pagination. Avoid unbounded fetch joins in pageable queries; they can complicate result counts and page boundaries.
  • Inspect database behavior. For complex joins or filters, review generated SQL, relevant indexes, and execution plans in the target database. The API does not guarantee a universal speed advantage.

How to tell whether Specifications are the right fit

  • Use specifications when users can select different filters and the combinations need to change at runtime.
  • Use derived query methods when a small number of fixed predicates keeps the repository readable.
  • Consider Query by Example for straightforward matching against a probe object.
  • Use explicit JPQL or Criteria code when the query’s structure needs more direct control than reusable predicates provide.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.