Hibernate.orgCommunity Documentation
Table of Contents
FROM
clauseSELECT
clauseWHERE
clauseThe Hibernate Query Language (HQL) and Java Persistence Query Language (JPQL) are both object model focused query languages similar in nature to SQL. JPQL is a heavily-inspired-by subset of HQL. A JPQL query is always a valid HQL query, the reverse is not true however.
Both HQL and JPQL are non-type-safe ways to perform query operations. Criteria queries offer a type-safe approach to querying. See Chapter 15, Criteria for more information.
With the exception of names of Java classes and properties, queries are case-insensitive.
So SeLeCT
is the same as sELEct
is the same as
SELECT
, but
org.hibernate.eg.FOO
and org.hibernate.eg.Foo
are different, as are
foo.barSet
and foo.BARSET
.
This documentation uses lowercase keywords as convention in examples.
Both HQL and JPQL allow SELECT
, UPDATE
and DELETE
statements to be performed. HQL additionally allows INSERT
statements, in a form
similar to a SQL INSERT-SELECT
.
Care should be taken as to when a UPDATE
or DELETE
statement is
executed.
Caution should be used when executing bulk update or delete operations because they may result in inconsistencies between the database and the entities in the active persistence context. In general, bulk update and delete operations should only be performed within a transaction in a new persistence con- text or before fetching or accessing entities whose state might be affected by such operations. | ||
--Section 4.10 of the JPA 2.0 Specification |
The BNF for SELECT
statements in HQL is:
select_statement :: = [select_clause] from_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause]
The simplest possible HQL SELECT
statement is of the form:
from com.acme.Cat
The select statement in JPQL is exactly the same as for HQL except that JPQL requires a
select_clause
, whereas HQL does not. Even though HQL does not require the presence
of a select_clause
, it is generally good practice to include one. For simple queries
the intent is clear and so the intended result of the select_clause
is east to
infer. But on more complex queries that is not always the case. It is usually better to explicitly
specify intent. Hibernate does not actually enforce that a select_clause
be present
even when parsing JPQL queries, however applications interested in JPA portability should take heed of
this.
The BNF for UPDATE
statements is the same in HQL and JPQL:
update_statement ::= update_clause [where_clause] update_clause ::= UPDATE entity_name [[AS] identification_variable] SET update_item {, update_item}* update_item ::= [identification_variable.]{state_field | single_valued_object_field} = new_value new_value ::= scalar_expression | simple_entity_expression | NULL
UPDATE
statements, by default, do not effect the version
or the timestamp
attribute values for the affected entities. However,
you can force Hibernate to set the version
or timestamp
attribute
values through the use of a versioned update
. This is achieved by adding the
VERSIONED
keyword after the UPDATE
keyword. Note, however, that
this is a Hibernate specific feature and will not work in a portable manner. Custom version types,
org.hibernate.usertype.UserVersionType
, are not allowed in conjunction
with a update versioned
statement.
An UPDATE
statement is executed using the executeUpdate
of either org.hibernate.Query
or
javax.persistence.Query
. The method is named for those familiar with
the JDBC executeUpdate
on java.sql.PreparedStatement
.
The int
value returned by the executeUpdate()
method
indicates the number of entities effected by the operation. This may or may not correlate to the number
of rows effected in the database. An HQL bulk operation might result in multiple actual SQL statements
being executed (for joined-subclass, for example). The returned number indicates the number of actual
entities affected by the statement. Using a JOINED inheritance hierarchy, a delete against one of the
subclasses may actually result in deletes against not just the table to which that subclass is mapped,
but also the "root" table and tables “in between”
Example 14.1. Example UPDATE query statements
String hqlUpdate = "update Customer c " + "set c.name = :newName " + "where c.name = :oldName"; int updatedEntities = session.createQuery( hqlUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate();
String jpqlUpdate = "update Customer c " + "set c.name = :newName " + "where c.name = :oldName"; int updatedEntities = entityManager.createQuery( jpqlUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate();
String hqlVersionedUpdate = "update versioned Customer c " + "set c.name = :newName " + "where c.name = :oldName"; int updatedEntities = s.createQuery( hqlUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate();
Neither UPDATE
nor DELETE
statements are allowed to
result in what is called an implicit join. Their form already disallows explicit joins.
The BNF for DELETE
statements is the same in HQL and JPQL:
delete_statement ::= delete_clause [where_clause] delete_clause ::= DELETE FROM entity_name [[AS] identification_variable]
A DELETE
statement is also executed using the executeUpdate
method of either org.hibernate.Query
or
javax.persistence.Query
.
HQL adds the ability to define INSERT
statements as well. There is no JPQL
equivalent to this. The BNF for an HQL INSERT
statement is:
insert_statement ::= insert_clause select_statement insert_clause ::= INSERT INTO entity_name (attribute_list) attribute_list ::= state_field[, state_field ]*
The attribute_list
is analogous to the column specification
in the
SQL INSERT
statement. For entities involved in mapped inheritance, only attributes
directly defined on the named entity can be used in the attribute_list
. Superclass
properties are not allowed and subclass properties do not make sense. In other words,
INSERT
statements are inherently non-polymorphic.
select_statement
can be any valid HQL select query, with the caveat that the return
types must match the types expected by the insert. Currently, this is checked during query
compilation rather than allowing the check to relegate to the database. This may cause problems
between Hibernate Types which are equivalent as opposed to
equal. For example, this might cause lead to issues with mismatches between an
attribute mapped as a org.hibernate.type.DateType
and an attribute defined as
a org.hibernate.type.TimestampType
, even though the database might not make a
distinction or might be able to handle the conversion.
For the id attribute, the insert statement gives you two options. You can either explicitly specify
the id property in the attribute_list
, in which case its value is taken from the
corresponding select expression, or omit it from the attribute_list
in which case a
generated value is used. This latter option is only available when using id generators that operate
“in the database”; attempting to use this option with any “in memory” type
generators will cause an exception during parsing.
For optimistic locking attributes, the insert statement again gives you two options. You can either
specify the attribute in the attribute_list
in which case its value is taken from
the corresponding select expressions, or omit it from the attribute_list
in which
case the seed value
defined by the corresponding
org.hibernate.type.VersionType
is used.
Example 14.2. Example INSERT query statements
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ..."; int createdEntities = s.createQuery( hqlInsert ).executeUpdate();
The FROM
clause is responsible defining the scope of object model types available to
the rest of the query. It also is responsible for defining all the “identification variables”
available to the rest of the query.
Identification variables are often referred to as aliases. References to object model classes in the FROM clause can be associated with an identification variable that can then be used to refer to that type thoughout the rest of the query.
In most cases declaring an identification variable is optional, though it is usually good practice to declare them.
An identification variable must follow the rules for Java identifier validity.
According to JPQL, identification variables must be treated as case insensitive. Good practice says you should use the same case throughout a query to refer to a given identification variable. In other words, JPQL says they can be case insensitive and so Hibernate must be able to treat them as such, but this does not make it good practice.
A root entity reference, or what JPA calls a range variable declaration
, is
specifically a reference to a mapped entity type from the application. It cannot name component/
embeddable types. And associations, including collections, are handled in a different manner
discussed later.
The BNF for a root entity reference is:
root_entity_reference ::= entity_name [AS] identification_variable
We see that the query is defining a root entity reference to the com.acme.Cat
object model type. Additionally, it declares an alias of c
to that
com.acme.Cat
reference; this is the identification variable.
Usually the root entity reference just names the entity name
rather than the
entity class FQN. By default the entity name is the unqualified entity class name,
here Cat
Multiple root entity references can also be specified. Even naming the same entity!
Example 14.5. Simple query using multiple root entity references
// build a product between customers and active mailing campaigns so we can spam! select distinct cust, camp from Customer cust, Campaign camp where camp.type = 'mail' and current_timestamp() between camp.activeRange.start and camp.activeRange.end
// retrieve all customers with headquarters in the same state as Acme's headquarters select distinct c1 from Customer c1, Customer c2 where c1.address.state = c2.address.state and c2.name = 'Acme'
The FROM
clause can also contain explicit relationship joins using the
join
keyword. These joins can be either inner
or left outer
style joins.
Example 14.6. Explicit inner join examples
select c from Customer c join c.chiefExecutive ceo where ceo.age < 25 // same query but specifying join type as 'inner' explicitly select c from Customer c inner join c.chiefExecutive ceo where ceo.age < 25
Example 14.7. Explicit left (outer) join examples
// get customers who have orders worth more than $5000 // or who are in "preferred" status select distinct c from Customer c left join c.orders o where o.value > 5000.00 or c.status = 'preferred' // functionally the same query but using the // 'left outer' phrase select distinct c from Customer c left outer join c.orders o where o.value > 5000.00 or c.status = 'preferred'
An important use case for explicit joins is to define FETCH JOINS
which override
the laziness of the joined association. As an example, given an entity named Customer
with a collection-valued association named orders
As you can see from the example, a fetch join is specified by injecting the keyword fetch
after the keyword join
. In the example, we used a left outer join because we want
to return customers who have no orders also. Inner joins can also be fetched. But inner joins still
filter. In the example, using an inner join instead would have resulted in customers without any orders
being filtered out of the result.
Fetch joins are not valid in sub-queries.
Care should be taken when fetch joining a collection-valued association which is in any way further restricted; the fetched collection will be restricted too! For this reason it is usually considered best practice to not assign an identification variable to fetched joins except for the purpose of specifying nested fetch joins.
Fetch joins should not be used in paged queries (aka, setFirstResult
/
setMaxResults
). Nor should they be used with the HQL
scroll
or iterate
features.
HQL also defines a WITH
clause to qualify the join conditions. Again, this is
specific to HQL; JPQL does not define this feature.
Example 14.9. with-clause join example
select distinct c from Customer c left join c.orders o with o.value > 5000.00
The important distinction is that in the generated SQL the conditions of the
with clause
are made part of the on clause
in the generated SQL
as opposed to the other queries in this section where the HQL/JPQL conditions are made part of the
where clause
in the generated SQL. The distinction in this specific example is
probably not that significant. The with clause
is sometimes necessary in more
complicated queries.
Explicit joins may reference association or component/embedded attributes. For further information about collection-valued association references, see Section 14.3.5, “Collection member references”. In the case of component/embedded attributes, the join is simply logical and does not correlate to a physical (SQL) join.
Another means of adding to the scope of object model types available to the query is through the use of implicit joins, or path expressions.
Example 14.10. Simple implicit join example
select c from Customer c where c.chiefExecutive.age < 25 // same as select c from Customer c inner join c.chiefExecutive ceo where ceo.age < 25
An implicit join always starts from an identification variable
, followed by
the navigation operator (.), followed by an attribute for the object model type referenced by the
initial identification variable
. In the example, the initial
identification variable
is c
which refers to the
Customer
entity. The c.chiefExecutive
reference then refers
to the chiefExecutive
attribute of the Customer
entity.
chiefExecutive
is an association type so we further navigate to its
age
attribute.
If the attribute represents an entity association (non-collection) or a component/embedded, that reference can be further navigated. Basic values and collection-valued associations cannot be further navigated.
As shown in the example, implicit joins can appear outside the FROM clause
. However,
they affect the FROM clause
. Implicit joins are always treated as inner joins.
Multiple references to the same implicit join always refer to the same logical and physical (SQL) join.
Example 14.11. Reused implicit join
select c from Customer c where c.chiefExecutive.age < 25 and c.chiefExecutive.address.state = 'TX' // same as select c from Customer c inner join c.chiefExecutive ceo where ceo.age < 25 and ceo.address.state = 'TX' // same as select c from Customer c inner join c.chiefExecutive ceo inner join ceo.address a where ceo.age < 25 and a.state = 'TX'
Just as with explicit joins, implicit joins may reference association or component/embedded attributes. For further information about collection-valued association references, see Section 14.3.5, “Collection member references”. In the case of component/embedded attributes, the join is simply logical and does not correlate to a physical (SQL) join. Unlike explicit joins, however, implicit joins may also reference basic state fields as long as the path expression ends there.
References to collection-valued associations actually refer to the values of that collection.
Example 14.12. Collection references example
select c from Customer c join c.orders o join o.lineItems l join l.product p where o.status = 'pending' and p.status = 'backorder' // alternate syntax select c from Customer c, in(c.orders) o, in(o.lineItems) l join l.product p where o.status = 'pending' and p.status = 'backorder'
In the example, the identification variable o
actually refers to the object model
type Order
which is the type of the elements of the
Customer#orders
association.
The example also shows the alternate syntax for specifying collection association joins using the
IN
syntax. Both forms are equivalent. Which form an application chooses to use is
simply a matter of taste.
We said earlier that collection-valued associations actually refer to the values of that collection. Based on the type of collection, there are also available a set of explicit qualification expressions.
Example 14.13. Qualified collection references example
// Product.images is a Map<String,String> : key = a name, value = file path // select all the image file paths (the map value) for Product#123 select i from Product p join p.images i where p.id = 123 // same as above select value(i) from Product p join p.images i where p.id = 123 // select all the image names (the map key) for Product#123 select key(i) from Product p join p.images i where p.id = 123 // select all the image names and file paths (the 'Map.Entry') for Product#123 select entry(i) from Product p join p.images i where p.id = 123 // total the value of the initial line items for all orders for a customer select sum( li.amount ) from Customer c join c.orders o join o.lineItems li where c.id = 123 and index(li) = 1
Refers to the collection value. Same as not specifying a qualifier. Useful to explicitly show intent. Valid for any type of collection-valued reference.
According to HQL rules, this is valid for both Maps and Lists which specify a
javax.persistence.OrderColumn
annotation to refer to
the Map key or the List position (aka the OrderColumn value). JPQL however, reserves
this for use in the List case and adds KEY
for the MAP case.
Applications interested in JPA provider portability should be aware of this
distinction.
Valid only for Maps. Refers to the map's key. If the key is itself an entity, can be further navigated.
Only valid only for Maps. Refers to the Map's logical
java.util.Map.Entry
tuple (the combination of its key
and value). ENTRY
is only valid as a terminal path and only valid
in the select clause.
See Section 14.4.9, “Collection-related expressions” for additional details on collection related expressions.
HQL and JPQL queries are inherently polymorphic.
select p from Payment p
This query names the Payment
entity explicitly. However, all subclasses of
Payment
are also available to the query. So if the
CreditCardPayment
entity and WireTransferPayment
entity
each extend from Payment
all three types would be available to the query. And
the query would return instances of all three.
The HQL query from java.lang.Object
is totally valid! It returns every
object of every type defined in your application.
This can be altered by using either the
org.hibernate.annotations.Polymorphism
annotation (global, and
Hibernate-specific) or limiting them using in the query itself using an entity type expression.
Essentially expressions are references that resolve to basic or tuple values.
Again, see Section 14.3, “The FROM
clause”.
String literals are enclosed in single-quotes. To escape a single-quote within a string literal, use double single-quotes.
Example 14.14. String literal examples
select c from Customer c where c.name = 'Acme' select c from Customer c where c.name = 'Acme''s Pretzel Logic'
Numeric literals are allowed in a few different forms.
Example 14.15. Numeric literal examples
// simple integer literal select o from Order o where o.referenceNumber = 123 // simple integer literal, typed as a long select o from Order o where o.referenceNumber = 123L // decimal notation select o from Order o where o.total > 5000.00 // decimal notation, typed as a float select o from Order o where o.total > 5000.00F // scientific notation select o from Order o where o.total > 5e+3 // scientific notation, typed as a float select o from Order o where o.total > 5e+3F
In the scientific notation form, the E
is case insensitive.
Specific typing can be achieved through the use of the same suffix approach specified by Java. So,
L
denotes a long; D
denotes a double; F
denotes a float. The actual suffix is case insensitive.
The boolean literals are TRUE
and FALSE
, again case-insensitive.
Enums can even be referenced as literals. The fully-qualified enum class name must be used. HQL can also handle constants in the same manner, though JPQL does not define that as supported.
Entity names can also be used as literal. See Section 14.4.10, “Entity type”.
Date/time literals can be specified using the JDBC escape syntax: {d 'yyyy-mm-dd'}
for dates, {t 'hh:mm:ss'}
for times and
{ts 'yyyy-mm-dd hh:mm:ss[.millis]'}
(millis optional) for timestamps. These
literals only work if you JDBC drivers supports them.
HQL supports all 3 of the following forms. JPQL does not support the HQL-specific positional parameters notion. It is good practice to not mix forms in a given query.
Named parameters are declared using a colon followed by an identifier -
:aNamedParameter
. The same named parameter can appear multiple times in a query.
Example 14.16. Named parameter examples
String queryString = "select c " + "from Customer c " + "where c.name = :name " + " or c.nickName = :name"; // HQL List customers = session.createQuery( queryString ) .setParameter( "name", theNameOfInterest ) .list(); // JPQL List<Customer> customers = entityManager.createQuery( queryString, Customer.class ) .setParameter( "name", theNameOfInterest ) .getResultList();
JPQL-style positional parameters are declared using a question mark followed by an ordinal -
?1
, ?2
. The ordinals start with 1. Just like with
named parameters, positional parameters can also appear multiple times in a query.
Example 14.17. Positional (JPQL) parameter examples
String queryString = "select c " + "from Customer c " + "where c.name = ?1 " + " or c.nickName = ?1"; // HQL - as you can see, handled just like named parameters // in terms of API List customers = session.createQuery( queryString ) .setParameter( "1", theNameOfInterest ) .list(); // JPQL List<Customer> customers = entityManager.createQuery( queryString, Customer.class ) .setParameter( 1, theNameOfInterest ) .getResultList();
HQL-style positional parameters follow JDBC positional parameter syntax. They are declared using
?
without a following ordinal. There is no way to relate two such
positional parameters as being "the same" aside from binding the same value to each.
This form should be considered deprecated and may be removed in the near future.
Arithmetic operations also represent valid expressions.
Example 14.18. Numeric arithmetic examples
select year( current_date() ) - year( c.dateOfBirth ) from Customer c select c from Customer c where year( current_date() ) - year( c.dateOfBirth ) < 30 select o.customer, o.total + ( o.total * :salesTax ) from Order o
The following rules apply to the result of arithmetic operations:
If either of the operands is Double/double, the result is a Double;
else, if either of the operands is Float/float, the result is a Float;
else, if either operand is BigDecimal, the result is BigDecimal;
else, if either operand is BigInteger, the result is BigInteger (except for division, in which case the result type is not further defined);
else, if either operand is Long/long, the result is Long (except for division, in which case the result type is not further defined);
else, (the assumption being that both operands are of integral type) the result is Integer (except for division, in which case the result type is not further defined);
Date arithmetic is also supported, albeit in a more limited fashion. This is due partially to
differences in database support and partially to the lack of support for INTERVAL
definition in the query language itself.
HQL defines a concatenation operator in addition to supporting the concatenation
(CONCAT
) function. This is not defined by JPQL, so portable applications
should avoid it use. The concatenation operator is taken from the SQL concatenation operator -
||
.
Example 14.19. Concatenation operation example
select 'Mr. ' || c.name.first || ' ' || c.name.last from Customer c where c.gender = Gender.MALE
See Section 14.4.8, “Scalar functions” for details on the concat()
function
Aggregate functions are also valid expressions in HQL and JPQL. The semantic is the same as their SQL counterpart. The supported aggregate functions are:
COUNT
(including distinct/all qualifiers) - The result type is always Long.
AVG
- The result type is always Double.
MIN
- The result type is the same as the argument type.
MAX
- The result type is the same as the argument type.
SUM
- The result type of the avg()
function depends on
the type of the values being averaged. For integral values (other than BigInteger), the result
type is Long. For floating point values (other than BigDecimal) the result type is Double. For
BigInteger values, the result type is BigInteger. For BigDecimal values, the result type is
BigDecimal.
Example 14.20. Aggregate function examples
select count(*), sum( o.total ), avg( o.total ), min( o.total ), max( o.total ) from Order o select count( distinct c.name ) from Customer c select c.id, c.name, sum( o.total ) from Customer c left join c.orders o group by c.id, c.name
Aggregations often appear with grouping. For information on grouping see Section 14.8, “Grouping”
Both HQL and JPQL define some standard functions that are available regardless of the underlying database in use. HQL can also understand additional functions defined by the Dialect as well as the application.
Here are the list of functions defined as supported by JPQL. Applications interested in remaining portable between JPA providers should stick to these functions.
String concatenation function. Variable argument length of 2 or more string values to be concatenated together.
Extracts a portion of a string value.
substring( string_expression, numeric_expression [, numeric_expression] )
The second argument denotes the starting position. The third (optional) argument denotes the length.
Upper cases the specified string
Lower cases the specified string
Follows the semantics of the SQL trim function.
Returns the length of a string.
Locates a string within another string.
locate( string_expression, string_expression[, numeric_expression] )
The third argument (optional) is used to denote a position from which to start looking.
Calculates the mathematical absolute value of a numeric value.
Calculates the remainder of dividing the first argument by the second.
Calculates the mathematical square root of a numeric value.
Returns the database current date.
Returns the database current time.
Returns the database current timestamp.
Beyond the JPQL standardized functions, HQL makes some additional functions available regardless of the underlying database in use.
Returns the length of binary data.
Performs a SQL cast. The cast target should name the Hibernate mapping type to use. See the chapter on data types for more information.
Performs a SQL extraction on datetime values. An extraction extracts parts of the datetime (the year, for example). See the abbreviated forms below.
Abbreviated extract form for extracting the second.
Abbreviated extract form for extracting the minute.
Abbreviated extract form for extracting the hour.
Abbreviated extract form for extracting the day.
Abbreviated extract form for extracting the month.
Abbreviated extract form for extracting the year.
Abbreviated form for casting a value as character data.
Hibernate Dialects can register additional functions known to be available for that particular database product. These functions are also available in HQL (and JPQL, though only when using Hibernate as the JPA provider obviously). However, they would only be available when using that database/Dialect. Applications that aim for database portability should avoid using functions in this category.
Application developers can also supply their own set of functions. This would usually represent
either custom SQL functions or aliases for snippets of SQL. Such function declarations are
made by using the addSqlFunction
method of
org.hibernate.cfg.Configuration
There are a few specialized expressions for working with collection-valued associations. Generally these are just abbreviated forms or other expressions for the sake of conciseness.
Calculate the size of a collection. Equates to a subquery!
Available for use on collections of basic type. Refers to the maximum value as determined
by applying the max
SQL aggregation.
Available for use on indexed collections. Refers to the maximum index (key/position) as
determined by applying the max
SQL aggregation.
Available for use on collections of basic type. Refers to the minimum value as determined
by applying the min
SQL aggregation.
Available for use on indexed collections. Refers to the minimum index (key/position) as
determined by applying the min
SQL aggregation.
Used to refer to the elements of a collection as a whole. Only allowed in the where clause.
Often used in conjunction with ALL
, ANY
or
SOME
restrictions.
Similar to elements
except that indices
refers to
the collections indices (keys/positions) as a whole.
Example 14.21. Collection-related expressions examples
select cal from Calendar cal where maxelement(cal.holidays) > current_date() select o from Order o where maxindex(o.items) > 100 select o from Order o where minelement(o.items) > 10000 select m from Cat as m, Cat as kit where kit in elements(m.kittens) // the above query can be re-written in jpql standard way: select m from Cat as m, Cat as kit where kit member of m.kittens select p from NameList l, Person p where p.name = some elements(l.names) select cat from Cat cat where exists elements(cat.kittens) select p from Player p where 3 > all elements(p.scores) select show from Show show where 'fizard' in indices(show.acts)
Elements of indexed collections (arrays, lists, and maps) can be referred to by index operator.
Example 14.22. Index operator examples
select o from Order o where o.items[0].id = 1234 select p from Person p, Calendar c where c.holidays['national day'] = p.birthDay and p.nationality.calendar = c select i from Item i, Order o where o.items[ o.deliveredItemIndices[0] ] = i and o.id = 11 select i from Item i, Order o where o.items[ maxindex(o.items) ] = i and o.id = 11 select i from Item i, Order o where o.items[ size(o.items) - 1 ] = i
See also Section 14.3.5.1, “Special case - qualified path expressions” as there is a good deal of overlap.
We can also refer to the type of an entity as an expression. This is mainly useful when dealing
with entity inheritance hierarchies. The type can expressed using a TYPE
function
used to refer to the type of an identification variable representing an entity. The name of the
entity also serves as a way to refer to an entity type. Additionally the entity type can be
parametrized, in which case the entity's Java Class reference would be bound as the parameter
value.
Example 14.23. Entity type expression examples
select p from Payment p where type(p) = CreditCardPayment select p from Payment p where type(p) = :aType
HQL also has a legacy form of referring to an entity type, though that legacy form is considered
deprecated in favor of TYPE
. The legacy form would have used p.class
in the examples rather than type(p)
. It is mentioned only for completeness.
Both the simple and searched forms are supported, as well as the 2 SQL defined abbreviated forms
(NULLIF
and COALESCE
)
The simple form has the following syntax:
CASE {operand} WHEN {test_value} THEN {match_result} ELSE {miss_result} END
Example 14.24. Simple case expression example
select case c.nickName when null then '<no nick name>' else c.nickName end from Customer c // This NULL checking is such a common case that most dbs // define an abbreviated CASE form. For example: select nvl( c.nickName, '<no nick name>' ) from Customer c // or: select isnull( c.nickName, '<no nick name>' ) from Customer c // the standard coalesce abbreviated form can be used // to achieve the same result: select coalesce( c.nickName, '<no nick name>' ) from Customer c
The searched form has the following syntax:
CASE [ WHEN {test_conditional} THEN {match_result} ]* ELSE {miss_result} END
Example 14.25. Searched case expression example
select case when c.name.first is not null then c.name.first when c.nickName is not null then c.nickName else '<no first name>' end from Customer c // Again, the abbreviated form coalesce can handle this a // little more succinctly select coalesce( c.name.first, c.nickName, '<no first name>' ) from Customer c
NULLIF is an abbreviated CASE expression that returns NULL if its operands are considered equal.
Example 14.26. NULLIF example
// return customers who have changed their last name select nullif( c.previousName.last, c.name.last ) from Customer c // equivalent CASE expression select case when c.previousName.last = c.name.last then null else c.previousName.last end from Customer c
The SELECT
clause identifies which objects and values to return as the query results.
The expressions discussed in Section 14.4, “Expressions” are all valid select expressions, except
where otherwise noted. See the section Section 14.10, “Query API” for information on handling the results
depending on the types of values specified in the SELECT
clause.
There is a particular expression type that is only valid in the select clause. Hibernate calls this “dynamic instantiation”. JPQL supports some of that feature and calls it a “constructor expression”
Example 14.27. Dynamic instantiation example - constructor
select new Family( mother, mate, offspr ) from DomesticCat as mother join mother.mate as mate left join mother.kittens as offspr
So rather than dealing with the Object[] (again, see Section 14.10, “Query API”) here we are wrapping the values in a type-safe java object that will be returned as the results of the query. The class reference must be fully qualified and it must have a matching constructor.
The class here need not be mapped. If it does represent an entity, the resulting instances are returned in the NEW state (not managed!).
That is the part JPQL supports as well. HQL supports additional “dynamic instantiation” features. First, the query can specify to return a List rather than an Object[] for scalar results:
Example 14.28. Dynamic instantiation example - list
select new list(mother, offspr, mate.name) from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr
The results from this query will be a List<List> as opposed to a List<Object[]>
HQL also supports wrapping the scalar results in a Map.
Example 14.29. Dynamic instantiation example - map
select new map( mother as mother, offspr as offspr, mate as mate ) from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr select new map( max(c.bodyWeight) as max, min(c.bodyWeight) as min, count(*) as n ) from Cat c
The results from this query will be a List<Map<String,Object>> as opposed to a List<Object[]>. The keys of the map are defined by the aliases given to the select expressions.
Predicates form the basis of the where clause, the having clause and searched case expressions.
They are expressions which resolve to a truth value, generally TRUE
or
FALSE
, although boolean comparisons involving NULLs generally resolve to
UNKNOWN
.
Comparisons involve one of the comparison operators - =, >, >=, <, <=, <>]>. HQL also defines <![CDATA[!= as a comparison operator synonymous with <>. The operands should be of the same type.
Example 14.30. Relational comparison examples
// numeric comparison select c from Customer c where c.chiefExecutive.age < 30 // string comparison select c from Customer c where c.name = 'Acme' // datetime comparison select c from Customer c where c.inceptionDate < {d '2000-01-01'} // enum comparison select c from Customer c where c.chiefExecutive.gender = com.acme.Gender.MALE // boolean comparison select c from Customer c where c.sendEmail = true // entity type comparison select p from Payment p where type(p) = WireTransferPayment // entity value comparison select c from Customer c where c.chiefExecutive = c.chiefTechnologist
Comparisons can also involve subquery qualifiers - ALL
, ANY
,
SOME
. SOME and ANY are synonymous.
The ALL qualifier resolves to true if the comparison is true for all of the values in the result of the subquery. It resolves to false if the subquery result is empty.
Example 14.31. ALL subquery comparison qualifier example
// select all players that scored at least 3 points // in every game. select p from Player p where 3 > all ( select spg.points from StatsPerGame spg where spg.player = p )
The ANY/SOME qualifier resolves to true if the comparison is true for some of (at least one of) the values in the result of the subquery. It resolves to false if the subquery result is empty.
Check a value for nullness. Can be applied to basic attribute references, entity references and parameters. HQL additionally allows it to be applied to component/embeddable types.
Example 14.32. Nullness checking examples
// select everyone with an associated address select p from Person p where p.address is not null // select everyone without an associated address select p from Person p where p.address is null
Performs a like comparison on string values. The syntax is:
like_expression ::= string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]
The semantics follow that of the SQL like expression. The pattern_value
is the
pattern to attempt to match in the string_expression
. Just like SQL,
pattern_value
can use “_” and “%” as wildcards. The
meanings are the same. “_” matches any single character. “%” matches
any number of characters.
The optional escape_character
is used to specify an escape character used to
escape the special meaning of “_” and “%” in the
pattern_value
. THis is useful when needing to search on patterns including either
“_” or “%”
Example 14.33. Like predicate examples
select p from Person p where p.name like '%Schmidt' select p from Person p where p.name not like 'Jingleheimmer%' // find any with name starting with "sp_" select sp from StoredProcedureMetadata sp where sp.name like 'sp|_%' escape '|'
Analogous to the SQL between expression. Perform a evaluation that a value is within the range of 2 other values. All the operands should have comparable types.
Example 14.34. Between predicate examples
select p from Customer c join c.paymentHistory p where c.id = 123 and index(p) between 0 and 9 select c from Customer c where c.president.dateOfBirth between {d '1945-01-01'} and {d '1965-01-01'} select o from Order o where o.total between 500 and 5000 select p from Person p where p.name between 'A' and 'E'
IN
predicates performs a check that a particular value is in a list of values.
Its syntax is:
in_expression ::= single_valued_expression [NOT] IN single_valued_list single_valued_list ::= constructor_expression | (subquery) | collection_valued_input_parameter constructor_expression ::= (expression[, expression]*)
The types of the single_valued_expression
and the individual values in the
single_valued_list
must be consistent. JPQL limits the valid types here
to string, numeric, date, time, timestamp, and enum types. In JPQL,
single_valued_expression
can only refer to:
“state fields”, which is its term for simple attributes. Specifically this excludes association and component/embedded attributes.
entity type expressions. See Section 14.4.10, “Entity type”
In HQL, single_valued_expression
can refer to a far more broad set of expression
types. Single-valued association are allowed. So are component/embedded attributes, although that
feature depends on the level of support for tuple or “row value constructor syntax” in
the underlying database. Additionally, HQL does not limit the value type in any way, though
application developers should be aware that different types may incur limited support based on
the underlying database vendor. This is largely the reason for the JPQL limitations.
The list of values can come from a number of different sources. In the
constructor_expression
and collection_valued_input_parameter
, the
list of values must not be empty; it must contain at least one value.
Example 14.35. In predicate examples
select p from Payment p where type(p) in (CreditCardPayment, WireTransferPayment) select c from Customer c where c.hqAddress.state in ('TX', 'OK', 'LA', 'NM') select c from Customer c where c.hqAddress.state in ? select c from Customer c where c.hqAddress.state in ( select dm.state from DeliveryMetadata dm where dm.salesTax is not null ) // Not JPQL compliant! select c from Customer c where c.name in ( ('John','Doe'), ('Jane','Doe') ) // Not JPQL compliant! select c from Customer c where c.chiefExecutive in ( select p from Person p where ... )
Exists expressions test the existence of results from a subquery. The affirmative form returns true if the subquery result contains values. The negated form returns true if the subquery result is empty.
The IS [NOT] EMPTY
expression applies to collection-valued path expressions. It
checks whether the particular collection has any associated values.
Example 14.36. Empty collection expression examples
select o from Order o where o.lineItems is empty select c from Customer c where c.pastDueBills is not empty
The [NOT] MEMBER [OF]
expression applies to collection-valued path expressions. It
checks whether a value is a member of the specified collection.
Example 14.37. Member-of collection expression examples
select p from Person p where 'John' member of p.nickNames select p from Person p where p.name.first = 'Joseph' and 'Joey' not member of p.nickNames
The NOT
operator is used to negate the predicate that follows it. If that
following predicate is true, the NOT resolves to false. If the predicate is true, NOT resolves to
false. If the predicate is unknown, the NOT resolves to unknown as well.
The AND
operator is used to combine 2 predicate expressions. The result of the
AND expression is true if and only if both predicates resolve to true. If either predicate resolves
to unknown, the AND expression resolves to unknown as well. Otherwise, the result is false.
The WHERE
clause of a query is made up of predicates which assert whether values in
each potential row match the predicated checks. Thus, the where clause restricts the results returned
from a select query and limits the scope of update and delete queries.
The GROUP BY
clause allows building aggregated results for various value groups. As an
example, consider the following queries:
Example 14.38. Group-by illustration
// retrieve the total for all orders select sum( o.total ) from Order o // retrieve the total of all orders // *grouped by* customer select c.id, sum( o.total ) from Order o inner join o.customer c group by c.id
The first query retrieves the complete total of all orders. The second retrieves the total for each customer; grouped by each customer.
In a grouped query, the where clause applies to the non aggregated values (essentially it determines whether
rows will make it into the aggregation). The HAVING
clause also restricts results,
but it operates on the aggregated values. In the Example 14.38, “Group-by illustration” example,
we retrieved order totals for all customers. If that ended up being too much data to deal with,
we might want to restrict the results to focus only on customers with a summed order total of more than
$10,000.00:
Example 14.39. Having illustration
select c.id, sum( o.total ) from Order o inner join o.customer c group by c.id having sum( o.total ) > 10000.00
The HAVING clause follows the same rules as the WHERE clause and is also made up of predicates. HAVING is applied after the groupings and aggregations have been done; WHERE is applied before.
The results of the query can also be ordered. The ORDER BY
clause is used to specify
the selected values to be used to order the result. The types of expressions considered valid as part
of the order-by clause include:
state fields
component/embeddable attributes
scalar expressions such as arithmetic operations, functions, etc.
identification variable declared in the select clause for any of the previous expression types
Additionally, JPQL says that all values referenced in the order-by clause must be named in the select clause. HQL does not mandate that restriction, but applications desiring database portability should be aware that not all databases support referencing values in the order-by clause that are not referenced in the select clause.
Individual expressions in the order-by can be qualified with either ASC
(ascending) or
DESC
(descending) to indicated the desired ordering direction. Null values can be placed
in front or at the end of sorted set using NULLS FIRST
or NULLS LAST
clause respectively.
Example 14.40. Order-by examples
// legal because p.name is implicitly part of p select p from Person p order by p.name select c.id, sum( o.total ) as t from Order o inner join o.customer c group by c.id order by t
In Hibernate the HQL/JPQL query is represented as org.hibernate.Query
which
is obtained from the Session. If the HQL/JPQL is a named query, Session#getNamedQuery
would be used; otherwise Session#createQuery
would be used.
Example 14.41. Obtaining a Query reference - Hibernate
Query query = session.getNamedQuery( "my-predefined-named-query" );
Query query = session.createQuery( "select e.id, e.name from MyEntity e" );
The Query interface can then be used to control the execution of the query. For example, we may want to specify an execution timeout or control caching.
Example 14.42. Basic Query usage - Hibernate
Query query = ...; // in seconds query.setTimeout( 2 ); // write to L2 caches, but do not read from them query.setCacheMode( CacheMode.REFRESH ); // assuming query cache was enabled for the SessionFactory query.setCacheable( true ); // add a comment to the generated SQL if enabled with the SF query.setComment( "e pluribus unum" )
For complete details, see the Query javadocs.
Query hints here are database query hints. They are added directly to the generated SQL
according to Dialect#getQueryHintString
. The JPA notion of query
hints, on the other hand, refer to hints that target the provider (Hibernate). So even though
they are called the same, be aware they have a very different purpose. Also be aware that
Hibernate query hints generally make the application non-portable across databases unless the code
adding them first checks the Dialect.
Flushing is covered in detail in ???. Locking is covered in detail in ???. The concept of read-only state is covered in Chapter 5, Persistence Contexts.
Hibernate also allows an application to hook into the process of building the query results via the
org.hibernate.transform.ResultTransformer
contract. See its javadocs
as well as the Hibernate-provided implementations for additional details.
The last thing that needs to happen before we can execute the query is to bind the values for any parameters defined in the query. Query defines many overloaded methods for this purpose. The most generic form takes the value as well as the Hibernate Type.
Example 14.43. Parameter binding - Hibernate
Query query = session.createQuery( "select e from MyEntity e where e.name like :filter" ); query.setParameter( "filter", "D%", StringType.INSTANCE );
Hibernate generally understands the expected type of the parameter given its context in the query. In the previous example, since we are using the parameter in a LIKE comparison against a String-typed attribute Hibernate would automatically infer the type; so the above could be simplified.
Example 14.44. Parameter binding (inferred type) - Hibernate
Query query = session.createQuery( "select e from MyEntity e where e.name like :filter" ); query.setParameter( "filter", "D%" );
There are also short hand forms for binding common types such as strings, booleans, integers, etc.
Example 14.45. Parameter binding (short forms) - Hibernate
Query query = session.createQuery( "select e from MyEntity e where e.name like :filter" ); query.setString( "filter", "D%" ); query = session.createQuery( "select e from MyEntity e where e.active = :active" ); query.setBoolean( "active", true );
In terms of execution, Hibernate offers 4 different methods. The 2 most commonly used are
Query#list
- executes the select query and returns back the list
of results.
Query#uniqueResult
- executes the select query and returns the
single result. If there were more than one result an exception is thrown.
Example 14.46. list() and uniqueResult()
List results = session.createQuery( "select e from MyEntity e" ) .list();
String qry = "select e from MyEntity e " + " where e.code = :code" MyEntity result = (MyEntity) session.createQuery( qry ) .setParameter( "code", 123 ) .uniqueResult();
If the unique result is used often and the attributes upon which it is based are unique, you may want to consider mapping a natural-id and using the natural-id loading API. See the Hibernate Domain Mapping Guide for more information on natural-ids.
Hibernate offers 2 additional, specialized methods for performing the query and handling results.
Query#scroll
works in tandem with the JDBC notion of a scrollable
ResultSet. The scroll
method is overloaded. Then main form accepts a single
argument of type org.hibernate.ScrollMode
which indicates the type of
scrolling to be used. See the javadocs for ScrollMode for the details on each. The second form accepts
no argument and will use the ScrollMode indicated by
Dialect#defaultScrollMode
. Query#scroll
returns
a org.hibernate.ScrollableResults
which wraps the underlying JDBC
(scrollable) ResultSet and provides access to the results. Since this form holds the JDBC ResultSet
open, the application should indicate when it is done with the ScrollableResults by calling
its close
method (as inherited from java.io.Closeable
,
so that ScrollableResults will work with try-with-resources blocks!). If left unclosed by the
application, Hibernate will automatically close the ScrollableResults when the current transaction
completes.
If you plan to use Query#scroll
with collection fetches it is important
that your query explicitly order the results so that the JDBC results contain the the related
rows sequentially.
The last is Query#iterate
, which is intended for loading entities which the
the application feels certain will be in the second-level cache. The idea behind iterate is that just
the matching identifiers will be obtained in the SQL query. From these the identifiers are resolved
by second-level cache lookup. If these second-level cache lookups fail, additional queries will
need to be issued against the database. This operation can perform significantly better for loading
large numbers of entities that for certain already exist in the second-level cache. In cases where
many of the entities do not exist in the second-level cache, this operation will almost definitely
perform worse. The Iterator returned from Query#iterate
is actually a
specially typed Iterator: org.hibernate.engine.HibernateIterator
. It
is specialized to expose a close
method (again,
inherited from java.io.Closeable
). When you are done with this Iterator
you should close it, either by casting to HibernateIterator or Closeable, or by calling
org.hibernate.Hibernate#close
In JPA the query is represented by javax.persistence.Query
or
javax.persistence.TypedQuery
as obtained from the EntityManager. For named
queries EntityManager#createNamedQuery
is used; otherwise
EntityManager#createQuery
is used.
Example 14.47. Obtaining a Query reference - JPA
Query query = em.createNamedQuery( "my-predefined-named-query" ); TypedQuery<MyEntity> query2 = em.createNamedQuery( "my-predefined-named-query", MyEntity.class );
Query query = em.createQuery( "select e from MyEntity e where name like :filter" ); TypedQuery<MyEntity> query2 = em.createQuery( "select e from MyEntity e where name like :filter" MyEntity.class );
This will all sound very familiar. Not only was the JPQL syntax heavily inspired by HQL, but many of the JPA APIs were heavily inspired by Hibernate. The 2 Query contracts are very similar.
The Query interface can then be used to control the execution of the query. For example, we may want to specify an execution timeout or control caching.
Example 14.48. Basic Query usage - JPA
Query query = ...; // timeout - in milliseconds query.setHint( "javax.persistence.query.timeout", 2000 ) // Do not peform (AUTO) implicit flushing query.setFlushMode( COMMIT );
For complete details, see the Query javadocs. Many of the settings controlling the execution of the query are defined as hints. JPA defines some standard hints (like timeout in the example), but most are provider specific. Relying on provider specific hints limits your applications portability to some degree.
JPA standardized Query hints
javax.persistence.query.timeout
- Defines the query timeout, in milliseconds.
javax.persistence.fetchgraph
- Defines a "fetchgraph" EntityGraph.
Attributes explicitly specified as AttributeNodes are treated as FetchType.EAGER (via join fetch
or subsequent select). For details, see the EntityGraph discussions in Chapter 10, Fetching.
javax.persistence.loadgraph
- Defines a "loadgraph" EntityGraph. Attributes
explicitly specified as AttributeNodes are treated as FetchType.EAGER (via join fetch or
subsequent select). Attributes that are not specified are treated as FetchType.LAZY or
FetchType.EAGER depending on the attribute's definition in metadata. For details, see the
EntityGraph discussions in Chapter 10, Fetching.
Hibernate specific JPA Query hints
org.hibernate.cacheMode
- Defines the CacheMode to use. See
org.hibernate.Query#setCacheMode
.
org.hibernate.cacheable
- Defines whether the query is cacheable.
true/false. See org.hibernate.Query#setCacheable
.
org.hibernate.cacheRegion
For queries that are cacheable, defines a specific
cache region to use. See org.hibernate.Query#setCacheRegion
.
org.hibernate.comment
- Defines the comment to apply to the generated SQL.
See org.hibernate.Query#setComment
.
org.hibernate.fetchSize
- Defines the JDBC fetch-size to use. See
org.hibernate.Query#setFetchSize
org.hibernate.flushMode
- Defines the Hibernate-specific FlushMode to use.
See org.hibernate.Query#setFlushMode
. If possible, prefer using
javax.persistence.Query#setFlushMode
instead.
org.hibernate.readOnly
- Defines that entities and collections loaded by
this query should be marked as read-only. See org.hibernate.Query#setReadOnly
Just as seen in the Hibernate API, the final thing that needs to happen before the query can be executed is to bind the values for any defined parameters. JPA defines a simplified set of parameter binding methods. Essentially it supports setting the parameter value (by name/position) and a specialized form for Calendar/Date types additionally accepting a TemporalType.
Example 14.49. Parameter binding - JPA
Query query = em.createQuery( "select e from MyEntity e where e.name like :filter" ); query.setParameter( "filter", "D%" ); Query q2 = em.createQuery( "select e from MyEntity e where e.activeDate > :activeDate" ); q2.setParameter( "activeDate", new Date(), TemporalType.DATE );
Additionally, JPA allows access to read some information about parameters as well.
As far as execution, JPA supports the two main methods discussed above for the Hibernate API. It calls
these methods Query#getResultList
and Query#getSingleResult
.
They behave exactly as described for org.hibernate.Query#list
and
org.hibernate.Query#uniqueResult
.