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?
answer
- orm.xml auto-loaded; others via <mapping-file>
- XML beats annotations for the same name
- xml-mapping-metadata-complete ignores annotations entirely
- CDATA for < and > inside <query>
- Per-vendor native variants under one name
basics
~20 sorm.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.
solid answer
~60 sBoth routes produce identical runtime objects; only the *source of the metadata* differs. In XML: ```xml <named-query name="Book.findByAuthor"> <query>select b from Book b where b.author = :author</query> <hint name="org.hibernate.cacheable" value="true"/> </named-query> ``` Why teams do it: - **Query text is not compiled in** — a long JPQL or a tuned native SQL can be changed by editing a mapping file, and one deployment can ship a vendor-specific variant of a native query. - **Entity classes stay readable** instead of carrying a wall of `@NamedQuery` annotations. - **It is the only route when you cannot annotate the class** — generated or third-party entities. Override rules: XML mapping metadata **wins over annotations**. A `<named-query>` with the same name replaces the annotated declaration rather than colliding with it. Setting `<xml-mapping-metadata-complete/>` goes further and makes the provider ignore annotation-sourced metadata entirely. Duplicates *within* the same source are still a bootstrap error. Cost: the text moves away from the code and away from IDE support, so it is worth it mainly for large or environment-specific queries.
go deeper
Know that named queries can live in orm.xml as well as annotations and that both are used identically at the call site.
State the precedence rule clearly (XML overrides annotations), mention metadata-complete, and give a real reason to externalise such as long or vendor-specific SQL.
Weigh the override as action-at-a-distance during debugging, and describe when per-environment mapping files genuinely pay for themselves versus adding confusion.
Treat it as a codebase convention question: one place for query text, documented precedence, and whether shipping vendor-specific query files is a portability strategy you actually want to maintain.
## Two sources, one model JPA reads mapping metadata from two places: annotations on classes, and XML mapping files. `META-INF/orm.xml` is picked up automatically; other files are listed in `persistence.xml` via `<mapping-file>`. Whatever the source, the provider builds the same internal declaration — there is no runtime difference between an XML-declared and an annotation-declared named query, and `createNamedQuery("Book.findByAuthor")` cannot tell them apart. ```xml <entity-mappings xmlns="https://jakarta.ee/xml/ns/persistence/orm" version="3.0"> <named-query name="Book.findRecent"> <query><![CDATA[select b from Book b where b.published > :since]]></query> </named-query> <named-native-query name="Book.stats" result-set-mapping="BookStats"> <query><![CDATA[select ...]]></query> </named-native-query> </entity-mappings> ``` Note the CDATA wrapper: XML has no love for `<` and `>` in a comparison, and unescaped angle brackets are the classic first mistake. ## Why externalise **Editable without recompiling.** The query lives in a resource file. For an operator debugging a slow report, adding a hint or reshaping a join means editing a file and restarting, not rebuilding the artifact. This matters most for *native* queries, where the change may be a vendor-specific optimizer hint. **Per-environment or per-vendor variants.** Because the mapping file is selected in `persistence.xml`, one build can ship several files and pick the one matching the target database. A named native query tuned for one engine and another for a second, both under the same query name, is a legitimate portability tactic that annotations cannot express. **Entity classes stay about the domain.** A dozen `@NamedQuery` annotations above a class push the actual model off the screen. Moving them out is a readability call. **Classes you cannot annotate.** Generated sources, entities from a shared jar, or a codebase where annotating is not permitted — XML is the only route. **Central inventory.** One file listing every query the application can issue is a genuinely useful artifact during a schema migration or a performance review. ## Precedence rules The JPA rule is that **XML mapping metadata overrides annotations**. For named queries specifically: if `orm.xml` declares `<named-query name="Book.findByAuthor">` and the `Book` class also carries `@NamedQuery(name = "Book.findByAuthor", ...)`, the XML declaration is the one that survives. This is deliberate — it is what makes 'ship an override for this deployment' work — and it is a one-way rule: annotations never override XML. Two modifiers are worth knowing: - `<xml-mapping-metadata-complete/>` in `<persistence-unit-metadata>` tells the provider to **ignore all annotation metadata** for the persistence unit. The XML is then the complete truth; anything not declared there does not exist. Useful when you want no ambiguity about what is running, brutal if you forget a query. - `<metadata-complete="true">` on an individual `<entity>` does the same for that class only. Duplicates *within one source* remain an error: two `<named-query>` elements with the same name, or two identical annotation names, fail the bootstrap. Overriding only works across the annotation/XML boundary. Because the namespace is global to the persistence unit (not per entity, not per file), the `EntityName.queryName` convention matters even more once queries live in a shared XML file where several developers add entries. ## Costs The query text loses IDE support that a JPQL-aware editor may give inside annotations: no navigation from the string to the entity attribute, weaker refactoring. Reading a call site tells you nothing about the query — you must go find the name in a file. And an override that silently replaces an annotated query is exactly the kind of action-at-a-distance that confuses a debugging session; anyone who does not know the precedence rule will read the annotation and reason about the wrong SQL. Startup validation still applies, and applies to whichever declaration won, so a broken override fails the boot rather than lurking. That takes the sharpest edge off the trade-off. ## Practical stance A reasonable default: keep short, stable JPQL as annotations next to the entity; externalise long queries, native SQL, and anything that must vary by database vendor or environment. Mixing is fine — but write down which queries are externalised and why, because the precedence rule is invisible at the call site.
- You add a <named-query> to orm.xml with the same name as an existing @NamedQuery. Do you get a duplicate-name bootstrap error?No. Cross-source duplication is an override, not a collision: the XML declaration replaces the annotated one and the persistence unit starts normally. Duplicate-name errors only arise within a single source — two annotations with the same name, or two elements in XML. That silent override is precisely why the precedence rule is worth documenting for the team.
- Does moving a named query into orm.xml lose the startup validation you get from annotations?No. Validation happens on the merged metadata model after XML and annotations are combined, so whichever declaration wins is parsed and resolved against the mappings at bootstrap. A typo in an externalised JPQL query still fails the boot. Native SQL in XML is as unvalidated as native SQL in an annotation.
saying these in an interview costs you the question
- Saying annotations win over orm.xml — the precedence runs the other way
- Expecting a duplicate-name error when the same name is declared in both an annotation and XML
- Forgetting CDATA or entity escaping and shipping XML that breaks on a > comparison
- Assuming the query name is scoped per mapping file rather than global to the persistence unit
- Believing externalised queries skip startup validation