skip to content

Specifications & Criteria

Specifications wrap Criteria API predicates in composable objects you can and/or together, which is how you build a dynamic filter safely. The standard answer to a search endpoint with a dozen optional parameters.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is Spring Data JPA's Specification mechanism and how do you enable it on a repository?

level: juniorimportance: must knowfreq 65%

answer

  1. toPredicate(Root, CriteriaQuery, CriteriaBuilder)
  2. extend JpaSpecificationExecutor<T>
  3. returns a Predicate (WHERE fragment)
  4. null predicate = no restriction
  5. wrapper over JPA Criteria API

basics

~10 s

A Specification is a reusable object holding one query condition (a WHERE predicate). To use it, your repository extends JpaSpecificationExecutor, which adds methods like findAll(Specification) that run those conditions.

solid answer

~40 s

Spring Data JPA's Specification is a functional interface (org.springframework.data.jpa.domain.Specification) that wraps a single JPA Criteria API predicate — one piece of a WHERE clause. You make a repository capable of running them by extending JpaSpecificationExecutor<T> alongside JpaRepository<T, ID>. That mix-in adds query methods: findAll(Specification), findOne(Specification), count(Specification), findAll(Specification, Pageable), findAll(Specification, Sort), and exists(Specification). Each Specification implements toPredicate(Root, CriteriaQuery, CriteriaBuilder), which builds a javax/jakarta.persistence.criteria.Predicate. Because a Specification is just an object, you can build them dynamically at runtime and combine several with and()/or(), which is why they're the standard tool for dynamic filtering / search screens where the set of active filters isn't known at compile time.

code

java · 18 lines
java
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface UserRepository
        extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

public final class UserSpecs {
    private UserSpecs() {}

    public static Specification<User> hasStatus(UserStatus status) {
        return (root, query, cb) -> cb.equal(root.get("status"), status);
    }
}

// usage
List<User> active = userRepository.findAll(UserSpecs.hasStatus(UserStatus.ACTIVE));

go deeper

for a junior

Know that Specification = one WHERE condition and the repo must extend JpaSpecificationExecutor.

for a middle

Know the toPredicate signature and the full set of executor methods (findOne/findAll/count/exists/Pageable).

for a senior

Explain the static-factory pattern, null-as-no-restriction, and when Specs beat derived queries.

for a principal

Weigh Specifications vs QueryDSL vs native SQL; discuss metamodel type-safety and query-plan/caching implications at scale.

**The problem it solves.** With plain Spring Data, you write derived query methods like `findByStatusAndCreatedAfter(...)`. That works until filters become optional and combinatorial — a search page with 6 optional filters would need dozens of method variants. Specifications let you build the WHERE clause programmatically at runtime. **The Specification interface.** `org.springframework.data.jpa.domain.Specification<T>` is a functional interface with one method: ``` Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); ``` - `Root<T>` — the query's FROM entity; use `root.get("fieldName")` to reference a column/attribute. - `CriteriaQuery<?>` — the overall query; lets you set distinct, order, grouping. - `CriteriaBuilder` (cb) — the factory for predicates and expressions: `cb.equal(...)`, `cb.like(...)`, `cb.greaterThan(...)`, `cb.and(...)`, etc. - Returns a `Predicate` — a boolean condition (one node of the WHERE clause). Returning `null` is allowed and is treated as "no restriction" (the specification contributes nothing). **Enabling it.** Extend the mix-in interface: ``` interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {} ``` `JpaSpecificationExecutor<T>` (also in `org.springframework.data.jpa.repository`) contributes these methods, all accepting a `Specification`: - `Optional<T> findOne(Specification<T>)` — throws `IncorrectResultSizeDataAccessException` if more than one row matches. - `List<T> findAll(Specification<T>)` - `Page<T> findAll(Specification<T>, Pageable)` - `List<T> findAll(Specification<T>, Sort)` - `long count(Specification<T>)` - `boolean exists(Specification<T>)` (Spring Data JPA 3+) - `long delete(Specification<T>)` (Spring Data JPA 3.x+) **The Criteria API underneath.** Specifications are a thin, composable wrapper over the JPA Criteria API (`jakarta.persistence.criteria.*`, or `javax.*` on older Spring Boot 2). Criteria is the type-safe, programmatic alternative to JPQL strings — no string concatenation, so no injection risk from the query structure and compile-time-ish safety. **Static factory pattern.** Idiomatically you write static methods returning specifications, e.g. `UserSpecs.hasStatus(status)`, so callers read declaratively and each predicate is unit-reusable. **When to use.** Reach for Specifications when filters are optional/dynamic, when you want to reuse predicate fragments across queries, or when a search endpoint composes conditions at runtime. For fixed queries, derived methods or `@Query` are simpler. For very complex reporting queries, consider QueryDSL or native SQL instead. **Gotchas.** (1) The entity's `@StaticMetamodel` (`User_.status`) gives type-safe attribute references instead of stringly-typed `root.get("status")`. (2) `findOne` returns `Optional` and enforces at-most-one. (3) Sorting on joined columns needs care. (4) `CriteriaQuery` is shared across the composed specs — avoid calling `query.distinct()` in a reusable spec unless intended.

  • What does returning null from toPredicate mean?
    It contributes no restriction. Spring skips a null predicate when composing, so it's the standard way to make an optional filter that's inactive when its input is absent.
  • What's the difference between findOne and findAll on JpaSpecificationExecutor?
    findOne returns Optional<T> and throws IncorrectResultSizeDataAccessException if more than one row matches; findAll returns a List (or Page/with Sort) of all matches.

saying these in an interview costs you the question

  • Thinking you extend JpaRepository alone and Specifications magically work — you must also extend JpaSpecificationExecutor.
  • Believing Specification is a JPQL string builder — it's built on the type-safe Criteria API.
  • Assuming returning null throws or matches everything — it means 'no restriction' and is skipped during composition.

context

open as a page

How do you compose Specifications with and(), or(), and not() to build dynamic filters?

level: middleimportance: must knowfreq 60%

basics

~10 s

Each small Specification is one condition. Combine them with spec1.and(spec2), spec1.or(spec2), and Specification.not(spec). You start from a neutral base and conditionally chain filters that are active, producing one combined WHERE clause.

open as a page

Show how to build reusable, null-safe Specification factory methods for an optional-filter search, and explain the null-predicate contract.

level: middleimportance: should knowfreq 40%

basics

~10 s

Write static methods returning Specification<T>. Each checks its input: if the filter value is null, return a lambda whose toPredicate returns null (no restriction); otherwise build the predicate. Compose the active ones with and().

open as a page

How do you write a Specification that filters on a joined/associated entity, and how do you avoid duplicate rows and N+1 problems?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Inside toPredicate, call root.join("association") to reach the related entity, then build a predicate on it. A to-many join can duplicate parents, so add query.distinct(true). Use a fetch join or entity graph to avoid N+1 loading.

open as a page

When would you choose Specifications over QueryDSL, Query-by-Example, or @Query, and what are their trade-offs at scale?

level: principalimportance: should knowfreq 30%

basics

~20 s

Use Specifications for dynamic, reusable, composable filters built at runtime with type-safe Criteria. Prefer @Query for fixed complex queries, Query-by-Example for simple probe matching, and QueryDSL when you want a fluent DSL and heavy compile-time safety.

open as a page