skip to content

Named Queries

Statically declared queries validated at startup instead of at first use. A short but real interview topic — the startup-validation benefit is the answer they are fishing for.

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

questions

4

In JPA, what is a named query, how do you declare one with the @NamedQuery annotation, and how do you execute it through the EntityManager?

level: juniorimportance: must knowfreq 58%

answer

  1. Declared once, run by name
  2. Global namespace → Entity.queryName
  3. JPQL validated at boot; native not
  4. createNamedQuery(name, Class) → TypedQuery
  5. Plan cached; Query instance is per-call

basics

~20 s

A named query is a JPQL string declared once under a name, usually with @NamedQuery on an entity class. You execute it with entityManager.createNamedQuery("Name", Result.class), bind parameters, then getResultList() or getSingleResult(). The name is global to the persistence unit.

solid answer

~50 s

`@NamedQuery(name = "Book.findByAuthor", query = "select b from Book b where b.author = :author")` goes on an entity class (the annotation is repeatable, or grouped in `@NamedQueries`). The text is then referenced by name instead of inlined at the call site: ```java List<Book> books = em.createNamedQuery("Book.findByAuthor", Book.class) .setParameter("author", author) .getResultList(); ``` Three points an interviewer wants: - **The name is global to the whole persistence unit**, not scoped to the entity it is written on — hence the `Entity.someName` convention. Two declarations with the same name is a bootstrap failure, not a silent win. - **Named JPQL queries are parsed and validated when the persistence unit starts**, so a typo blows up at startup instead of on first call. - **The translated plan is cached and reused**, so repeat executions skip re-parsing. Everything else — positional/named parameters, `setFirstResult`/`setMaxResults`, result typing — behaves exactly as with `createQuery`. `@NamedNativeQuery` is the raw-SQL sibling and is *not* validated at boot.

go deeper

for a junior

Be able to write the annotation, name it EntityName.something, and call createNamedQuery with a result class plus setParameter. Knowing that the text is validated at startup already puts you ahead.

for a middle

Explain the global namespace, the plan caching, the difference between the Query instance and the cached plan, and that native named queries get no startup validation.

for a senior

Discuss when named queries earn their place — hints and lock mode attached to the declaration, an auditable inventory of queries — versus when the Criteria API is the right tool because predicates are dynamic.

for a principal

Frame it as a codebase policy: where query text lives, how it is reviewed and tuned, whether externalising to orm.xml is worth the indirection, and how startup validation acts as a cheap schema-drift alarm across services.

## What a named query is A named query is a query string declared **once**, as metadata of the persistence unit, under a name — and then executed by that name. It is not a different query language: the string is ordinary JPQL (or HQL, Hibernate's superset). What changes is *where the text lives* and *when it is processed*. Declaration normally sits on an entity class: ```java @Entity @NamedQuery(name = "Book.findByAuthor", query = "select b from Book b where b.author = :author order by b.title") public class Book { ... } ``` The annotation is repeatable in modern JPA; older code groups several inside `@NamedQueries({ ... })`. The same declarations can be written in XML instead (`orm.xml`), which is how teams keep long SQL/JPQL out of Java source. ## Executing one ```java TypedQuery<Book> q = em.createNamedQuery("Book.findByAuthor", Book.class); q.setParameter("author", author); q.setMaxResults(20); List<Book> books = q.getResultList(); ``` `createNamedQuery(String)` returns an untyped `Query`; the two-argument overload returns a `TypedQuery<T>` and is what you should use. If the name does not exist, you get an `IllegalArgumentException` at that call. A `Query` object is **stateful and not thread-safe** — you create a fresh one per execution; what is shared and reused is the *compiled plan* behind the name, not the `Query` instance. ## The name is a global namespace The single most common surprise: the name is **not** scoped to the entity class the annotation happens to sit on. All named queries in a persistence unit share one flat namespace. Declaring `"findAll"` on two entities is a duplicate-name error when the persistence unit boots. The universal convention `EntityName.queryName` exists purely to partition that namespace by hand. ## Startup parsing, and why it matters When the persistence unit is built, each named **JPQL** query is parsed, its entity and attribute references are resolved against the mapping metadata, and it is translated to SQL. A misspelled attribute, a renamed field, a syntax error — all surface as a startup failure with the query name in the message. Inline `createQuery("...")` strings get none of that: they fail the first time that code path executes, which may be in production, on a rare branch. That validation is also *why* named queries are cheap at runtime. The plan is already in the query-plan cache, so execution is: fetch plan, bind parameters, run JDBC. An inline string has to be looked up in the plan cache by its full text (a hash of the string) and translated on first miss. The same guarantee does **not** extend to `@NamedNativeQuery`. Native SQL is opaque to the provider — it cannot know your database's dialect quirks or whether a table exists — so a named native query is registered but not verified. It fails when it runs. ## Parameters and result shape Named queries support named parameters (`:author`) and positional ones (`?1`); named parameters are the norm because the declaration and the call site are far apart, so a positional index is easy to get wrong. The result class is supplied at the call site via `createNamedQuery(name, Book.class)`. Newer JPA revisions also allow declaring `resultClass` on the annotation itself, which lets the provider check the projection at boot too. ## Extras carried on the declaration `@NamedQuery` can carry a `lockMode` and an array of `@QueryHint`s, so caching, fetch size, timeout or a pessimistic lock become part of the declaration rather than something every call site must remember to set. That is a real argument for the style: the *policy* travels with the query. ## Trade-offs Against: the query text is far from the code that uses it, so reading a call site tells you nothing about what runs; refactoring tools help less; and the flat namespace invites collisions. Dynamic predicates cannot be expressed at all — a query whose WHERE clause varies with user input belongs in the Criteria API, not in a named query, and string-concatenating a named query defeats the whole point. For: startup validation, one place to review and tune, hints attached to the declaration, and an obvious inventory of every query the application can issue — which is genuinely useful during a schema change or a performance audit.

  • Are named queries thread-safe — can you cache the Query object returned by createNamedQuery in a field and reuse it?
    No. A Query is a mutable, session-bound object: it holds bound parameters, first/max results and flush mode, and it is tied to the EntityManager that created it. Sharing one across threads or requests corrupts parameters and leaks a closed persistence context. What is safely shared is the compiled plan the provider keeps internally, keyed by the query name; you create a cheap new Query per execution.
  • Where would you put a query whose filters depend on which fields a user actually filled in?
    Not in a named query. A named query is a fixed string, so variable predicates would force string concatenation, which loses startup validation and risks injection. That case belongs to the Criteria API, which builds the predicate tree programmatically and still goes through the same translation and parameter binding.

saying these in an interview costs you the question

  • Thinking the query name is scoped to the entity class it is annotated on, so duplicates across entities are fine
  • Believing @NamedNativeQuery is syntax-checked at startup like JPQL is
  • Caching and reusing the returned Query object across requests or threads
  • Claiming named queries are faster because 'the SQL is precompiled in the database' — the caching is in the provider's plan cache, not a database prepared-statement plan
  • Building the query string by concatenation and still calling it a named query

context

open as a page

Teams sometimes move JPQL out of inline createQuery calls into @NamedQuery declarations specifically for the fail-fast behaviour. What exactly does JPA/Hibernate check when the persistence unit boots, what does it not check, and how does that differ for @NamedNativeQuery?

level: middleimportance: must knowfreq 46%

basics

~20 s

At bootstrap Hibernate parses every named JPQL query and resolves entity and attribute names against the mappings, so typos and stale field references fail startup. It does not run the query or check the real database schema. Named native SQL is registered unparsed and only fails when executed.

open as a page

JPA lets you declare named queries either with annotations on entity classes or in an XML mapping file such as META-INF/orm.xml. What does the XML route give you, and what are the override rules when the same query name appears in both places?

level: middleimportance: should knowfreq 34%

basics

~20 s

orm.xml declares the same named queries outside Java, so query text can be reviewed, edited or swapped per deployment without recompiling entities. XML metadata takes precedence over annotations: a named query defined in orm.xml with the same name replaces the annotated one.

open as a page

Beyond holding the query text, a JPA @NamedQuery declaration can carry query hints and a lock mode. What can you attach there, and why would you prefer attaching it to the declaration over setting it at each call site?

level: seniorimportance: nice to knowfreq 26%

basics

~20 s

@NamedQuery accepts a lockMode plus @QueryHint entries such as timeout, JDBC fetch size, read-only mode, query-cache participation and a SQL comment. Attaching them to the declaration makes the policy travel with the query, so no call site can forget it and every caller gets identical behaviour.

open as a page