HQL Tutorial: Hibernate Query Language With Examples

Hibernate Query Language (HQL) queries entities and attributes instead of tables and columns. Learn HQL select, joins, join fetch, aggregates, subqueries, update, delete and insert in Hibernate 7, with the SQL Hibernate generates for each query.

Jakarta_EE

Hibernate Query Language (HQL) is an object-oriented query language that we write against entity classes and their attributes instead of tables and columns. A query such as select a.title from Artwork a names the Artwork entity and its title field, and Hibernate translates it into the SQL of our database before it runs.

We use HQL for most reads and bulk changes in a Hibernate app, for example to list the artworks of one artist in a museum catalog or to raise the estimated value of every photo. HQL is a superset of JPQL, the query language of Jakarta Persistence, so every JPQL query is valid HQL, and HQL adds features such as limit, common table expressions and insert statements.

The following example maps an Artwork entity and runs a select, a join, a group by and an update in HQL.

@Entity
@Table(name = "artworks")
public class Artwork {

  @Id @GeneratedValue
  private Long id;
  private String title;
  @Column(name = "created_year")
  private Integer year;
  private BigDecimal estimatedValue;
  @Enumerated(EnumType.STRING)
  private Medium medium;                 // PAINTING, SCULPTURE, PHOTO
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "artist_id")
  private Artist artist;
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "gallery_id")
  private Gallery gallery;               // null = in storage
}
List<Artwork> recent = em.createQuery(
    "select a from Artwork a where a.year > 2019 order by a.year", Artwork.class)
    .getResultList();                     // [Red Fields, Old Bridge, Night Train]
// select a1_0.id,...,a1_0.created_year from artworks a1_0 where a1_0.created_year>2019 order by a1_0.created_year

List<String> french = em.createQuery(
    "select a.title from Artwork a where a.artist.country = :country order by a.title", String.class)
    .setParameter("country", "France")
    .getResultList();                     // [Iron Leaf, Old Bridge, Red Fields, Stone Bird]
// select a1_0.title from artworks a1_0 join artists a2_0 on a2_0.id=a1_0.artist_id where a2_0.country=? ...

List<Object[]> perGallery = em.createQuery(
    "select g.name, count(a) from Gallery g join g.artworks a group by g.name order by g.name", Object[].class)
    .getResultList();                     // [Modern Wing, 3], [Sculpture Hall, 2]

int updated = em.createQuery("update Artwork a set a.estimatedValue = a.estimatedValue * 1.1 where a.medium = :m")
    .setParameter("m", Medium.PHOTO)
    .executeUpdate();                     // updated = 1

Notice that the queries name Artwork, a.year and a.artist.country, and never the artworks table or the created_year column.

Next, we compare HQL with JPQL and SQL. After that, we write select, join, update, delete and insert queries in Hibernate 7 and check the SQL that Hibernate generates for each of them.

1. HQL vs JPQL vs SQL

SQL works with what the database stores (tables, columns and foreign keys), whereas HQL and JPQL work with what our Java code sees (entity names, attribute names and associations). Hibernate reads the entity mappings, replaces each Java name with its table or column, and then writes the SQL in the dialect of our database.

Three boxes: an HQL query on Artwork with a.artist.country and a :country parameter goes through Hibernate (parse into a query tree, resolve names with the entity mappings, render SQL for the H2 dialect) and becomes SQL on the artworks and artists tables with a join and a ? parameter. A table below maps Artwork to artworks, a.year to a1_0.created_year, a.artist.country to a join, :country to a JDBC parameter
The query names entities and attributes. The mapping annotations decide which tables, columns and joins appear in the SQL.

HQL is case-sensitive for entity and attribute names, because they are Java names. The clause from Artwork works, while the table name from artwork fails with Could not resolve root entity ‘artwork’. Keywords such as select or SELECT are not case-sensitive.

JPQL is the standard query language, defined by the Jakarta Persistence 3.2 specification, so it runs on any provider. HQL is Hibernate’s own extension of JPQL.

FeatureSQLJPQL (Jakarta Persistence 3.2)HQL (Hibernate 7)
Queries refer toTables and columnsEntities and attributesEntities and attributes
Joinson a foreign keyPath or association (a.artist)Same as JPQL
ResultRows of columnsEntities, values, Object[], DTOsSame, plus records without select new
select clauseRequiredOptional since 3.2Optional
union, intersect, exceptYesYes, since 3.2Yes
limit / offset in the queryDepends on the databaseNo, use setFirstResult()Yes
with (common table expression)YesNoYes
insert statementYesNoinsert … values and insert … select
Runs onOne database dialectAny JPA providerHibernate only

When an application must stay portable, we can make Hibernate reject the HQL extensions by setting hibernate.jpa.compliance.query to true, which turns on strict JPQL checks.

java.lang.IllegalArgumentException: org.hibernate.query.sqm.StrictJpaComplianceViolation: Strict JPA query language compliance was violated: use of LIMIT/OFFSET clause

2. HQL Examples With Hibernate 7

Our examples query a small art museum in an in-memory H2 database with Hibernate 7.4, Java 25 and Jakarta Persistence 3.2. The complete project on GitHub prints the SQL of every query, and its 40 JUnit tests check every result in this guide (mvn -q compile exec:java, mvn test).

2.1. The Art Museum Model

A museum has galleries and artists, and each artwork belongs to one artist and is shown in one gallery, unless it is in storage and has no gallery. Each association is a many-to-one on Artwork, and Gallery and Artist list their artworks.

// Gallery: name, floor
@OneToMany(mappedBy = "gallery")
private List<Artwork> artworks = new ArrayList<>();

// Artist: name, country
@OneToMany(mappedBy = "artist")
private List<Artwork> artworks = new ArrayList<>();

The @Table annotation names the tables galleries, artists and artworks, and the year attribute is stored in the created_year column, so the SQL shows which names come from the mapping. The museum has six artworks.

titleyearestimatedValuemediumartist (country)gallery
Blue River20191200.00PAINTINGLokesh (India)Modern Wing
Old Bridge2021800.00PAINTINGEmma (France)Modern Wing
Night Train2023450.00PHOTOLokesh (India)Modern Wing
Stone Bird20153000.00SCULPTUREHugo (France)Sculpture Hall
Iron Leaf20182200.00SCULPTUREEmma (France)Sculpture Hall
Red Fields2020950.00PAINTINGHugo (France)null (storage)

Two rows have no related rows. The gallery “East Room” has no artworks yet, and the artist “Mia” from Japan has no artworks either. Later, East Room and Mia show the difference between the join types.

We build the EntityManagerFactory with HibernatePersistenceConfiguration and showSql(true, false, false), so every query prints its SQL.

2.2. Creating and Running a Query

An HQL query is a String that we pass to a factory method together with the Java type of the result, and the method returns a typed query object. Jakarta Persistence and Hibernate’s Session offer four such methods.

MethodReturnsUse it for
em.createQuery(hql, Artwork.class)TypedQuery<Artwork>Select queries, standard API
em.createQuery(hql)QueryUntyped results and executeUpdate()
session.createSelectionQuery(hql, Artwork.class)SelectionQuery<Artwork>Select queries; an update or delete fails with IllegalSelectQueryException
session.createMutationQuery(hql)MutationQueryupdate, delete and insert; a select fails with IllegalMutationQueryException

We get the Session from the EntityManager with unwrap(). When the query returns the entity itself, we can leave out the select clause, which HQL always allowed and JPQL allows since Jakarta Persistence 3.2.

Session session = em.unwrap(Session.class);
List<Artwork> sculptures = session.createSelectionQuery(
        "from Artwork a where a.medium = :medium order by a.title", Artwork.class)
    .setParameter("medium", Medium.SCULPTURE)
    .getResultList();                     // [Iron Leaf, Stone Bird]
// select a1_0.id,...,a1_0.created_year from artworks a1_0 where a1_0.medium=? order by a1_0.title

Three methods read the result. For one row, getSingleResultOrNull() returns null when nothing matches, while getSingleResult() throws a NoResultException.

List<Artwork> all = query.getResultList();               // all matching rows
Artwork bird = em.createQuery("from Artwork a where a.title = :title", Artwork.class)
    .setParameter("title", "Stone Bird").getSingleResult();         // Stone Bird
Artwork none = em.createQuery("from Artwork a where a.title = :title", Artwork.class)
    .setParameter("title", "Sunflowers").getSingleResultOrNull();   // null

2.3. Filtering With where and Parameters

The where clause filters rows with the same operators as SQL, such as =, <>, >, between, in, like, is null, and, or and not. Values from outside the query go in as parameters, which come in two forms.

  • A named parameter starts with a colon, as in :medium, and is set with setParameter(“medium”, value).
  • A positional parameter is a question mark with a number, as in ?1, and is set with setParameter(1, value).
List<Artwork> range = em.createQuery("""
    select a from Artwork a
    where a.estimatedValue between ?1 and ?2
    order by a.estimatedValue desc""", Artwork.class)
    .setParameter(1, new BigDecimal("800"))
    .setParameter(2, new BigDecimal("2500"))
    .getResultList();                     // [Iron Leaf, Blue River, Red Fields, Old Bridge]
// ... from artworks a1_0 where a1_0.estimatedValue between ? and ? order by a1_0.estimatedValue desc

Both forms become JDBC ? placeholders, and the database receives the value separately from the SQL text. We use one style per query. The Jakarta Persistence specification says that named and positional parameters must not be mixed in a single query. Hibernate runs such an HQL query without an error, but it fails once hibernate.jpa.compliance.query is turned on, and other providers can reject it. Never concatenate user input into an HQL string. With the input x’ or ‘1’=’1, the concatenated query where a.title = ‘x’ or ‘1’=’1′ returns all 6 artworks, and the same input as a parameter returns 0.

2.4. Sorting and Pagination

The order by clause takes attributes, paths or expressions, each with asc (the default) or desc, and sorting also works on several attributes and controls where null values go. For pagination, we pick a page of the sorted result, for example when the museum website lists the collection two titles per page. The standard way sets the page in Java code, but Hibernate 6 and 7 also accept limit and offset inside the query.

List<String> page = em.createQuery("select a.title from Artwork a order by a.title", String.class)
    .setFirstResult(2)                    // skip 2 rows
    .setMaxResults(2)                     // return 2 rows
    .getResultList();                     // [Night Train, Old Bridge]
// select a1_0.title from artworks a1_0 order by a1_0.title offset ? rows fetch first ? rows only

List<String> same = em.createQuery(
    "select a.title from Artwork a order by a.title limit 2 offset 2", String.class)
    .getResultList();                     // [Night Train, Old Bridge]
// select a1_0.title from artworks a1_0 order by a1_0.title offset 2 rows fetch first 2 rows only

Hibernate writes the paging clause in the syntax of the database dialect, which is offset … fetch first … rows only for H2. We always sort a paged query, because otherwise the database may return rows in a different order on each page.

2.5. Selecting Attributes Instead of Entities

A select clause with attributes is a projection, which reads only those columns and returns values, not managed entities. The Java type of the result depends on what we select.

select clauseResult classOne result
select aArtwork.classThe Artwork entity “Stone Bird”
select a.titleString.class“Blue River”
select a.title, a.yearObject[].class[Stone Bird, 2015]
select a.title as title, a.estimatedValue as valueTuple.classt.get(“title”) = “Stone Bird”
select new …ArtworkSummary(a.title, a.artist.name)ArtworkSummary.classArtworkSummary[title=Blue River, artist=Lokesh]
List<String> titles = em.createQuery("select a.title from Artwork a order by a.title", String.class)
    .getResultList();                     // [Blue River, Iron Leaf, Night Train, Old Bridge, Red Fields, Stone Bird]
// select a1_0.title from artworks a1_0 order by a1_0.title

List<Object[]> rows = em.createQuery(
    "select a.title, a.year from Artwork a where a.medium = :m order by a.year", Object[].class)
    .setParameter("m", Medium.SCULPTURE)
    .getResultList();                     // [Stone Bird, 2015], [Iron Leaf, 2018]

With Object[], we read each value by position and cast it. A Java record gives every value a name and a type instead.

public record ArtworkSummary(String title, String artist) {}

JPQL fills the record with a constructor expression, that is, select new followed by the fully qualified class name. Hibernate 6 and 7 also accept a plain select list when we pass the record as the result class, and Hibernate matches the list to the record’s constructor.

List<ArtworkSummary> withNew = em.createQuery("""
    select new com.howtodoinjava.hibernate.hql.ArtworkSummary(a.title, a.artist.name)
    from Artwork a where a.medium = :m order by a.title""", ArtworkSummary.class)
    .setParameter("m", Medium.PAINTING)
    .getResultList();

List<ArtworkSummary> implicit = em.createQuery("""
    select a.title, a.artist.name
    from Artwork a where a.medium = :m order by a.title""", ArtworkSummary.class)
    .setParameter("m", Medium.PAINTING)
    .getResultList();

// both: [ArtworkSummary[title=Blue River, artist=Lokesh],
//        ArtworkSummary[title=Old Bridge, artist=Emma],
//        ArtworkSummary[title=Red Fields, artist=Hugo]]
// both: select a1_0.title,a2_0.name from artworks a1_0 join artists a2_0 on a2_0.id=a1_0.artist_id
//         where a1_0.medium=? order by a1_0.title

2.6. Joining Associations

A join combines an entity with an associated entity. In HQL, we join through the association attribute (g.artworks, a.artist), and Hibernate writes the on condition from the foreign key. HQL has three ways to join.

  • An implicit join is a path such as a.artist.country. Hibernate adds an inner join to the SQL for it.
  • An explicit join names the association with an alias in the from clause and keeps only rows that have a related entity.
  • A left join keeps every row of the left entity and fills the missing related entity with null.
select g.name, a.title from Gallery g join g.artworks a order by g.name, a.title
-- 5 rows: [Modern Wing, Blue River], [Modern Wing, Night Train], ..., [Sculpture Hall, Stone Bird]
-- SQL: from galleries g1_0 join artworks a1_0 on g1_0.id=a1_0.gallery_id

select g.name, a.title from Gallery g left join g.artworks a order by g.name, a.title
-- 6 rows: [East Room, null], [Modern Wing, Blue River], ..., [Sculpture Hall, Stone Bird]
-- SQL: from galleries g1_0 left join artworks a1_0 on g1_0.id=a1_0.gallery_id

The join type decides which rows stay in the result, and the entity in the from clause decides which side is kept. Starting from Gallery, the artwork in storage is in neither result.

Left: the galleries table with Modern Wing, Sculpture Hall and East Room, and the artworks table where Red Fields has a null gallery. Middle: join g.artworks a returns 5 rows without East Room. Right: left join g.artworks a returns 6 rows including East Room with a null title. A note says Red Fields is in neither result and needs from Artwork a left join a.gallery g
join drops East Room, left join keeps it with null. Red Fields needs a query that starts from Artwork.

An on clause adds a condition to the join itself. With a left join, the artist stays in the result even when no artwork matches the condition.

select ar.name, a.title from Artist ar
left join ar.artworks a on a.medium = :m          -- m = SCULPTURE
order by ar.name
-- [Emma, Iron Leaf], [Hugo, Stone Bird], [Lokesh, null], [Mia, null]
-- SQL: left join artworks a2_0 on a1_0.id=a2_0.artist_id and a2_0.medium=?

Moving a.medium = :m into where would remove Lokesh and Mia, because where filters after the join and null never equals SCULPTURE.

2.7. Loading Collections With join fetch

Artist.artworks is LAZY, so from Artist loads only the artists, and Hibernate reads each artist’s list from the database the first time our code uses it. For four artists, that is one query for the artists plus one query per artist, which is called the “N+1 select problem”.

The join fetch clause loads the association in the same SQL statement and fills the collections.

List<Artist> artists = em.createQuery(
    "select ar from Artist ar left join fetch ar.artworks order by ar.name", Artist.class)
    .getResultList();                     // 4 artists, artworks already loaded
// select a1_0.id,a2_0.artist_id,a2_0.id,...,a2_0.title,...,a1_0.name
//   from artists a1_0 left join artworks a2_0 on a1_0.id=a2_0.artist_id order by a1_0.name
Left panel: from Artist ar runs 5 statements, one select on artists and one select on artworks for each of Emma, Hugo, Lokesh and Mia. Right panel: select ar from Artist ar left join fetch ar.artworks runs 1 statement with a left join and returns the same 4 artists with their artworks
Without join fetch, reading the four lists costs four extra queries. With it, one statement loads everything.

We use left join fetch when entities without children must stay in the result, because Mia has no artworks and a plain join fetch would drop her.

2.8. Aggregate Functions, group by and having

HQL has the SQL aggregate functions count(), sum(), avg(), min() and max(). The group by clause computes them once per group, and having filters the groups after the aggregates are computed. For example, the front desk wants to know which galleries show more than one artwork and what their artworks are worth.

select g.name, count(a), sum(a.estimatedValue)
from Gallery g join g.artworks a
group by g.name
having count(a) > 1
order by g.name
-- [Modern Wing, 3, 2450.00], [Sculpture Hall, 2, 5200.00]
-- SQL: ... group by g1_0.name having count(a1_0.id)>1 order by g1_0.name

The function count() returns a Long, so the second value in each Object[] is 3L, not an Integer.

2.9. Subqueries and exists

A subquery is a query in parentheses inside another query, and it can return one value for a comparison. For example, a curator looks for the artworks worth more than the average value (1433.33).

select a.title from Artwork a
where a.estimatedValue > (select avg(a2.estimatedValue) from Artwork a2)
order by a.title
-- [Iron Leaf, Stone Bird]

The exists operator checks whether a subquery finds at least one row. Because the subquery refers to the outer alias ar, the database checks it for each artist, so not exists finds the artists without any artwork.

select ar.name from Artist ar
where exists (select 1 from Artwork a where a.artist = ar and a.medium = :m)   -- m = SCULPTURE
order by ar.name
-- [Emma, Hugo]
-- SQL: where exists(select 1 from artworks a2_0 where a2_0.artist_id=a1_0.id and a2_0.medium=?)

select ar.name from Artist ar
where not exists (select 1 from Artwork a where a.artist = ar)
-- [Mia]

2.10. case, coalesce and Other Functions

The case expression returns a value chosen by conditions, and coalesce() returns its first argument that is not null. For example, a printed visitor list shows each artwork with a price label and a location, and the artwork without a gallery gets the location “Storage”.

select a.title,
       case when a.estimatedValue >= 2000 then 'high' else 'normal' end,
       coalesce(g.name, 'Storage')
from Artwork a left join a.gallery g
order by a.title
-- [Blue River, normal, Modern Wing]
-- [Iron Leaf, high, Sculpture Hall]
-- ...
-- [Red Fields, normal, Storage]

HQL has the common string, numeric and date functions, and Hibernate translates each function for the database. For example, length() became character_length() on H2.

select upper(ar.name), ar.name || ' (' || ar.country || ')', length(ar.name)
from Artist ar where ar.country = 'France' order by ar.name
-- [EMMA, Emma (France), 4], [HUGO, Hugo (France), 4]
-- SQL: select upper(a1_0.name),(((a1_0.name||' (')||a1_0.country)||')'),character_length(a1_0.name) ...

Other functions include lower(), trim(), substring(), locate(), abs(), round(), mod(), current_date and extract(), and Hibernate translates every function in the HQL function list for the dialect in the same way.

2.11. update, delete and insert Statements

HQL can change many rows with one statement. The update and delete statements are also standard JPQL, whereas insert is HQL only. Hibernate’s createMutationQuery() runs all three, and executeUpdate() returns the number of rows changed.

Bulk statements must run inside a transaction, because without one, executeUpdate() throws a TransactionRequiredException.

int updated = em.unwrap(Session.class).createMutationQuery(
        "update Artwork a set a.estimatedValue = a.estimatedValue * 1.1 where a.medium = :m")
    .setParameter("m", Medium.PHOTO)
    .executeUpdate();                     // updated = 1
// update artworks a1_0 set estimatedValue=(a1_0.estimatedValue*1.1) where a1_0.medium=?

A bulk statement changes the rows in the database, not the entities already loaded in the persistence context. “Night Train” was loaded before the update and keeps its old value until we call em.refresh().

BigDecimal before = train.getEstimatedValue();   // 450.00 (loaded before the update)
em.refresh(train);
BigDecimal after = train.getEstimatedValue();    // 495.00

The delete statement removes the matching rows, and the JPA method em.createQuery(…).executeUpdate() works as well.

int deleted = em.createQuery("delete from Artwork a where a.gallery is null")
    .executeUpdate();                     // deleted = 1 (Red Fields)
// delete from artworks a1_0 where a1_0.gallery_id is null

To delete a single entity with its cascades, we load it and delete it with em.remove() instead.

HQL insert has two forms. The insert … values form adds rows from literal values, and Hibernate generates the id from the sequence.

int inserted = session.createMutationQuery(
        "insert into Artist (name, country) values ('Ravi', 'India'), ('Sara', 'Italy')")
    .executeUpdate();                     // inserted = 2
// insert into artists(name,country,id) values ('Ravi','India',?), ('Sara','Italy',?)

The insert … select form copies the result of a query into another entity. For example, the museum copies the expensive artworks into the Highlight entity for a guided tour.

int copied = session.createMutationQuery("""
    insert into Highlight (title, artistName)
    select a.title, a.artist.name from Artwork a where a.estimatedValue > 2000""")
    .executeUpdate();                     // copied = 2: [Iron Leaf by Emma, Stone Bird by Hugo]

Because each new Highlight needs an id from a sequence, Hibernate first copies the selected rows into a temporary table HTE_highlights and assigns the ids, and then runs insert into highlights(title,artistName,id) select ….

2.12. HQL Features in Hibernate 6 and 7

Hibernate 6 rewrote the HQL parser, which made many SQL features available in HQL. Each feature in the table runs on Hibernate 7.4.11 with H2.

FeatureHQLResult
Set operationsselect ar.name from Artist ar where ar.country = ‘France’ union select g.name from Gallery g where g.floor = 0Emma, Hugo, Sculpture Hall
select without fromselect 2 + 35 (Integer); SQL: select (2+3)
limit and offset… order by a.title limit 2 offset 2Night Train, Old Bridge
Records without select newselect a.title, a.artist.name with ArtworkSummary.classSee section 2.5
Multi-row insert … valuesinsert into Artist (name, country) values (…), (…)2 rows
Common table expressionwith expensive as (…) select …See the with example

A common table expression (CTE) gives a subquery a name with with, so the main query can read the CTE like an entity.

with expensive as (
  select a.title as title, a.estimatedValue as price
  from Artwork a where a.estimatedValue > 1000
)
select e.title from expensive e order by e.price desc
-- [Stone Bird, Iron Leaf, Blue River]
-- SQL on H2: select e1_0.title from (select a1_0.title,a1_0.estimatedValue from artworks a1_0
--            where a1_0.estimatedValue>1000) e1_0(title,price) order by e1_0.price desc

On H2, Hibernate rendered the CTE as a subquery in the from clause. Support for the Hibernate 6 and 7 features depends on the database dialect, so we test them on the database that we deploy to.

2.13. HQL Clause Cheat Sheet

Hibernate translates each HQL clause into the same SQL pattern every time, so we can match each statement in the SQL log to its HQL query. The table pairs each clause with the SQL that Hibernate 7.4 generated on H2.

ClauseHQLGenerated SQL
fromfrom Artwork afrom artworks a1_0
selectselect a.titleselect a1_0.title
wherewhere a.year > 2019where a1_0.created_year>2019
Parametera.medium = :mediuma1_0.medium=?
Implicit joina.artist.countryjoin artists a2_0 on a2_0.id=a1_0.artist_id
joinGallery g join g.artworks agalleries g1_0 join artworks a1_0 on g1_0.id=a1_0.gallery_id
left join … onleft join ar.artworks a on a.medium = :mleft join artworks a2_0 on a1_0.id=a2_0.artist_id and a2_0.medium=?
join fetchleft join fetch ar.artworksSame left join, with all artworks columns in the select
group by / havinggroup by g.name having count(a) > 1group by g1_0.name having count(a1_0.id)>1
existsexists (select 1 from Artwork a where a.artist = ar)exists(select 1 from artworks a2_0 where a2_0.artist_id=a1_0.id)
order byorder by a.titleorder by a1_0.title
Paginglimit 2 offset 2offset 2 rows fetch first 2 rows only
updateupdate Artwork a set a.estimatedValue = …update artworks a1_0 set estimatedValue=(…)
deletedelete from Artwork a where a.gallery is nulldelete from artworks a1_0 where a1_0.gallery_id is null
insertinsert into Artist (name, country) values (…)insert into artists(name,country,id) values (…,?)

3. HQL FAQs

3.1. Why Does HQL Throw “Could not resolve root entity”?

Because the query uses a name that is not an entity, most often the table name. HQL needs the entity name, which is the simple class name unless @Entity(name = …) sets another name.

java.lang.IllegalArgumentException: org.hibernate.query.sqm.UnknownEntityException: Could not resolve root entity 'artwork'

The query select a from Artwork a fixes the error. A column name, such as a.created_year, fails with Could not resolve attribute, because the attribute name is a.year. If the name is correct, we check that the class is registered as a managed class of the persistence unit.

3.2. What Replaced Query.list(), setString() and createSQLQuery()?

In Hibernate 7.4, Query.list() and uniqueResult() still exist, but the typed setters and the old native query method are removed. Old HQL tutorials use the Hibernate 3 to 5 API.

Old APIHibernate 6 and 7
session.createQuery(hql) without a typeem.createQuery(hql, Artwork.class) or session.createSelectionQuery(hql, Artwork.class)
query.setString(“name”, v), setInteger(), setDouble()query.setParameter(“name”, v)
query.list()getResultList() (list() still works)
query.uniqueResult()getSingleResult() or getSingleResultOrNull() (uniqueResult() still works)
session.createSQLQuery(sql)createNativeQuery(sql), see native SQL queries
session.createQuery(hql).executeUpdate()session.createMutationQuery(hql).executeUpdate(), which rejects a select
Session.save(entity)em.persist(entity)

We declare a query that we reuse in several places once, as a named query with @NamedQuery.

Because a path through an association is an inner join. “Red Fields” has no gallery, so sorting by a.gallery.name returns five titles instead of six.

select a.title from Artwork a order by a.gallery.name, a.title
-- [Blue River, Night Train, Old Bridge, Iron Leaf, Stone Bird]
-- SQL: from artworks a1_0 join galleries g1_0 on g1_0.id=a1_0.gallery_id ...

When the association can be null, we write an explicit left join and use its alias, as in the coalesce(g.name, ‘Storage’) query of section 2.10.

3.4. Do I Still Need distinct With join fetch?

No. In Hibernate 5, join fetch on a collection returned the parent once per child, so we added distinct to remove the duplicates.

In Hibernate 6 and 7, the result list holds each entity once. For example, select ar from Artist ar left join fetch ar.artworks returned 4 artists, even though the SQL produced 7 rows (Emma, Hugo and Lokesh twice each, Mia once).

3.5. Should I Use HQL, the Criteria API or Native SQL?

It depends on how the query is built and which database features it needs, because all three run through the same EntityManager.

OptionUse it when
HQL / JPQL stringThe query is known at compile time; it is the shortest to read
Criteria APIFilters are added at runtime, for example from optional search fields
Native SQLThe query needs a database feature HQL does not have

3.6. How Do I See the SQL Generated for an HQL Query?

We turn on SQL logging when building the factory, and showSql(showSql, formatSql, highlightSql) prints each statement to the console.

HibernatePersistenceConfiguration config = new HibernatePersistenceConfiguration("hql")
    .showSql(true, false, false);         // Hibernate: select a1_0.title from artworks a1_0 ...

As configuration properties, for example in persistence.xml, the same settings are hibernate.show_sql, hibernate.format_sql and hibernate.highlight_sql. The property hibernate.use_sql_comments adds the HQL text as a comment in front of each SQL statement.

4. Conclusion

HQL lets us query entities and attributes, and Hibernate translates each query into SQL for our database. Entity names become table names, and paths through associations become joins.

We write select queries with createQuery() or createSelectionQuery() and a result type, and we bind every value as a parameter. We choose join, left join or join fetch based on which rows and collections we need. For bulk update, delete and insert, we use createMutationQuery(), followed by refresh() for entities already loaded.

Hibernate 6 and 7 add limit, set operations, CTEs and records without select new. When the code must run on another JPA provider, strict JPQL compliance mode rejects extensions such as limit and with.

5. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. Thank you for this tutorial it was helpful.

    I have a question and maybe you have the right response,
    you wrote in chapter “What is HQL” the following:

    HQL queries are translated by Hibernate into conventional SQL queries. Note that Hibernate also provides the APIs that allow us to directly issue SQL queries as well.

    can you tell me how can I translate HQL to native SQL, please

    I have a project that uses hibernate version 3 and I am migrating to version 6.*

    in version 3 I used ASTQueryTranslatorFactory and QueryTranslator to do that but in version 6 i didn’t find a way

    Thank You

    • One way can be to enable debug logging and capture the SQL queries from logs. Then work with DBA to fine-tune if needed.

      Logging can be enabled by setting logger org.hibernate.SQL to DEBUG.

      <logger name="org.hibernate.SQL" level="DEBUG" />

  2. Hi Lokesh,
    In the Update syntax you have mentioned optional [FROM] . Could you please explain we should use from in update ?

    • Use of FROM is completely optional. It’ more of readability perspective. Even if use like below:

      Query query=session.createQuery(&quot;update FROM Employee set age=:age where name=:name&quot;);
      

      Above update statement is completely valid and will run similarly as:

      Query query=session.createQuery(&quot;update Employee set age=:age where name=:name&quot;);
      

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.