skip to content

Query by Example & Querydsl

Query by Example builds a query from a probe object and a matcher, while Querydsl generates typed Q-classes for compile-safe dynamic predicates. Interviewers raise them when the scenario has optional filters that string concatenation would ruin.

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

questions

5

What is Query by Example (QBE) in Spring Data, and what are the three building blocks?

level: juniorimportance: must knowfreq 55%

answer

  1. Probe + ExampleMatcher + Example
  2. non-null fields become WHERE by default
  3. QueryByExampleExecutor.findAll(Example)
  4. no ranges / no BETWEEN / no OR-across-fields
  5. primitives can't be null — must ignore

basics

~20 s

Query by Example lets you search by filling in a sample entity (a 'probe'). Spring turns the non-null fields into a WHERE clause. The three parts are: Probe (the sample entity), ExampleMatcher (matching rules), and Example (probe + matcher combined).

solid answer

~40 s

Query by Example is a Spring Data feature for building queries dynamically without writing JPQL or method names. You create a 'probe' — an instance of your entity with only the fields you want to match populated. You wrap it in an Example (Example.of(probe) or Example.of(probe, matcher)) and pass it to repository methods inherited from QueryByExampleExecutor, such as findAll(Example), findOne(Example), or count(Example). By default only non-null properties become predicates, joined with AND, using exact equality. An ExampleMatcher customizes this: which properties to ignore, string matching mode (contains, starts-with), case sensitivity, and whether to AND or OR the predicates. It's ideal for simple, uniform search forms but can't express ranges, OR across arbitrary fields, or nested traversals well.

code

java · 15 lines
java
// Repository just extends the standard interfaces
public interface PersonRepository
        extends JpaRepository<Person, Long> { } // JpaRepository extends QueryByExampleExecutor

// 1. Probe: a sample entity with only the fields we care about
Person probe = new Person();
probe.setLastName("Smith");
probe.setCity("Berlin");

// 2. Example wraps the probe (default matcher: non-null props, AND, exact)
Example<Person> example = Example.of(probe);

// 3. Query
List<Person> results = personRepository.findAll(example);
long count = personRepository.count(example);

go deeper

for a junior

Should know the three parts (probe, matcher, Example) and that non-null fields become the filter.

for a middle

Should know the primitive gotcha and the exact repository methods, plus default AND/exact-match behavior.

for a senior

Should articulate the LIKE/equality-only limitation and when to reach for Querydsl/Specifications instead.

for a principal

Frames QBE as one of several dynamic-query strategies, weighing refactor-safety vs expressiveness for team-wide search patterns.

**Query by Example (QBE)** is a query technique in Spring Data (interface `QueryByExampleExecutor<T>`, which `JpaRepository` extends) that lets you build a query from a *sample instance* of your domain object instead of writing JPQL, a derived method name, or a `@Query`. **The three building blocks:** 1. **Probe** — an actual instance of your entity class where you set only the fields you want to filter on. Example: `Person probe = new Person(); probe.setLastName("Smith");`. Fields left `null` are ignored by default (for primitives, which can't be null, you must explicitly ignore their paths or they'll always be part of the query with their default value like `0` or `false` — a classic gotcha). 2. **ExampleMatcher** — an immutable configuration object (`org.springframework.data.domain.ExampleMatcher`) describing *how* to match: which paths to ignore, string-matching strategy, case sensitivity, null handling, and AND vs OR. Created via `ExampleMatcher.matching()` (all predicates ANDed), `matchingAll()`, or `matchingAny()` (ORed). 3. **Example** — `org.springframework.data.domain.Example<T>`, which pairs a probe with a matcher. `Example.of(probe)` uses the default matcher; `Example.of(probe, matcher)` uses a custom one. **How it executes:** Spring introspects the probe, reads each property's value, and for every non-ignored, non-null property emits a predicate. It combines them per the matcher (AND/OR) and translates to the underlying store query (JPA Criteria for JPA, a query document for MongoDB, etc.). **Repository methods** (from `QueryByExampleExecutor`): `findOne(Example)`, `findAll(Example)`, `findAll(Example, Sort)`, `findAll(Example, Pageable)`, `count(Example)`, `exists(Example)`, and the fluent `findBy(Example, queryFunction)`. **Strengths:** No query strings, refactor-safe, great for dynamic search forms where the shape of the query varies by which fields the user filled in. **Limitations:** Only supports equality-style and string predicates (`=`, `LIKE`); cannot express `<`, `>`, `BETWEEN`, `IN` with a collection, negation, or OR across *different* firing conditions cleanly; nested/associated property matching is limited; regex/like is only on the outermost property level. For richer dynamic queries use Querydsl or Specifications.

  • Why are primitive fields a common QBE gotcha?
    Primitives (int, boolean, long) can't be null, so they always hold a default value (0, false). QBE includes them in the query by default, silently adding predicates like `age = 0`. You must call matcher.withIgnorePaths("age") or use boxed types.
  • Which repository interface provides QBE methods?
    org.springframework.data.repository.query.QueryByExampleExecutor<T>. JpaRepository (and Mongo/etc. repositories) extend it, so findAll(Example), findOne(Example), count(Example), exists(Example) are available automatically.

saying these in an interview costs you the question

  • Thinking QBE can express ranges like age > 18 or BETWEEN
  • Believing null fields are matched as 'IS NULL' by default (they're ignored)
  • Forgetting primitive fields silently add predicates
  • Confusing Example (the wrapper) with ExampleMatcher (the rules)

context

open as a page

How does Querydsl integrate with Spring Data repositories, and what does QuerydslPredicateExecutor provide?

level: seniorimportance: must knowfreq 50%

basics

~10 s

Extend QuerydslPredicateExecutor<T> on your repository. Querydsl's annotation processor generates 'Q-types' (e.g., QPerson) from your entities. You build type-safe Predicate objects with those Q-types and pass them to methods like findAll(Predicate) and findOne(Predicate).

open as a page

How do you configure an ExampleMatcher for case-insensitive 'contains' search across some fields while ignoring others?

level: middleimportance: should knowfreq 45%

basics

~10 s

Use ExampleMatcher.matching() (AND) or matchingAny() (OR), then chain withIgnoreCase(), withStringMatcher(StringMatcher.CONTAINING), and withIgnorePaths("..."). You can also set per-field rules with withMatcher("name", m -> m.contains().ignoreCase()).

open as a page

What are the key limitations and gotchas of Query by Example around primitives, null handling, and nested/associated properties?

level: middleimportance: should knowfreq 30%

basics

~20 s

Primitive fields can't be null, so their default value (0/false) silently becomes a filter — ignore those paths. Null object fields are skipped by default (not matched as IS NULL). QBE also can't do ranges, and matching on nested associations is limited.

open as a page

As an architect, how do you choose among Query by Example, Querydsl, Specifications, and derived/@Query methods for dynamic querying?

level: principalimportance: should knowfreq 35%

basics

~20 s

Match the tool to the query's complexity and team cost. QBE: simple equality/LIKE search forms, zero codegen. Specifications: dynamic JPA-Criteria logic, no extra build step. Querydsl: rich type-safe dynamic queries but needs codegen. @Query/derived: fixed, known queries.

open as a page