skip to content

Repository Programming Model (Commons)

The store-agnostic core: the repository interface hierarchy, how proxies are generated, query derivation from method names, custom fragments, Query by Example and Querydsl, and is-new detection. Understanding this layer is what lets you answer 'where does the implementation come from'.

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

explore

questions

30

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

What is a custom fragment interface in Spring Data, and how does Spring find its implementation?

level: juniorimportance: must knowfreq 55%

basics

~10 s

A fragment is a small interface declaring custom repository methods you write yourself. Your repository extends it, and Spring finds the implementation class by name: the fragment interface name plus the suffix 'Impl'.

open as a page

When you call repository.save(entity), how does Spring Data decide whether to run an INSERT or an UPDATE by default?

level: juniorimportance: must knowfreq 62%

basics

~10 s

By default Spring Data treats an entity as new when its @Id is null (or 0 for a primitive id type). New means INSERT; otherwise it's an existing row, so UPDATE.

open as a page

You declare a Spring Data repository as just an interface with no body. Who provides the actual implementation, and when?

level: juniorimportance: must knowfreq 72%

basics

~20 s

You never write the implementation. Spring Data creates it for you automatically at application startup — it generates a proxy object that implements your repository interface and wires it into the Spring container as a bean.

open as a page

What are derived query methods in Spring Data, and how does a method name like findByLastName become a query?

level: juniorimportance: must knowfreq 72%

basics

~10 s

You declare a method on a repository interface, like findByLastName(String name), and write no body. Spring Data reads the method name and generates the query automatically from it.

open as a page

What is the Repository marker interface in Spring Data, and how does CrudRepository relate to it?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Repository<T, ID> is an empty marker interface that tells Spring Data an interface is a repository and captures the entity type T and its id type ID. CrudRepository extends it and adds ready-made save/find/delete methods.

open as a page

Explain the derived-query keywords And, Or, Between, Like, In, IsNull, and OrderBy, and how they combine into a predicate.

level: middleimportance: must knowfreq 58%

basics

~10 s

And/Or combine conditions; Between matches a range (two args); Like does pattern matching; In matches a collection; IsNull checks for null (no arg); OrderBy sorts results, e.g. OrderByAgeDesc.

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

You assign your own @Id (e.g. a UUID or natural key). Why do inserts now behave oddly, and what goes wrong under the hood?

level: seniorimportance: must knowfreq 50%

basics

~20 s

With a manually assigned id, the id is never null, so the default is-new check reports 'existing'. In JPA that routes new objects through merge instead of persist: an extra SELECT, and if the row is truly missing, a surprising insert-after-select. In JDBC/Mongo it issues an UPDATE that silently affects zero rows.

open as a page

How do you implement Persistable<ID> to take full control of is-new detection, and how does @CreatedDate fit in?

level: seniorimportance: must knowfreq 44%

basics

~20 s

Implement Persistable<ID> and override getId() plus isNew(). Back isNew() with a transient boolean set false on load/persist, or return createdDate == null using an @CreatedDate field. When present, Persistable.isNew() overrides all the default id/version checks.

open as a page

What is @NoRepositoryBean and when must you use it?

level: seniorimportance: must knowfreq 50%

basics

~20 s

@NoRepositoryBean marks a repository interface as a base/intermediate interface that Spring Data should NOT create a bean/proxy for. You put it on shared base interfaces you extend but never use directly, so Spring doesn't try to instantiate them.

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

How do you compose multiple fragment interfaces into one repository, and why would you split behavior across several fragments?

level: middleimportance: should knowfreq 40%

basics

~20 s

A repository interface can extend several fragment interfaces at once. Each has its own Impl class. You split behavior so unrelated custom logic (e.g., search vs. reporting) stays in small, cohesive, independently testable units instead of one giant custom class.

open as a page

How does a @Version property change Spring Data's is-new detection, and when does it take precedence over the @Id check?

level: middleimportance: should knowfreq 38%

basics

~20 s

If the entity has a non-primitive @Version field (e.g. Long version), Spring Data considers it new when the version is null, instead of checking the id. A primitive version type is ignored and it falls back to the id check.

open as a page

What kind of proxy does Spring Data create for a repository, and which class actually builds it?

level: middleimportance: should knowfreq 55%

basics

~20 s

A JDK dynamic proxy — because repositories are interfaces, no subclassing (CGLIB) is needed. The proxy is assembled by RepositoryFactorySupport (the JPA subclass is JpaRepositoryFactory) using Spring AOP's ProxyFactory, which adds interceptors before creating the interface-based proxy.

open as a page

What do ListCrudRepository and ListPagingAndSortingRepository add over their base interfaces, and why were they introduced?

level: middleimportance: should knowfreq 45%

basics

~10 s

They were added in Spring Data 3.0. ListCrudRepository is like CrudRepository but findAll, findAllById and saveAll return List<T> instead of Iterable<T>. ListPagingAndSortingRepository makes findAll(Sort) return List<T>.

open as a page

Explain the method-resolution order Spring Data uses across fragments and the base repository. How would you override a CRUD method like save()?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Spring Data checks fragments in the order you declared them in the repository's extends clause; the first fragment that declares the method handles the call. Fragments outrank the generated base, so declaring a fragment with save() (listed before the base is implicitly) lets it override the default save().

open as a page

Walk through how @EnableJpaRepositories turns your repository interfaces into beans. What is the role of RepositoryFactoryBean?

level: seniorimportance: should knowfreq 48%

basics

~20 s

@EnableJpaRepositories imports a registrar that scans the base packages for repository interfaces. For each one it registers a bean definition whose class is JpaRepositoryFactoryBean. That FactoryBean, at startup, uses a RepositoryFactory to build and hand back the proxy as the actual bean.

open as a page

What is a RepositoryProxyPostProcessor, when in the lifecycle is it invoked, and give real examples of what it contributes.

level: seniorimportance: should knowfreq 34%

basics

~20 s

It is a callback that lets Spring Data add AOP advice to a repository proxy before the proxy is created. During getRepository(), each registered post-processor's postProcess(ProxyFactory, RepositoryInformation) runs. Examples: adding transaction interception and JPA exception translation.

open as a page

How does Spring Data decide between deriving a query from the method name and using a hand-written query? Explain QueryLookupStrategy.

level: seniorimportance: should knowfreq 33%

basics

~10 s

It depends on the QueryLookupStrategy. By default (CREATE_IF_NOT_FOUND) Spring first looks for a declared query (an @Query annotation or a named query); if none exists, it derives the query from the method name.

open as a page

How does the PartTree engine parse a repository method name? Walk through subject vs. predicate and the findBy/countBy/existsBy/deleteBy verbs.

level: seniorimportance: should knowfreq 34%

basics

~20 s

PartTree splits the name at the first By into a subject (the verb: find/count/exists/delete) and a predicate. It then breaks the predicate on Or into OR-parts and each on And into Parts, mapping every Part to a property and keyword.

open as a page

Explain PagingAndSortingRepository. What changed about its position in the hierarchy in Spring Data 3.0?

level: seniorimportance: should knowfreq 40%

basics

~10 s

PagingAndSortingRepository<T, ID> adds two methods: findAll(Sort) for sorted results and findAll(Pageable) returning a Page<T> for paginated results. In Spring Data 3.0 it stopped extending CrudRepository, so it no longer inherits save/delete.

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

Across JPA, Spring Data JDBC, and MongoDB, how does entity-state detection differ, and how does it interact with @CreatedDate/@LastModifiedDate auditing?

level: principalimportance: should knowfreq 24%

basics

~20 s

All stores share the same detection order (Persistable > nullable @Version > @Id null/0), but JPA can hide mistakes via merge while JDBC/Mongo have no persistence context and directly pick INSERT vs UPDATE. Auditing's IsNewAwareAuditingHandler reuses isNew() to set @CreatedDate only on new entities and @LastModifiedDate on every save.

open as a page

You're setting a team convention for which repository interface to extend. Walk through the trade-offs across the hierarchy from Repository up to JpaRepository.

level: principalimportance: should knowfreq 25%

basics

~20 s

Extend the narrowest interface that exposes only the operations a repository should offer: Repository for a minimal custom surface, CrudRepository/ListCrudRepository for CRUD, PagingAndSortingRepository for read/paging, JpaRepository when you need JPA extras like flush and batch. Broader interfaces mean broader, harder-to-govern APIs.

open as a page

How does Spring Data discover a fragment's Impl class, and how do you handle a non-default postfix or a manually-configured fragment bean?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

Spring scans the repository's base packages for a class named <Fragment>Impl and instantiates it, injecting constructor dependencies. You can change the 'Impl' suffix via repositoryImplementationPostfix, or register the Impl as an explicit Spring bean when it needs special wiring.

open as a page

Compare default methods on a repository interface with fragment implementations. When do you choose each, and how do they interact with the base implementation and derived queries?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Default methods live on the interface and are run directly by the proxy — good for simple logic that just composes existing repository methods, with no Impl class. Fragments are separate classes for real custom logic needing injected collaborators (EntityManager, external clients) and can override CRUD methods.

open as a page

At runtime, when you call a method on the generated repository proxy, how does it decide what to actually execute? And what is resolved eagerly at startup versus per call?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Every call goes through the proxy's QueryExecutorMethodInterceptor, which classifies the method: a declared query method runs a pre-built RepositoryQuery; a custom-fragment or default method goes to that implementation; a base CRUD method goes to SimpleJpaRepository. Query objects and metadata are built at startup; dispatch happens per call.

open as a page

As a tech lead, when do you push back on derived query methods, and what are the design tradeoffs versus explicit queries?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Use derivation for simple, readable lookups. Push back when method names get long or need joins, grouping, functions, or complex OR logic — those belong in an explicit @Query or a custom fragment, which are clearer and testable.

open as a page