A Hibernate named query is a JPQL, HQL or SQL query that we declare once with a fixed name and run anywhere by that name. We declare it with @NamedQuery on an entity (or in orm.xml), and at runtime em.createNamedQuery() finds the query by name, so we bind the parameters and run it like any other query.
We use named queries for queries that several classes share, because Hibernate parses and validates every JPQL/HQL named query when the EntityManagerFactory starts. A typo stops the application at startup instead of failing later in production.
The following example declares a query on the Room entity of a hotel app and runs it by name.
@Entity
@NamedQuery(name = "Room.findAvailableByType",
query = "select r from Room r where r.type = :type and r.available = true order by r.number",
resultClass = Room.class)
public class Room { ... }
List<Room> doubles = em.createNamedQuery("Room.findAvailableByType", Room.class)
.setParameter("type", "double")
.getResultList(); // [201 double 120.00, 202 double 120.00]
// select r1_0.id,r1_0.available,r1_0.nightlyRate,r1_0.number,r1_0.type from Room r1_0
// where r1_0.type=? and r1_0.available=true order by r1_0.number
Notice that the usage code refers to the query only by its name, Room.findAvailableByType, and Hibernate generates the same SQL as for a dynamic query.
Next, we build a small hotel example and look at parameters, result types, native SQL and orm.xml. After that, we move the check to compile time with JPA 3.2 and Hibernate 7.
1. How Does a Named Query Differ From createQuery()?
A named query and em.createQuery() run the same JPQL and produce the same SQL, and the difference is when Hibernate reads the query string. Hibernate parses a dynamic query, em.createQuery(“select …”), the first time that line runs, whereas it parses a named query while the factory starts, before any request reaches the application. For example, a typo in the query behind a rarely used “cancel booking” screen shows up at the first test run with a named query, but with a dynamic query only when a guest clicks that button.

The styles differ in where the query text lives and when a mistake in it shows up.
| Style | Where the query lives | Checked | Typo found |
|---|---|---|---|
| em.createQuery(“…”) | Inline in Java code | When the line runs | At runtime, in the code path that runs it |
| @NamedQuery | On the entity or in orm.xml | At startup | When the application starts |
| @NamedNativeQuery | On the entity or in orm.xml | By the database, when it runs | At runtime |
| @HQL method (Hibernate 7) | On an interface method | At compile time | When javac runs |
Named queries suit queries that many classes share or that we want reviewed in one place. They have a searchable name next to the entity, and they fail fast. For a one-off query used by one method, a dynamic query is fine.
2. Named Query Example
We build a small hotel with Hibernate 7.4, Java 25 and an in-memory H2 database, and run each kind of named query against it.
2.1. Rooms and Bookings
A room has a number (101), a type (“single”, “double”, “suite”), a nightly rate and an available flag. A booking holds the guest name and the number of nights, and it points to the booked room.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Integer number;
private String type;
private BigDecimal nightlyRate;
private boolean available;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String guest;
private int nights;
@ManyToOne(fetch = FetchType.LAZY)
private Room room;
The @ManyToOne association is LAZY, so loading a booking does not load its room unless the query asks for it.
2.2. Declaring a Named Query With @NamedQuery
We put @NamedQuery on the entity class. Since JPA 2.2 the annotation is repeatable, so we stack several of them and no longer need the @NamedQueries wrapper.
@Entity
@NamedQuery(name = "Room.findAvailableByType",
query = "select r from Room r where r.type = :type and r.available = true order by r.number",
resultClass = Room.class)
@NamedQuery(name = "Room.countAvailable",
query = "select count(r) from Room r where r.available = true")
@NamedQuery(name = "Room.updateRate",
query = "update Room r set r.nightlyRate = :rate where r.type = :type")
public class Room { ... }
The annotation has two required attributes and three optional ones.
| Attribute | Required | What it does |
|---|---|---|
| name | Yes | The key for createNamedQuery(). Unique in the whole persistence unit, not only in the entity |
| query | Yes | The JPQL or HQL text |
| resultClass | No (new in JPA 3.2) | The type of each result. Only queries with it appear in emf.getNamedQueries() (section 2.10) |
| lockMode | No | A lock such as PESSIMISTIC_WRITE; the query must then run in a transaction |
| hints | No | Provider hints, for example org.hibernate.readOnly |
We prefix each name with the entity, as in Room.findAvailableByType, because all names share one namespace and Spring Data JPA looks queries up by this pattern (see FAQ 3.5).
A named query can also use join fetch to load the lazy room in the same statement.
@NamedQuery(name = "Booking.findByGuest",
query = "select b from Booking b join fetch b.room where b.guest = :guest")
Booking booking = em.createNamedQuery("Booking.findByGuest", Booking.class)
.setParameter("guest", "Lokesh")
.getSingleResult(); // Lokesh in 102 for 3 nights
// select b1_0.id,b1_0.guest,b1_0.nights,r1_0.id,...,r1_0.type from Booking b1_0
// join Room r1_0 on r1_0.id=b1_0.room_id where b1_0.guest=?
2.3. Named and Positional Parameters
A named parameter starts with a colon (:type), and we bind it by name. A positional parameter is a question mark with a number (?1), and we bind it by its number. One query must use one style, because Hibernate rejects a query that mixes named and positional parameters.
@NamedQuery(name = "Room.findByRateBetween",
query = "select r from Room r where r.nightlyRate between ?1 and ?2 order by r.number")
@NamedQuery(name = "Room.findByTypes",
query = "select r from Room r where r.type in :types order by r.number")
List<Room> midRange = em.createNamedQuery("Room.findByRateBetween", Room.class)
.setParameter(1, new BigDecimal("100"))
.setParameter(2, new BigDecimal("300"))
.getResultList(); // [201 double 120.00, 202 double 120.00, 301 suite 250.00]
List<Room> singlesAndSuites = em.createNamedQuery("Room.findByTypes", Room.class)
.setParameter("types", List.of("single", "suite"))
.getResultList(); // [101 single 80.00, 102 single 80.00, 301 suite 250.00]
// ... where r1_0.type in (?,?) ...
A collection bound to an in parameter becomes one ? per element in the SQL. Named parameters read better and survive changes to the query, so we use them unless a tool requires positions.
2.4. Choosing the Result Type
The class we pass to createNamedQuery(name, type) must match what the select clause returns. One query with two columns can return either Object[] or a record.
![Five rows map a select clause to a class and a result: select r from Room r with Room.class gives a managed Room entity; select count(r) with Long.class gives 3; select r.number, r.nightlyRate with Object[].class gives [101, 80.00]; the same select with RoomRate.class gives RoomRate[number=101, ...]; native SQL with a resultSetMapping and RoomCount.class gives RoomCount[type=double, rooms=2]; a wrong class fails with QueryTypeMismatchException](https://howtodoinjava.com/wp-content/uploads/2026/10/hibernate-named-query-five-rows-map-select-clause.png)
// @NamedQuery(name = "Room.findRates",
// query = "select r.number, r.nightlyRate from Room r where r.type = :type order by r.number")
public record RoomRate(Integer number, BigDecimal nightlyRate) { }
List<Object[]> rows = em.createNamedQuery("Room.findRates", Object[].class)
.setParameter("type", "single")
.getResultList(); // rows.get(0) = [101, 80.00]
List<RoomRate> rates = em.createNamedQuery("Room.findRates", RoomRate.class)
.setParameter("type", "single")
.getResultList(); // [RoomRate[number=101, nightlyRate=80.00], RoomRate[number=102, ...]]
Long free = em.createNamedQuery("Room.countAvailable", Long.class)
.getSingleResult(); // 3
Hibernate 7 calls the record’s constructor with the selected columns in order, so we do not need a select new expression. A class that does not match fails as soon as we create the query.
org.hibernate.query.QueryTypeMismatchException: Incorrect query result type: query produces 'java.lang.Long' but type 'com.howtodoinjava.hibernate.namedquery.Room' was given
2.5. Update and Delete Named Queries
A named query can also hold an update or delete statement. We create it without a result class and call executeUpdate(), which returns the number of changed rows.
emf.runInTransaction(em -> {
int updated = em.createNamedQuery("Room.updateRate")
.setParameter("rate", new BigDecimal("90.00"))
.setParameter("type", "single")
.executeUpdate(); // updated = 2
}); // update Room r1_0 set nightlyRate=? where r1_0.type=?
The statement goes straight to the database. A room already loaded in the same persistence context keeps its old rate (80.00) until we call em.refresh(room), so we run bulk updates in their own transaction or reload the entities afterwards.
2.6. Hibernate’s @NamedQuery With Extra Settings
Hibernate has its own @org.hibernate.annotations.NamedQuery. It takes the same name and query and adds typed settings that JPA only offers as string hints.
@org.hibernate.annotations.NamedQuery(name = "Booking.findLongStays",
query = "select b from Booking b where b.nights >= :nights order by b.guest",
readOnly = true, // entities are not dirty-checked
timeout = 2, // seconds
fetchSize = 50) // JDBC fetch size
List<Booking> longStays = em.createNamedQuery("Booking.findLongStays", Booking.class)
.setParameter("nights", 3)
.getResultList(); // [Alex, Lokesh]
boolean readOnly = em.unwrap(Session.class).isReadOnly(longStays.get(0)); // true
longStays.get(0).setNights(10); // ignored at commit, Alex keeps 5 nights
Hibernate’s @NamedQuery also has cacheable and cacheRegion, which put the results in the query cache of the second-level cache. We write the full package name, because both annotations are called NamedQuery and a class can import only one of them.
2.7. Native SQL With @NamedNativeQuery
The annotation @NamedNativeQuery holds plain SQL for the database, so it can use table and column names and database functions that JPQL cannot express. With resultClass, Hibernate turns each row into a managed entity.
@NamedNativeQuery(name = "Room.findFreeUnderRate",
query = "select * from Room where available = true and nightlyRate <= ? order by number",
resultClass = Room.class)
List<Room> cheapRooms = em.createNamedQuery("Room.findFreeUnderRate", Room.class)
.setParameter(1, new BigDecimal("100"))
.getResultList(); // [101 single 80.00]
For rows that are not entities, a @SqlResultSetMapping tells Hibernate which constructor to call.
@NamedNativeQuery(name = "Room.countByType",
query = "select type, count(*) as rooms from Room group by type order by type",
resultSetMapping = "RoomCountMapping")
@SqlResultSetMapping(name = "RoomCountMapping",
classes = @ConstructorResult(targetClass = RoomCount.class,
columns = {@ColumnResult(name = "type"), @ColumnResult(name = "rooms", type = Long.class)}))
public record RoomCount(String type, Long rooms) { }
List<RoomCount> counts = em.createNamedQuery("Room.countByType", RoomCount.class).getResultList();
// [RoomCount[type=double, rooms=2], RoomCount[type=single, rooms=2], RoomCount[type=suite, rooms=1]]
Hibernate does not check native SQL at startup. A wrong table name passes the startup and fails only when the query runs.
org.hibernate.exception.SQLGrammarException: Could not prepare statement [Table "ROOMS" not found; SQL statement:
select * from Rooms where available = true [42102-252]] [select * from Rooms where available = true]
2.8. Defining Named Queries in orm.xml
The same queries can live in META-INF/orm.xml, which keeps long SQL out of the entity and lets us change a query without touching Java code. The file uses the Jakarta Persistence 3.2 schema.
<entity-mappings xmlns="https://jakarta.ee/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence/orm https://jakarta.ee/xml/ns/persistence/orm/orm_3_2.xsd"
version="3.2">
<named-query name="Booking.countByRoom">
<query>select count(b) from Booking b where b.room.number = :number</query>
</named-query>
<named-native-query name="Booking.guestsSql">
<query>select guest from Booking order by guest</query>
</named-native-query>
</entity-mappings>
With persistence.xml, the provider reads META-INF/orm.xml by default. With HibernatePersistenceConfiguration, we must register the file ourselves, otherwise its queries fail with No query named ‘Booking.countByRoom’.
new HibernatePersistenceConfiguration("named-queries")
.mappingFile("META-INF/orm.xml")
.managedClasses(Room.class, Booking.class)
...
Long roomBookings = em.createNamedQuery("Booking.countByRoom", Long.class)
.setParameter("number", 301)
.getSingleResult(); // 1
List<?> guests = em.createNamedQuery("Booking.guestsSql").getResultList(); // [Alex, Lokesh, Maria]
An XML query with the same name as an annotation replaces the annotation, so a mapping file can change a query without recompiling the entity (see FAQ 3.2). The file orm.xml also replaces the <query> element of the old Hibernate hbm.xml files.
2.9. What Happens When a Named Query Has a Typo?
Say a developer misspells an attribute and writes availble instead of available.
@NamedQuery(name = "Room.findFree",
query = "select r from Room r where r.availble = true")
The application does not start, because Hibernate logs the error and createEntityManagerFactory() throws a PersistenceException.
ERROR org.hibernate.orm.query - HHH90003001: Error in named query: Room.findFree
org.hibernate.query.sqm.UnknownPathException: Could not resolve attribute 'availble' of 'com.howtodoinjava.hibernate.namedquery.Room' [select r from Room r where r.availble = true]
...
jakarta.persistence.PersistenceException: Unable to build Hibernate SessionFactory [persistence unit: named-queries]
Caused by: org.hibernate.query.NamedQueryValidationException: Errors in named queries:
[1] Error in query named 'Room.findFree': Could not resolve attribute 'availble' of 'com.howtodoinjava.hibernate.namedquery.Room' [select r from Room r where r.availble = true]
The message names the broken query and points to the wrong attribute of Room. The same check catches a wrong entity name and a type mismatch, so any test that starts the factory shows the mistake.
| Mistake in the query | Message after “Error in query named …” |
|---|---|
| r.availble | Could not resolve attribute ‘availble’ of ‘…Room’ |
| from Rooms r (table-like name) | Could not resolve root entity ‘Rooms’ |
| r.nightlyRate = ‘cheap’ | Cannot compare left expression of type ‘java.math.BigDecimal’ with right expression of type ‘java.lang.String’ |
Old Hibernate versions reported the same problem as “Errors in named queries” while the SessionFactory was created. The fix has not changed. A JPQL query uses the entity class name and its Java field names, never table or column names.
The check is on by default. If we set hibernate.query.startup_check to false, Hibernate skips it, and the error moves to the first createNamedQuery() call.
config.property("hibernate.query.startup_check", false); // factory starts
TypedQuery<Room> query = em.createNamedQuery("Room.findFree", Room.class); // IllegalArgumentException: ...
// Could not resolve attribute 'availble' ...
We keep the check on, because it is the main reason to use named queries.
2.10. Type-Safe References in JPA 3.2
String names have one weakness. A misspelled call such as createNamedQuery(“Room.findAvailabelByType”) compiles and fails at runtime with IllegalArgumentException: No query named …. JPA 3.2 adds TypedQueryReference, a reference to a named query that carries its result type, and em.createQuery(reference) to run it.
The static metamodel generator, which writes Room_ for the entity Room, also adds a constant with the name and a typed reference for each named query.
public static final String QUERY_ROOM_FIND_AVAILABLE_BY_TYPE = "Room.findAvailableByType";
public static volatile TypedQueryReference<Room> _Room_findAvailableByType_;
public static volatile TypedQueryReference<Long> _Room_countAvailable_;
List<Room> singles = em.createQuery(Room_._Room_findAvailableByType_)
.setParameter("type", "single")
.getResultList(); // [101 single 80.00]
Long free = em.createQuery(Room_._Room_countAvailable_)
.getSingleResult(); // 3, no cast and no class argument
With the generated references, a renamed or deleted query breaks the build instead of a request. The generator needs one plugin entry in pom.xml.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-processor</artifactId>
<version>7.4.11.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
Without the processor, emf.getNamedQueries(Room.class) returns a map of references at runtime. In Hibernate 7.4 the map contains only the queries that declare a resultClass.
Set<String> names = emf.getNamedQueries(Room.class).keySet(); // [Room.findAvailableByType, Room.findFreeUnderRate]
2.11. Checking Queries at Compile Time With @HQL
Hibernate Processor can also check the query text while javac runs, in one of two ways.
- @CheckHQL on a class makes the processor validate the @NamedQuery annotations of that class.
- @HQL on an interface method declares the query next to its Java signature, and the processor generates the implementation.
public interface HotelQueries {
@HQL("where type = :type and available = true order by number")
List<Room> findAvailable(String type);
@HQL("select count(*) from Booking where room.number = :number")
long countBookings(Integer number);
}
// generated class HotelQueries_, the EntityManager comes first
List<Room> doubles = HotelQueries_.findAvailable(em, "double"); // [201 double 120.00, 202 double 120.00]
long bookings = HotelQueries_.countBookings(em, 102); // 1
The processor checks each name and type in the query against the entities, so the availble typo from section 2.9 fails the build.
[ERROR] HotelQueries.java:[11,8] Could not interpret path expression 'availble'
[ERROR] Room.java:[38,8] Could not resolve attribute 'availble' of 'com.howtodoinjava.hibernate.namedquery.Room'
The same processor also generates the implementations of Jakarta Data repositories. For new code on Hibernate 7, @HQL methods give the earliest feedback; @NamedQuery stays the portable JPA choice that works with any provider.
The complete project on GitHub runs every query from section 2 and prints its SQL, and 27 JUnit tests check each result and error (mvn -q compile exec:java, mvn test).
3. Named Query FAQs
3.1. What Replaced session.getNamedQuery()?
The methods Session.getNamedQuery() and the untyped Session.createNamedQuery(String) are deprecated in Hibernate 7. We use the JPA method with a result class, or the Hibernate methods that separate reads from writes.
| Hibernate 5 code | Hibernate 7 and JPA code |
|---|---|
| session.getNamedQuery(name) for a select | em.createNamedQuery(name, Room.class) or session.createNamedSelectionQuery(name, Room.class) |
| session.getNamedQuery(name) for an update | em.createNamedQuery(name) or session.createNamedMutationQuery(name) |
| query.list() | getResultList() |
| query.uniqueResult() | getSingleResult() or getSingleResultOrNull() |
Session session = em.unwrap(Session.class);
List<Room> singles = session.createNamedSelectionQuery("Room.findAvailableByType", Room.class)
.setParameter("type", "single")
.getResultList(); // [101 single 90.00]
int updated = session.createNamedMutationQuery("Room.updateRate")
.setParameter("rate", new BigDecimal("130.00"))
.setParameter("type", "double")
.executeUpdate(); // 2
3.2. Can Two Named Queries Have the Same Name?
Not in annotations. Names are global in the persistence unit, so the same name on two classes stops the startup.
org.hibernate.DuplicateMappingException: Duplicate named query 'Room.countAvailable'
An orm.xml entry with the same name as an annotation is the one exception, because the XML definition replaces the annotation. In the project, an XML version of Room.findByTypes that adds r.available = true returns only room 101 for “single” and “suite”.
3.3. Why Does a Named Native Query Fail When I Pass a Result Class?
A native query declared without resultClass or resultSetMapping does not tell Hibernate how to build typed results. Calling it with a class fails with an IllegalArgumentException.
java.lang.IllegalArgumentException: Named query exists, but did not specify a resultClass
We either call createNamedQuery(name) without a class and get a raw List, or we declare the result type. In orm.xml, we set the result-class attribute.
<named-native-query name="Booking.guestNames" result-class="java.lang.String">
<query>select guest from Booking order by guest</query>
</named-native-query>
List<String> guestNames = em.createNamedQuery("Booking.guestNames", String.class).getResultList(); // [Alex, Lokesh, Maria]
3.4. Are Named Queries Faster Than Dynamic Queries?
Not in a way we can measure in normal applications. Hibernate keeps the parsed form of dynamic JPQL strings in its query plan cache, so a repeated createQuery() with the same string skips parsing too, and the database runs the same SQL either way. The real benefits of named queries are the startup check and one stable name for each query, kept in one place.
3.5. How Does Spring Data JPA Use Named Queries?
A Spring Data JPA repository first looks for a named query called <Entity>.<methodName>. If it finds one, it runs that query instead of deriving one from the method name.
public interface RoomRepository extends JpaRepository<Room, Long> {
List<Room> findAvailableByType(@Param("type") String type);
}
For example, a call to roomRepository.findAvailableByType(“double”) runs the Room.findAvailableByType query from section 2.2.
3.6. Where Can I Declare a Named Query?
JPA allows @NamedQuery on an entity or a mapped superclass. Hibernate also reads it from any class we register as a managed class, which is how the project keeps its broken test queries in a separate BrokenQueries class.
| Place | How Hibernate finds it |
|---|---|
| Entity class | Always, with the entity |
| Any class with @NamedQuery | Registered with managedClass() or a <class> entry in persistence.xml |
| orm.xml | Listed as a mapping file |
| Package (package-info.java) | Only Hibernate’s own @NamedQuery, with addPackage() |
For stored procedures, JPA has @NamedStoredProcedureQuery, and for queries that we build in code at runtime, we use the Criteria API.
4. Conclusion
A named query is a JPQL or SQL statement declared once with @NamedQuery, @NamedNativeQuery or orm.xml and run by name with createNamedQuery(). Hibernate checks every JPQL named query at startup, so typos fail early, while native queries are checked only by the database. On Hibernate 7 and JPA 3.2, TypedQueryReference removes the string names from our code, and @HQL methods move the check to compile time.
5. References
- Jakarta Persistence 3.2 specification: Named Queries
- Jakarta Persistence 3.2 specification: Canonical Metamodel
- NamedQuery JavaDoc (Jakarta Persistence 3.2)
- NamedNativeQuery JavaDoc (Jakarta Persistence 3.2)
- TypedQueryReference JavaDoc (Jakarta Persistence 3.2)
- Hibernate ORM 7.4 User Guide: Declaring named queries
- Introduction to Hibernate 7.4: Named queries
- Introduction to Hibernate 7.4: Generated query methods
Happy Learning !!
We can’t do like this for Update Query.
It should be like
“UPDATE DepartmentEntity d SET d.name=:’Finance’ where d.id = :id”
thanx for the great article.. Can we define all the Named Queries at one place like some property file instead of writing in the entity class. thanx
Using annotations? NO, you can not. I tried to find out other way, and i got this: https://stackoverflow.com/questions/602397/how-do-i-externalize-named-queries-in-a-hibernate-annotations-app/604345#604345 Look at last reply using named-query tag.
Hi Lokesh,
I have uses JPA 2 and it allows to write named query in orm mapping file, in fact we have used annotation for mapping and orm.xml for writing named query. Haven’t try it using in hibernate.
No himansu, I have not tried it yet.I will soon go though it and let you know my thoughts. Thanks for the suggestion.
We can define all named queries on One place. See the example
https://stackoverflow.com/questions/26838032/can-we-define-all-the-named-queries-at-one-place-like-some-property-file-instead/26840015#26840015
Is it necessary to define string constants?? Any advantage?
It is only for making the code more readable. Also, can refer queryname outside the entity with more clarity.
No other use.
If it comes to reusability you should avoid the constants and provide a method to call the query and hide all the technical stuff to do it as early as possible. If you use constants – next step would be constants for parameters – you are opening the door for non-compiler-safe use everywhere.