BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Persistence and JPA

Persistence

Dynamic queries (Specifications, Example, Querydsl)

When search filters are optional (name, status, date range), you cannot write one fixed query method.

Learn these first

At a glance

Mental model

Building a sandwich to order: each requested ingredient is a small, reusable piece, and you stack only the ones the customer asked for.

What happens, in order
  1. 1A Specification<Order> is a function that adds a condition: (root, query, cb) -> cb.equal(root.get("status"), status)Small static methods return them, and and() / or() combine them.
  2. 2The repository extends JpaSpecificationExecutor<Order> and calls findAll(spec, pageable)A null specification means "no filter".
  3. 3Query by Example uses a probe object: fields that are set become conditions, with an ExampleMatcher choosing contains or ignore-caseQuerydsl does the same with a generated, type-safe query language.
  4. 4Pageable and Sort parameters add LIMIT, OFFSET and ORDER BY; Page also runs a count query, Slice skips it
Remember
Watch out
Sorting by arbitrary client inputOffset pagination on huge tables (slow for deep pages)Unindexed filter columnsStringly typed field names that break on refactoring
Example (highlighted lines explain it)
public final class OrderSpecs {    public static Specification<Order> hasStatus(String status) {        return (root, query, cb) -> status == null ? null : cb.equal(root.get("status"), status);    }    public static Specification<Order> createdAfter(LocalDate day) {        return (root, query, cb) -> day == null ? null : cb.greaterThan(root.get("createdOn"), day);    }}Page<Order> page = orders.findAll(    Specification.where(OrderSpecs.hasStatus(status)).and(OrderSpecs.createdAfter(since)),    PageRequest.of(0, 20, Sort.by("createdOn").descending()));
  1. line 2 uses Specification, the core of Dynamic queries (Specifications, Example, Querydsl)
  2. line 5 uses Specification, the core of Dynamic queries (Specifications, Example, Querydsl)
  3. line 11 uses Specification, the core of Dynamic queries (Specifications, Example, Querydsl)
  4. line 12 uses PageRequest, the core of Dynamic queries (Specifications, Example, Querydsl)
Read the full explanation (about 1 min)

When search filters are optional (name, status, date range), you cannot write one fixed query method. Specifications and similar builders assemble the WHERE clause from the filters that are actually present.

A mental model

Building a sandwich to order: each requested ingredient is a small, reusable piece, and you stack only the ones the customer asked for.

How it works

A Specification<Order> is a function that adds a condition: (root, query, cb) -> cb.equal(root.get("status"), status). Small static methods return them, and and() / or() combine them.

The repository extends JpaSpecificationExecutor<Order> and calls findAll(spec, pageable). A null specification means "no filter".

Query by Example uses a probe object: fields that are set become conditions, with an ExampleMatcher choosing contains or ignore-case. Querydsl does the same with a generated, type-safe query language.

Pageable and Sort parameters add LIMIT, OFFSET and ORDER BY; Page also runs a count query, Slice skips it.

Going deeper

String field names like "status" are checked only at runtime. Use the generated metamodel (Order_.status) or Querydsl for compile-time safety, so renaming a field breaks the build instead of production.

Whitelist the sortable and filterable fields. Accepting any sort property from the client allows sorting by a sensitive or unindexed column.

Joins in specifications can duplicate rows or trigger the N+1 problem; use fetch joins and distinct, and check the generated SQL.

Under the hood

Each specification is invoked with the JPA Criteria API objects (Root, CriteriaQuery, CriteriaBuilder) and returns a Predicate. The repository builds one CriteriaQuery, applies the combined predicate, translates it to JPQL and then SQL through Hibernate, and executes it as a normal query.

Common mistakes

  • Sorting by arbitrary client input.
  • Offset pagination on huge tables (slow for deep pages).
  • Unindexed filter columns.
  • Stringly typed field names that break on refactoring.

How Unravel recognises it

When you paste code, Unravel looks for these signs of dynamic queries (specifications, example, querydsl). Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Dynamic queries (Specifications, Example, Querydsl)?

When search filters are optional (name, status, date range), you cannot write one fixed query method. Specifications and similar builders assemble the WHERE clause from the filters that are actually present.

What are the most common mistakes with Dynamic queries (Specifications, Example, Querydsl)?

Sorting by arbitrary client input. Offset pagination on huge tables (slow for deep pages). Unindexed filter columns. Stringly typed field names that break on refactoring.

How can I see Dynamic queries (Specifications, Example, Querydsl) in real code?

Paste your own Java into Unravel and it will point out where dynamic queries (specifications, example, querydsl) appears, explain how it works at the detail level you choose, and draw the flow of the code around it.