skip to content

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