skip to content

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%

answer

  1. QueryLookupStrategy: CREATE / USE_DECLARED_QUERY / CREATE_IF_NOT_FOUND
  2. default = CREATE_IF_NOT_FOUND
  3. declared (@Query/named) beats derived under default
  4. USE_DECLARED_QUERY = no fallback, fails
  5. queryLookupStrategy attr on @Enable...Repositories

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.

solid answer

~40 s

The choice is governed by QueryLookupStrategy, configurable via the queryLookupStrategy attribute on @EnableJpaRepositories (and the equivalent for other stores). Three strategies exist: CREATE always derives from the method name (ignoring declared queries); USE_DECLARED_QUERY only uses a declared query — an @Query annotation or a named query — and fails if none is found; CREATE_IF_NOT_FOUND (the default) tries the declared query first and falls back to name derivation. So for a given method, a hand-written @Query wins over the name under the default, letting you override derivation whenever the name can't express the query. This is how the two coexist: simple lookups stay as readable derived methods, while complex ones drop to @Query without changing the repository's shape.

code

java · 19 lines
java
import org.springframework.data.repository.query.QueryLookupStrategy.Key;

@Configuration
@EnableJpaRepositories(
    basePackages = "com.example.repo",
    queryLookupStrategy = Key.CREATE_IF_NOT_FOUND   // the default, shown explicitly
)
class PersistenceConfig { }

interface PersonRepository extends CrudRepository<Person, Long> {

    // Derivable name, no declared query -> under the default, PartTree derives it.
    List<Person> findByLastName(String lastName);

    // Same style of name, but @Query is a *declared* query and WINS over derivation
    // under CREATE_IF_NOT_FOUND -> lets you express what the name cannot.
    @Query("select p from Person p where p.lastName = ?1 and p.age > (select avg(x.age) from Person x)")
    List<Person> findByLastNameOlderThanAverage(String lastName);
}

go deeper

for a junior

Knows @Query lets you override the derived query.

for a middle

Can name CREATE_IF_NOT_FOUND as default and that @Query wins.

for a senior

Must contrast all three strategies, the no-fallback behavior of USE_DECLARED_QUERY, and named queries as declared.

for a principal

Uses the strategy to shape a repository's maintainability policy and to reason about store-neutral configuration.

**The core decision.** For every repository method, Spring Data must pick a `RepositoryQuery` implementation. The policy is `QueryLookupStrategy.Key`, an enum with three values: - **`CREATE`** — always *create* (derive) the query from the method name via `PartTree`. Declared queries are ignored. Fails at startup if the name can't be parsed. - **`USE_DECLARED_QUERY`** — only use a *declared* query: an `@Query` annotation on the method, or a *named query* (a query defined by convention/externally and looked up by name, e.g. `Entity.methodName`). If no declared query is found, startup fails — it will **not** fall back to derivation. - **`CREATE_IF_NOT_FOUND`** — the **default**. Look for a declared query first; if none is found, derive from the method name. This combines both: declared queries take precedence, derivation is the fallback. **How you configure it.** On the enable annotation for your store, e.g. `@EnableJpaRepositories(queryLookupStrategy = Key.CREATE_IF_NOT_FOUND)`. Most projects never set it and rely on the default. **Precedence under the default.** Because `CREATE_IF_NOT_FOUND` checks declared queries first, an `@Query` on a method *overrides* what the name would derive. This is the practical escape hatch: keep the method name (so callers see a normal repository method) but attach an explicit query when the derivation grammar is insufficient (joins, `GROUP BY`, DB functions, projections, native SQL). A *named query* (declared outside the method, keyed by `<SimpleEntityName>.<methodName>`) is also treated as declared and wins over derivation. **When derivation is the right tool.** Simple equality/range/`In`/`Like`/null lookups and simple sorts — readable, self-validating (bad property = startup error), no query string to maintain. **When to go hand-written (`@Query` / custom impl).** The name would be unreadably long; you need constructs outside the keyword vocabulary; you want a native query; or you need imperative logic, in which case a *custom repository fragment* (an interface + `…Impl` class) is appropriate — those methods are neither derived nor annotated; Spring wires the implementation directly. **Gotchas.** - Under `USE_DECLARED_QUERY`, a method with a derivable name but no `@Query`/named query fails — a surprise if you assumed fallback. - Under `CREATE`, an `@Query` you added is silently ignored; if the name is also non-derivable, startup fails. - Adding an `@Query` doesn't change the method signature/return-type rules; those still apply. **Store neutrality.** `QueryLookupStrategy` is a Commons concept; each module exposes it on its own `@Enable…Repositories`. The three keys mean the same everywhere; only what counts as a 'declared query' (annotation/named query flavor) varies by store.

  • Under the default strategy, you add @Query to a method whose name is also derivable. Which runs?
    The @Query. CREATE_IF_NOT_FOUND checks declared queries first, so the annotation overrides what the name would derive.
  • What breaks if you set queryLookupStrategy to USE_DECLARED_QUERY but leave a plain findByLastName method?
    Startup fails: USE_DECLARED_QUERY never falls back to derivation, and that method has no @Query or named query, so no query can be resolved for it.

saying these in an interview costs you the question

  • Saying the default ignores @Query and always derives (that's CREATE, not the default).
  • Believing USE_DECLARED_QUERY falls back to name derivation.
  • Thinking @Query and derived naming can't coexist on the same repository.
  • Not knowing named queries also count as 'declared' and win over derivation.

context