HQL: The Hibernate Query Language
Hibernate is equiped with an extremely powerful query language that (quite intentionally)
looks very much like SQL. But don't be fooled by the syntax; HQL is fully object-oriented,
understanding notions like inheritence, polymorphism and association.
Case Sensitivity
Queries are case-insensitive, except for names of Java classes and properties.
So SeLeCT is the same as
sELEct is the same as
SELECT but
org.hibernate.eg.FOO is not
org.hibernate.eg.Foo and
foo.barSet is not
foo.BARSET.
This manual uses lowercase HQL keywords. Some users find queries with uppercase keywords
more readable, but we find this convention ugly when embedded in Java code.
The from clause
The simplest possible Hibernate query is of the form:
which simply returns all instances of the class eg.Cat.
Most of the time, you will need to assign an alias, since
you will want to refer to the Cat in other parts of the
query.
This query assigns the alias cat to Cat
instances, so we could use that alias later in the query. The as
keyword is optional; we could also write:
Multiple classes may appear, resulting in a cartesian product or "cross" join.
It is considered good practice to name query aliases using an initial lowercase,
consistent with Java naming standards for local variables
(eg. domesticCat).
Associations and joins
We may also assign aliases to associated entities, or even to elements of a collection of
values, using a join.
The supported join types are borrowed from ANSI SQL
inner join
left outer join
right outer join
full join (not usually useful)
The inner join, left outer join and
right outer join constructs may be abbreviated.
In addition, a "fetch" join allows associations or collections of values to be
initialized along with their parent objects, using a single select. This is particularly
useful in the case of a collection. It effectively overrides the outer join and
lazy declarations of the mapping file for associations and collections. See
for more information.
A fetch join does not usually need to assign an alias, because the associated objects
should not be used in the where clause (or any other clause). Also,
the associated objects are not returned directly in the query results. Instead, they may
be accessed via the parent object.
Note that, in the current implementation, only one collection role may be fetched
in a query (everything else would be non-performant). Note also that the
fetch construct may not be used in queries called using
scroll() or iterate(). Finally, note that
full join fetch and right join fetch are not meaningful.
The select clause
The select clause picks which objects and properties to return in
the query result set. Consider:
The query will select mates of other Cats.
Actually, you may express this query more compactly as:
You may even select collection elements, using the special elements
function. The following query returns all kittens of any cat.
Queries may return properties of any value type including properties of component type:
Queries may return multiple objects and/or properties as an array of type
Object[]
or as an actual typesafe Java object
assuming that the class Family has an appropriate constructor.
Aggregate functions
HQL queries may even return the results of aggregate functions on properties:
Collections may also appear inside aggregate functions in the select
clause.
The supported aggregate functions are
avg(...), sum(...), min(...), max(...)
count(*)
count(...), count(distinct ...), count(all...)
The distinct and all keywords may be used and have
the same semantics as in SQL.
Polymorphic queries
A query like:
returns instances not only of Cat, but also of subclasses like
DomesticCat. Hibernate queries may name any Java
class or interface in the from clause. The query will return instances
of all persistent classes that extend that class or implement the interface. The following
query would return all persistent objects:
The interface Named might be implemented by various persistent
classes:
Note that these last two queries will require more than one SQL SELECT. This
means that the order by clause does not correctly order the whole result set.
(It also means you can't call these queries using Query.scroll().)
The where clause
The where clause allows you to narrow the list of instances returned.
returns instances of Cat named 'Fritz'.
will return all instances of Foo for which
there exists an instance of bar with a
date property equal to the
startDate property of the
Foo. Compound path expressions make the
where clause extremely powerful. Consider:
This query translates to an SQL query with a table (inner) join. If you were to write
something like
you would end up with a query that would require four table joins in SQL.
The = operator may be used to compare not only properties, but also
instances:
The special property (lowercase) id may be used to reference the
unique identifier of an object. (You may also use its property name.)
The second query is efficient. No table join is required!
Properties of composite identifiers may also be used. Suppose Person
has a composite identifier consisting of country and
medicareNumber.
Once again, the second query requires no table join.
Likewise, the special property class accesses the discriminator value
of an instance in the case of polymorphic persistence. A Java class name embedded in the
where clause will be translated to its discriminator value.
You may also specify properties of components or composite user types (and of components
of components, etc). Never try to use a path-expression that ends in a property of component
type (as opposed to a property of a component). For example, if store.owner
is an entity with a component address
An "any" type has the special properties id and class,
allowing us to express a join in the following way (where AuditLog.item
is a property mapped with <any>).
Notice that log.item.class and payment.class
would refer to the values of completely different database columns in the above query.
Expressions
Expressions allowed in the where clause include
most of the kind of things you could write in SQL:
mathematical operators +, -, *, /
binary comparison operators =, >=, <=, <>, !=, like
logical operations and, or, not
string concatenation ||
SQL scalar functions like upper() and
lower()
Parentheses ( ) indicate grouping
in,
between,
is null
JDBC IN parameters ?
named parameters :name, :start_date, :x1
SQL literals 'foo', 69, '1970-01-01 10:00:01.0'
Java public static final constants eg.Color.TABBY
in and between may be used as follows:
and the negated forms may be written
Likewise, is null and is not null may be used to test
for null values.
Booleans may be easily used in expressions by declaring HQL query substitutions in Hibernate
configuration:
true 1, false 0]]>
This will replace the keywords true and false with the
literals 1 and 0 in the translated SQL from this HQL:
You may test the size of a collection with the special property size, or
the special size() function.
0
from eg.Cat cat where size(cat.kittens) > 0]]>
For indexed collections, you may refer to the minimum and maximum indices using
minIndex and maxIndex. Similarly, you may refer to the
minimum and maximum elements of a collection of basic type using minElement
and maxElement.
current date]]>
There are also functional forms (which, unlike the constructs above, are not case sensitive):
100
from Order order where minelement(order.items) > 10000]]>
The SQL functions any, some, all, exists, in are supported when passed the element
or index set of a collection (elements and indices functions)
or the result of a subquery (see below).
all elements(p.scores)
from eg.Show show where 'fizard' in indices(show.acts)]]>
Note that these constructs - size, elements,
indices, minIndex, maxIndex,
minElement, maxElement - have certain usage
restrictions:
in a where clause: only for databases with subselects
in a select clause: only elements and
indices make sense
Elements of indexed collections (arrays, lists, maps) may be referred to by
index (in a where clause only):
The expression inside [] may even be an arithmetic expression.
HQL also provides the built-in index() function, for elements of
a one-to-many association or collection of values.
Scalar SQL functions supported by the underlying database may be used
If you are not yet convinced by all this, think how much longer and less readable the
following query would be in SQL:
Hint: something like
The order by clause
The list returned by a query may be ordered by any property of a returned class or components:
The optional asc or desc indicate ascending or descending order
respectively.
The group by clause
A query that returns aggregate values may be grouped by any property of a returned class or components:
Note: You may use the elements and indices constructs
inside a select clause, even on databases with no subselects.
A having clause is also allowed.
SQL functions and aggregate functions are allowed in the having
and order by clauses, if supported by the underlying database (ie.
not in MySQL).
100
order by count(kitten) asc, sum(kitten.weight) desc]]>
Note that neither the group by clause nor the
order by clause may contain arithmetic expressions.
Subqueries
For databases that support subselects, Hibernate supports subqueries within queries. A subquery must
be surrounded by parentheses (often by an SQL aggregate function call). Even correlated subqueries
(subqueries that refer to an alias in the outer query) are allowed.
(
select avg(cat.weight) from eg.DomesticCat cat
)
from eg.DomesticCat as cat
where cat.name = some (
select name.nickName from eg.Name as name
)
from eg.Cat as cat
where not exists (
from eg.Cat as mate where mate.mate = cat
)
from eg.DomesticCat as cat
where cat.name not in (
select name.nickName from eg.Name as name
)]]>
HQL examples
Hibernate queries can be quite powerful and complex. In fact, the power of the query language
is one of Hibernate's main selling points. Here are some example queries very similar to queries
that I used on a recent project. Note that most queries you will write are much simpler than these!
The following query returns the order id, number of items and total value of the order for all
unpaid orders for a particular customer and given minimum total value, ordering the results by
total value. In determining the prices, it uses the current catalog. The resulting SQL query,
against the ORDER, ORDER_LINE, PRODUCT,
CATALOG and PRICE tables has four inner joins and an
(uncorrelated) subselect.
= all (
select cat.effectiveDate
from Catalog as cat
where cat.effectiveDate < sysdate
)
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc]]>
What a monster! Actually, in real life, I'm not very keen on subqueries, so my query was
really more like this:
:minAmount
order by sum(price.amount) desc]]>
The next query counts the number of payments in each status, excluding all payments in the
AWAITING_APPROVAL status where the most recent status change was made by the
current user. It translates to an SQL query with two inner joins and a correlated subselect
against the PAYMENT, PAYMENT_STATUS and
PAYMENT_STATUS_CHANGE tables.
PaymentStatus.AWAITING_APPROVAL
or (
statusChange.timeStamp = (
select max(change.timeStamp)
from PaymentStatusChange change
where change.payment = payment
)
and statusChange.user <> :currentUser
)
group by status.name, status.sortOrder
order by status.sortOrder]]>
If I would have mapped the statusChanges collection as a list, instead of a set,
the query would have been much simpler to write.
PaymentStatus.AWAITING_APPROVAL
or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
group by status.name, status.sortOrder
order by status.sortOrder]]>
The next query uses the MS SQL Server isNull() function to return all
the accounts and unpaid payments for the organization to which the current user belongs.
It translates to an SQL query with three inner joins, an outer join and a subselect against
the ACCOUNT, PAYMENT, PAYMENT_STATUS,
ACCOUNT_TYPE, ORGANIZATION and
ORG_USER tables.
For some databases, we would need to do away with the (correlated) subselect.
Tips & Tricks
You can count the number of query results without actually returning them:
To order a result by the size of a collection, use the following query:
If your database supports subselects, you can place a condition upon selection
size in the where clause of your query:
= 1]]>
If your database doesn't support subselects, use the following query:
= 1]]>
As this solution can't return a User with zero messages
because of the inner join, the following form is also useful:
Properties of a JavaBean can be bound to named query parameters:
Collections are pageable by using the Query interface with a filter:
Collection elements may be ordered or grouped using a query filter:
You can find the size of a collection without initializing it: