diff --git a/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide.adoc b/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide.adoc index 0e5b3ca726..76e7dc5718 100644 --- a/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide.adoc +++ b/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide.adoc @@ -22,7 +22,8 @@ include::chapters/fetching/Fetching.adoc[] include::chapters/batch/Batching.adoc[] include::chapters/caching/Caching.adoc[] include::chapters/events/Events.adoc[] -include::chapters/query/hql/HQL.adoc[] +include::chapters/query/hql/Query.adoc[] +include::chapters/query/hql/QueryLanguage.adoc[] include::chapters/query/criteria/Criteria.adoc[] include::chapters/query/native/Native.adoc[] include::chapters/query/spatial/Spatial.adoc[] diff --git a/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc b/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc index f3886a6bbe..0bc79d00c9 100644 --- a/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc +++ b/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc @@ -389,7 +389,7 @@ The {jpaJavadocUrlPrefix}NamedQueries.html[`@NamedQueries`] annotation is used t The {jpaJavadocUrlPrefix}NamedQuery.html[`@NamedQuery`] annotation is used to specify a JPQL query that can be retrieved later by its name. -See the <> section for more info. +See the <> section for more info. [[annotations-jpa-namedstoredprocedurequeries]] ==== `@NamedStoredProcedureQueries` @@ -536,7 +536,7 @@ The {jpaJavadocUrlPrefix}PrimaryKeyJoinColumns.html[`@PrimaryKeyJoinColumns`] an The {jpaJavadocUrlPrefix}QueryHint.html[`@QueryHint`] annotation is used to specify a Jakarta Persistence provider hint used by a `@NamedQuery` or a `@NamedNativeQuery` annotation. -See the <> section for more info. +See the <> section for more info. [[annotations-jpa-secondarytable]] ==== `@SecondaryTable` @@ -1088,7 +1088,7 @@ The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibern - what SQL-level comment should be sent to the database - if the query is read-only, hence it does not store the resulted entities into the currently running Persistence Context -See the <> section for more info. +See the <> section for more info. [[annotations-hibernate-nationalized]] ==== `@Nationalized` diff --git a/documentation/src/main/asciidoc/userguide/chapters/batch/Batching.adoc b/documentation/src/main/asciidoc/userguide/chapters/batch/Batching.adoc index 0d13939203..e3b48dfd09 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/batch/Batching.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/batch/Batching.adoc @@ -293,7 +293,7 @@ include::{sourcedir}/BatchTest.java[tags=batch-bulk-hql-insert-example] ---- ==== -This section is only a brief overview of HQL. For more information, see <>. +This section is only a brief overview of HQL. For more information, see <>. [[batch-bulk-hql-strategies]] ==== Bulk-id strategies diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/hql/HQL.adoc b/documentation/src/main/asciidoc/userguide/chapters/query/hql/HQL.adoc deleted file mode 100644 index 5ef6b185c1..0000000000 --- a/documentation/src/main/asciidoc/userguide/chapters/query/hql/HQL.adoc +++ /dev/null @@ -1,2050 +0,0 @@ -[[hql]] -== HQL and JPQL -:modeldir: ../../../../../../main/java/org/hibernate/userguide/model -:sourcedir: ../../../../../../test/java/org/hibernate/userguide/hql -:extrasdir: extras - -The 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 <> for more information. - -[[hql-examples-domain-model]] -=== Example domain model - -To better understand the further HQL and JPQL examples, it's time to familiarize the domain model entities that are used in all the examples features in this chapter. - -[[hql-examples-domain-model-example]] -.Examples domain model -==== -[source, JAVA, indent=0] ----- -include::{modeldir}/Person.java[tags=hql-examples-domain-model-example] - -include::{modeldir}/AddressType.java[tags=hql-examples-domain-model-example] - -include::{modeldir}/Partner.java[tags=hql-examples-domain-model-example] - -include::{modeldir}/Phone.java[tags=hql-examples-domain-model-example] - -include::{modeldir}/PhoneType.java[tags=hql-examples-domain-model-example] - -include::{modeldir}/Call.java[tags=hql-examples-domain-model-example] - -include::{modeldir}/Payment.java[tags=hql-examples-domain-model-example] - -include::{modeldir}/CreditCardPayment.java[tags=hql-examples-domain-model-example] - -include::{modeldir}/WireTransferPayment.java[tags=hql-examples-domain-model-example] ----- -==== - -[[query-api]] -=== Query API - -When using Hibernate, you can execute entity queries either via Jakarta Persistence or the Hibernate-specific API. -Since 5.2, the Hibernate `Session` interface extends the Jakarta Persistence `EntityManager` interface. -For this reason, the query API was also merged, and now the Hibernate `org.hibernate.query.Query` interface extends the Jakarta Persistence `jakarta.persistence.Query`. - -Next, we are going to see how the query API differs between the standard Jakarta Persistence interfaces and the Hibernate-specific API. - -[[jpql-api]] -==== Jakarta Persistence Query API - -In Jakarta Persistence, the query is represented by `jakarta.persistence.Query` or `jakarta.persistence.TypedQuery` as obtained from the `EntityManager`. -The create an inline `Query` or `TypedQuery`, you need to use the `EntityManager#createQuery` method. -For named queries, the `EntityManager#createNamedQuery` method is needed. - -[[jpql-api-example]] -.Obtaining a Jakarta Persistence `Query` or a `TypedQuery` reference -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=jpql-api-example] ----- -==== - -[[jpql-api-named-query-example]] -.Obtaining a Jakarta Persistence `Query` or a `TypedQuery` reference for a named query -==== -[source, JAVA, indent=0] ----- -include::{modeldir}/Person.java[tags=jpql-api-named-query-example, indent=0] - -include::{sourcedir}/HQLTest.java[tags=jpql-api-named-query-example, indent=0] ----- -==== - -Hibernate offers a specific -https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/NamedQuery.html[`@NamedQuery`] annotation -which provides ways to configure various query features, like flush mode, cacheability, time out interval. - -[[jpql-api-hibernate-named-query-example]] -.Obtaining a Hibernate `Query` or a `TypedQuery` reference for a named query -==== -[source, JAVA, indent=0] ----- -include::{modeldir}/Phone.java[tags=jpql-api-hibernate-named-query-example, indent=0] - -include::{sourcedir}/HQLTest.java[tags=jpql-api-hibernate-named-query-example, indent=0] ----- -==== - -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. - -[[jpql-api-basic-usage-example]] -.Basic Jakarta Persistence `Query` usage -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=jpql-api-basic-usage-example] ----- -==== - -For complete details, see the `Query` {jpaJavadocUrlPrefix}Query.html[Javadocs]. -Many of the settings controlling the execution of the query are defined as hints. -Jakarta Persistence 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. - -`jakarta.persistence.query.timeout`:: - Defines the query timeout, in milliseconds. -`jakarta.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 <>. -`jakarta.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 <>. -`org.hibernate.cacheMode`:: - Defines the `CacheMode` to use. See `org.hibernate.query.Query#setCacheMode`. -`org.hibernate.cacheable`:: - Defines whether the query is cacheable. true/false. See `org.hibernate.query.Query#setCacheable`. -`org.hibernate.cacheRegion`:: - For queries that are cacheable, defines a specific cache region to use. See `org.hibernate.query.Query#setCacheRegion`. -`org.hibernate.comment`:: - Defines the comment to apply to the generated SQL. See `org.hibernate.query.Query#setComment`. -`org.hibernate.fetchSize`:: - Defines the JDBC fetch-size to use. See `org.hibernate.query.Query#setFetchSize`. -`org.hibernate.flushMode`:: - Defines the Hibernate-specific `FlushMode` to use. See `org.hibernate.query.Query#setFlushMode.` If possible, prefer using `jakarta.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.Query#setReadOnly`. - -The final thing that needs to happen before the query can be executed is to bind the values for any defined parameters. -Jakarta Persistence 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`. - -[[jpql-api-parameter-example]] -.Jakarta Persistence name parameter binding -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=jpql-api-parameter-example] ----- -==== - -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. - -[[jpql-api-positional-parameter-example]] -.Jakarta Persistence positional parameter binding -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=jpql-api-positional-parameter-example] ----- -==== - -[NOTE] -==== -It's good practice not to mix parameter binding forms in a given query. -==== - -In terms of execution, Jakarta Persistence `Query` offers 3 different methods for retrieving a result set. - -* `Query#getResultList()` - executes the select query and returns back the list of results. -* `Query#getResultStream()` - executes the select query and returns back a `Stream` over the results. -* `Query#getSingleResult()` - executes the select query and returns a single result. If there were more than one result an exception is thrown. - -[[jpql-api-list-example]] -.Jakarta Persistence `getResultList()` result -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=jpql-api-list-example] ----- -==== - -[[jpql-api-stream-example]] -.Jakarta Persistence `getResultStream()` result -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=jpql-api-stream-example] ----- -==== - -[[jpql-api-unique-result-example]] -.Jakarta Persistence `getSingleResult()` -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=jpql-api-single-result-example] ----- -==== - -[[hql-api]] -==== Hibernate Query API - -In Hibernate, the HQL query is represented as `org.hibernate.query.Query` which is obtained from a `Session`. -If the HQL is a named query, `Session#getNamedQuery` would be used; otherwise `Session#createQuery` is needed. - -[[hql-api-example]] -.Obtaining a Hibernate `Query` -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-example] ----- -==== - -[[hql-api-named-query-example]] -.Obtaining a Hibernate `Query` reference for a named query -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-named-query-example] ----- -==== - -[NOTE] -==== -Not only was the JPQL syntax heavily inspired by HQL, but many of the Jakarta Persistence APIs were heavily inspired by Hibernate too. -The two `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. - -[[hql-api-basic-usage-example]] -.Basic Query usage - Hibernate -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-basic-usage-example] ----- -==== - -For complete details, see the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/Query.html[Query] Javadocs. - -[IMPORTANT] -==== -Query hints here are database query hints. -They are added directly to the generated SQL according to `Dialect#getQueryHintString`. - -The Jakarta Persistence 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 <>. - -Hibernate also allows an application to hook into the process of building the query results via the `org.hibernate.transform.ResultTransformer` contract. -See its https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/transform/ResultTransformer.html[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. - -[[hql-api-parameter-example]] -.Hibernate name parameter binding -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-parameter-example] ----- -==== - -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. - -[[hql-api-parameter-inferred-type-example]] -.Hibernate name parameter binding (inferred type) -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-parameter-inferred-type-example] ----- -==== - -There are also short hand forms for binding common types such as strings, booleans, integers, etc. - -[[hql-api-parameter-short-form-example]] -.Hibernate name parameter binding (short forms) -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-parameter-short-form-example] ----- -==== - -[IMPORTANT] -==== -Traditionally, Hibernate used to support a JDBC positional parameter syntax form via a `?` symbol without a following ordinal. - -There was no way to relate two such positional parameters as being "the same" aside from binding the same value to each and, for this reason, this form is no longer supported. - -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-positional-parameter-example] ----- -==== - -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. - -[[hql-api-list-example]] -.Hibernate `list()` result -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-list-example] ----- -==== - -It is also possible to extract a single result from a `Query`. - -[[hql-api-unique-result-example]] -.Hibernate `uniqueResult()` -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-unique-result-example] ----- -==== - -[NOTE] -==== -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 <> for more information on this topic. -==== - -[[hql-api-scroll]] -==== Query scrolling - -Hibernate offers additional, specialized methods for scrolling the query and handling results using a server-side cursor. - -`Query#scroll` works in tandem with the JDBC notion of a scrollable `ResultSet`. - -The `Query#scroll` method is overloaded: - -* The main form accepts a single argument of type `org.hibernate.ScrollMode` which indicates the type of scrolling to be used. -See the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/ScrollMode.html[Javadocs] for the details on each. -* The second form takes 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. -Unlike a typical forward-only `ResultSet`, the `ScrollableResults` allows you to navigate the `ResultSet` in any direction. - -[[hql-api-scroll-example]] -.Scrolling through a `ResultSet` containing entities -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-scroll-example] ----- -==== - -[IMPORTANT] -==== -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 https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html[try-with-resources] blocks). - -If left unclosed by the application, Hibernate will automatically close the underlying resources (e.g. `ResultSet` and `PreparedStatement`) used internally by the `ScrollableResults` when the current transaction is ended (either commit or rollback). - -However, it is good practice to close the `ScrollableResults` explicitly. -==== - -[NOTE] -==== -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 related rows sequentially. -==== - -Hibernate also supports `Query#iterate`, which is intended for loading entities when it is known that the loaded entries are already stored 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. - -[NOTE] -==== -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 https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/Hibernate.html#close-java.util.Iterator-[`Hibernate#close(java.util.Iterator)`]. - -Since 5.2, Hibernate offers support for returning a `Stream` which can be later used to transform the underlying `ResultSet`. - -Internally, the `stream()` behaves like a `Query#scroll` and the underlying result is backed by a `ScrollableResults`. - -Fetching a projection using the `Query#stream` method can be done as follows: - -[[hql-api-stream-projection-example]] -.Hibernate `stream()` using a projection result type -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-stream-projection-example] ----- -==== - -When fetching a single result, like a `Person` entity, instead of a `Stream`, Hibernate is going to figure out the actual type, so the result is a `Stream`. - -[[hql-api-stream-example]] -.Hibernate `stream()` using an entity result type -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-api-stream-example] ----- -==== - -[IMPORTANT] -==== -Just like with `ScrollableResults`, you should always close a Hibernate `Stream` either explicitly or using a https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html[try-with-resources] block. -==== - -[[jpql-api-stream]] -==== Query streaming - -The Jakarta Persistence `Query` interface offers support for returning a `Stream` via the `getResultStream` method. - -Just like the `scroll` method, you can use a try-with-resources block to close the `Stream` -prior to closing the currently running Persistence Context. - -Since Hibernate 5.4, the `Stream` is also closed when calling a terminal operation, -as illustrated by the following example. - -[[jpql-api-stream-terminal-operation]] -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=jpql-api-stream-terminal-operation] ----- -==== - -The `Stream` is closed automatically after calling the `collect` method, -since there is no reason to keep the underlying JDBC `ResultSet` open -if the `Stream` cannot be reused. - -[[hql-case-sensitivity]] -=== Case Sensitivity - -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`. - -[NOTE] -==== -This documentation uses lowercase keywords as a convention in query examples. -==== - -[[hql-statement-types]] -=== Statement types - -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 FROM SELECT`. - -[IMPORTANT] -==== -Care should be taken as to when an `UPDATE` or `DELETE` statement is executed. - -[quote, Section 4.10 of the Java Persistence 2.0 Specification] -____ -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 context -or before fetching or accessing entities whose state might be affected by such operations. -____ -==== - -[[hql-select]] -=== Select statements - -The https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form[BNF] for `SELECT` statements in HQL is: - -[[hql-select-bnf-example]] -==== -[source, SQL, indent=0] ----- -include::{extrasdir}/statement_select_bnf.txt[] ----- -==== - -The simplest possible HQL `SELECT` statement is of the form: - -[[hql-select-simplest-example]] -==== -[source, SQL, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-select-simplest-example] ----- -==== - -[NOTE] -==== -The select statement in JPQL is exactly the same as for HQL except that JPQL requires a `select_clause`, whereas HQL does not. - -[source, SQL, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-select-simplest-jpql-example] ----- - -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 easy 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 Jakarta Persistence portability should take heed of this. -==== - -[[hql-update]] -=== Update statements - -The BNF for `UPDATE` statements is the same in HQL and JPQL: - -[[hql-update-bnf-example]] -==== -[source, SQL, indent=0] ----- -include::{extrasdir}/statement_update_bnf.txt[] ----- -==== - -`UPDATE` statements, by default, do not affect 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] -==== -Versioned updates 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 an `update versioned` statement. -==== - -An `UPDATE` statement is executed using the `executeUpdate()` of either `org.hibernate.query.Query` or `jakarta.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 affected by the operation. -This may or may not correlate to the number of rows affected 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". - -[[hql-update-example]] -.UPDATE query statements -==== -[source, SQL, indent=0] ----- -include::{sourcedir}/../batch/BatchTest.java[tags=batch-bulk-jpql-update-example] - -include::{sourcedir}/../batch/BatchTest.java[tags=batch-bulk-hql-update-example] - -include::{sourcedir}/../batch/BatchTest.java[tags=batch-bulk-hql-update-version-example] ----- -==== - -[IMPORTANT] -==== -Neither `UPDATE` nor `DELETE` statements allow implicit joins. Their form already disallows explicit joins too. -==== - -[[hql-delete]] -=== Delete statements - -The BNF for `DELETE` statements is the same in HQL and JPQL: - -[[hql-delete-bnf-example]] -==== -[source, SQL, indent=0] ----- -include::{extrasdir}/statement_delete_bnf.txt[] ----- -==== - -A `DELETE` statement is also executed using the `executeUpdate()` method of either `org.hibernate.query.Query` or `jakarta.persistence.Query`. - -[[hql-insert]] -=== Insert statements - -HQL adds the ability to define `INSERT` statements as well. - -[NOTE] -==== -There is no JPQL equivalent to HQL-style INSERT statements. -==== - -The BNF for an HQL `INSERT` statement is: - -[[hql-insert-bnf-example]] -==== -[source, SQL, indent=0] ----- -include::{extrasdir}/statement_insert_bnf.txt[] ----- -==== - -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 delegate to the database. -This may cause problems between Hibernate Types which are _equivalent_ as opposed to __equal__. -For example, this might lead to issues with mismatches between an attribute mapped as a `org.hibernate.type.StandardBasicTypes.DATE` and an attribute defined as a `org.hibernate.type.StandardBasicTypes.TIMESTAMP`, -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. - -[[hql-insert-example]] -.INSERT query statements -==== -[source, SQL, indent=0] ----- -include::{sourcedir}/../batch/BatchTest.java[tags=batch-bulk-hql-insert-example] ----- -==== - -[[hql-from-clause]] -=== The `FROM` clause - -The `FROM` clause is responsible for defining the scope of object model types available to the rest of the query. -It is also responsible for defining all the "identification variables" available to the rest of the query. - -[[hql-identification-variables]] -=== Identification variables - -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 throughout 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. - -[[hql-root-reference]] -=== Root entity references - -A root entity reference, or what Jakarta Persistence 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, as later discussed. - -The BNF for a root entity reference is: - -[[hql-root-reference-bnf-example]] -==== -[source, SQL, indent=0] ----- -include::{extrasdir}/root_entity_ref_bnf.txt[] ----- -==== - -[[hql-root-reference-jpql-fqn-example]] -.Simple query example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-select-simplest-jpql-fqn-example] ----- -==== - -We see that the query is defining a root entity reference to the `org.hibernate.userguide.model.Person` object model type. -Additionally, it declares an alias of `p` to that `org.hibernate.userguide.model.Person` reference, which is the identification variable. - -Usually, the root entity reference represents just the `entity name` rather than the entity class FQN (fully-qualified name). -By default, the entity name is the unqualified entity class name, here `Person`. - -[[hql-root-reference-jpql-example]] -.Simple query using entity name for root entity reference -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-select-simplest-jpql-example] ----- -==== - -Multiple root entity references can also be specified, even when naming the same entity. - -[[hql-multiple-root-reference-jpql-example]] -.Simple query using multiple root entity references -==== -[source, SQL, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-multiple-root-reference-jpql-example] ----- - -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-multiple-same-root-reference-jpql-example] ----- -==== - -[[hql-explicit-join]] -=== Explicit joins - -The `FROM` clause can also contain explicit relationship joins using the `join` keyword. -These joins can be either `inner` or `left outer` style joins. - -[[hql-explicit-inner-join-example]] -.Explicit inner join examples -==== -[source, SQL, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-explicit-inner-join-example] ----- -==== - -[[hql-explicit-outer-join-example]] -.Explicit left (outer) join examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-explicit-outer-join-example] ----- -==== - -HQL also defines a `WITH` clause to qualify the join conditions. - -[NOTE] -==== -The HQL-style WITH keyword is specific to Hibernate. JPQL defines the `ON` clause for this feature. -==== - -[[hql-explicit-join-with-example]] -.HQL `WITH` clause join example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-explicit-join-with-example] ----- -==== - -[[hql-explicit-join-jpql-on-example]] -.JPQL `ON` clause join example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-explicit-join-jpql-on-example] ----- -==== - -[NOTE] -==== -The important distinction is that in the generated SQL the conditions of the `WITH/ON` 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 for more complicated queries. - -Explicit joins may reference association or component/embedded attributes. -In the case of component/embedded attributes, the join is simply logical and does not correlate to a physical (SQL) join. -For further information about collection-valued association references, see <>. - -An important use case for explicit joins is to define ``FETCH JOIN``s which override the laziness of the joined association. -As an example, given an entity named `Person` with a collection-valued association named `phones`, the `JOIN FETCH` will also load the child collection in the same SQL query: - -[[hql-explicit-fetch-join-example]] -.Fetch join example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-explicit-fetch-join-example] ----- -==== - -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 also wanted to return customers who have no orders. - -Inner joins can also be fetched, but inner joins filter out the root entity. -In the example, using an inner join instead would have resulted in customers without any orders being filtered out of the result. - -[IMPORTANT] -==== -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 not to 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 (e.g. `setFirstResult()` or `setMaxResults()`), nor should they be used with the `scroll()` or `iterate()` features. -==== - -[[hql-implicit-join]] -=== Implicit joins (path expressions) - -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. - -[[hql-implicit-join-example]] -.Simple implicit join example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-implicit-join-example] ----- -==== - -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 `ph` which refers to the `Phone` entity. -The `ph.person` reference then refers to the `person` attribute of the `Phone` entity. -`person` is an association type so we further navigate to its age attribute. - -[IMPORTANT] -==== -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`. - -[NOTE] -==== -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. -==== - -[[hql-implicit-join-alias-example]] -.Reused implicit join -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-implicit-join-alias-example] ----- -==== - -Just as with explicit joins, implicit joins may reference association or component/embedded attributes. -For further information about collection-valued association references, see <>. - -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. - -[[hql-distinct]] -=== Distinct - -For JPQL and HQL, `DISTINCT` has two meanings: - -. It can be passed to the database so that duplicates are removed from a result set -. It can be used to filter out the same parent entity references when join fetching a child collection - -[[hql-distinct-projection-query]] -==== Using DISTINCT with SQL projections - -For SQL projections, `DISTINCT` needs to be passed to the database because the duplicated entries need to be filtered out before being returned to the database client. - -[[hql-distinct-projection-query-example]] -.Using DISTINCT with projection queries example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/SelectDistinctTest.java[tags=hql-distinct-projection-query-example] ----- -==== - -When running the query above, Hibernate generates the following SQL query: - -==== -[source, SQL, indent=0] ----- -include::{extrasdir}/hql-distinct-projection-query-example.sql[] ----- -==== - -For this particular use case, passing the `DISTINCT` keyword from JPQL/HQL to the database is the right thing to do. - -[[hql-distinct-entity-query]] -==== Using DISTINCT with entity queries - -`DISTINCT` can also be used to filter out entity object references when fetching a child association along with the parent entities. - -[[hql-distinct-entity-query-example]] -.Using DISTINCT with entity queries example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/SelectDistinctTest.java[tags=hql-distinct-entity-query-example] ----- -==== - -In this case, `DISTINCT` is used because there can be multiple `Books` entities associated with a given `Person`. -If in the database there are 3 ``Person``s in the database and each person has 2 ``Book``s, without `DISTINCT` this query will return 6 ``Person``s since -the SQL-level result-set size is given by the number of joined `Book` records. - -However, the `DISTINCT` keyword is passed to the database as well: - -==== -[source, SQL, indent=0] ----- -include::{extrasdir}/hql-distinct-entity-query-example.sql[] ----- -==== - -In this case, the `DISTINCT` SQL keyword is undesirable since it does a redundant result set sorting, as explained https://in.relation.to/2016/08/04/introducing-distinct-pass-through-query-hint/[in this blog post]. -To fix this issue, Hibernate 5.2.2 added support for the `HINT_PASS_DISTINCT_THROUGH` entity query hint: - -[[hql-distinct-entity-query-hint-example]] -.Using DISTINCT with entity queries example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/SelectDistinctTest.java[tags=hql-distinct-entity-query-hint-example] ----- -==== - -With this entity query hint, Hibernate will not pass the `DISTINCT` keyword to the SQL query: - -==== -[source, SQL, indent=0] ----- -include::{extrasdir}/hql-distinct-entity-query-hint-example.sql[] ----- -==== - -When using the `HINT_PASS_DISTINCT_THROUGH` entity query hint, Hibernate can still remove the duplicated parent-side entities from the query result. - -[[hql-collection-valued-associations]] -=== Collection member references - -References to collection-valued associations actually refer to the _values_ of that collection. - -[[hql-collection-valued-associations-example]] -.Collection references example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-collection-valued-associations] ----- -==== - -In the example, the identification variable `ph` actually refers to the object model type `Phone`, which is the type of the elements of the `Person#phones` 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. - -[[hql-collection-qualification]] -=== Special case - qualified path expressions - -We said earlier that collection-valued associations actually refer to the _values_ of that collection. -Based on the type of collection, there are also a set of explicit qualification expressions available. - -[[hql-collection-qualification-example]] -.Qualified collection references example -==== -[source, JAVA, indent=0] ----- -include::{modeldir}/Phone.java[tags=hql-collection-qualification-example, indent=0] - -include::{sourcedir}/HQLTest.java[tags=hql-collection-qualification-example, indent=0] ----- -==== - -VALUE:: - Refers to the collection value. - Same as not specifying a qualifier. - Useful to explicitly show intent. - Valid for any type of collection-valued reference. -INDEX:: - According to HQL rules, this is valid for both `Maps` and `Lists` which specify a `jakarta.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 Jakarta Persistence provider portability should be aware of this distinction. -KEY:: - Valid only for `Maps`. Refers to the map's key. If the key is itself an entity, it can be further navigated. -ENTRY:: - Only valid 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 it's applicable to the `SELECT` clause only. - -See <> for additional details on collection-related expressions. - -[[hql-polymorphism]] -=== Polymorphism - -HQL and JPQL queries are inherently polymorphic. - -[[hql-polymorphism-example]] -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-polymorphism-example, indent=0] ----- -==== - -This query names the `Payment` entity explicitly. -However, all subclasses of `Payment` are also available to the query. -So, if the `CreditCardPayment` and `WireTransferPayment` entities extend the `Payment` class, all three types would be available to the entity query, -and the query would return instances of all three. - - -This behavior can be altered in two ways: - -- by limiting the query to select only from the subclass entity. -- by using either the `org.hibernate.annotations.Polymorphism` annotation (global, and Hibernate-specific). See the <> for more info about this use case. - -[NOTE] -==== -The HQL query `from java.lang.Object` is totally valid (although not very practical from a performance perspective)! - -It returns every object of every entity type defined by your application mappings. -==== - -[[hql-expressions]] -=== Expressions - -Essentially, expressions are references that resolve to basic or tuple values. - -[[hql-identification-variable]] -=== Identification variable - -See <>. - -[[hql-path-expressions]] -=== Path expressions - -Again, see <>. - -[[hql-literals]] -=== Literals - -String literals are enclosed in single quotes. -To escape a single quote within a string literal, use double single quotes. - -[[hql-string-literals-example]] -.String literals examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-string-literals-example] ----- -==== - -Numeric literals are allowed in a few different forms. - -[[hql-numeric-literals-example]] -.Numeric literal examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-numeric-literals-example] ----- -==== - -[NOTE] -==== -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 being supported. - -Entity names can also be used as literal. See <>. - -Date/time literals can be specified using the JDBC escape syntax: - -* `{d 'yyyy-mm-dd'}` for dates -* `{t 'hh:mm:ss'}` for times -* `{ts 'yyyy-mm-dd hh:mm:ss[.millis]'}` (millis optional) for timestamps. - -These Date/time literals only work if the underlying JDBC driver supports them. -==== - -[[hql-numeric-arithmetic]] -=== Arithmetic - -Arithmetic operations also represent valid expressions. - -[[hql-numeric-arithmetic-example]] -.Numeric arithmetic examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-numeric-arithmetic-example] ----- -==== - -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 to differences in database support and partly to the lack of support for `INTERVAL` definition in the query language itself. - -[[hql-concatenation]] -=== Concatenation (operation) - -HQL defines a concatenation operator in addition to supporting the concatenation (`CONCAT`) function. -This is not defined by JPQL, so portable applications should avoid its use. -The concatenation operator is taken from the SQL concatenation operator (e.g `||`). - -[[hql-concatenation-example]] -.Concatenation operation example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-concatenation-example] ----- -==== - -See <> for details on the `concat()` function. - -[[hql-aggregate-functions]] -=== Aggregate functions - -Aggregate functions are also valid expressions in HQL and JPQL. -The semantics 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 `SUM()` function depends on the type of the values being summed. - 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`. - -Aggregations often appear with grouping. For information on grouping see <>. - -[[hql-aggregate-functions-example]] -.Aggregate function examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-aggregate-functions-example] ----- -==== - -All of these aggregate functions also support the inclusion of a specific *filter clause* - -[[hql-aggregate-functions-filter-example]] -.Filter clause example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-aggregate-functions-filter-example] ----- -==== - -[[hql-exp-functions]] -=== Scalar functions - -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. - -[[jpql-standardized-functions]] -=== JPQL standardized functions - -Here is the list of functions defined as supported by JPQL. -Applications interested in remaining portable between Jakarta Persistence providers should stick to these functions. - -CONCAT:: - String concatenation function. Variable argument length of 2 or more string values to be concatenated together. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-concat-function-example] ----- -==== - -SUBSTRING:: - Extracts a portion of a string value. - The second argument denotes the starting position, where 1 is the first character of the string. - The third (optional) argument denotes the length. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-substring-function-example] ----- -==== - -UPPER:: - Upper cases the specified string. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-upper-function-example] ----- -==== - -LOWER:: - Lower cases the specified string. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-lower-function-example] ----- -==== - -TRIM:: - Follows the semantics of the SQL trim function. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-trim-function-example] ----- -==== - -LENGTH:: - Returns the length of a string. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-length-function-example] ----- -==== - -LOCATE:: - Locates a string within another string. - The third argument (optional) is used to denote a position from which to start looking. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-locate-function-example] ----- -==== - -ABS:: - Calculates the mathematical absolute value of a numeric value. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-abs-function-example] ----- -==== - -MOD:: - Calculates the remainder of dividing the first argument by the second. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-mod-function-example] ----- -==== - -SQRT:: - Calculates the mathematical square root of a numeric value. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-sqrt-function-example] ----- -==== - -CURRENT_DATE:: - Returns the database current date. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-current-date-function-example] ----- -==== - -CURRENT_TIME:: - Returns the database current time. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-current-time-function-example] ----- -==== - -CURRENT_TIMESTAMP:: - Returns the database current timestamp. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-current-timestamp-function-example] ----- -==== - -[[hql-functions]] -=== HQL functions - -Beyond the JPQL standardized functions, HQL makes some additional functions available regardless of the underlying database in use. - -BIT_LENGTH:: - Returns the length of binary data. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-bit-length-function-example] ----- -==== - -CAST:: - Performs a SQL cast. - The cast target should name the Hibernate mapping type to use. - See the <> chapter on for more information. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-cast-function-example] ----- -==== - -EXTRACT:: - Performs a SQL extraction on datetime values. - An extraction extracts parts of the datetime (the year, for example). - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-extract-function-example] ----- -==== - -See the abbreviated forms below. - -YEAR:: -Abbreviated extract form for extracting the year. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-year-function-example] ----- -==== - -MONTH:: -Abbreviated extract form for extracting the month. -DAY:: -Abbreviated extract form for extracting the day. -HOUR:: -Abbreviated extract form for extracting the hour. -MINUTE:: -Abbreviated extract form for extracting the minute. -SECOND:: -Abbreviated extract form for extracting the second. -STR:: -Abbreviated form for casting a value as character data. - -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-str-function-example] ----- -==== - -[[hql-user-defined-functions]] -=== User-defined functions - -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 Jakarta Persistence 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 user-defined SQL functions or aliases for snippets of SQL. -Such function declarations are made by using the `addSqlFunction()` method of -the `org.hibernate.boot.MetadataBuilder` or the legacy `org.hibernate.cfg.Configuration`. - -Now, let's assume we have the following `apply_vat` PostgreSQL user-defined function: - -[[hql-user-defined-function-postgresql-example]] -.PostgreSQL user-defined function -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/PostgreSQLFunctionWhereClauseTest.java[tags=hql-user-defined-function-postgresql-example] ----- -==== - -Let's consider we have persisted the following entity in our database: - -[[hql-user-defined-function-postgresql-entity-example]] -.Book entity -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/PostgreSQLFunctionWhereClauseTest.java[tags=hql-user-defined-function-postgresql-entity-example] ----- -==== - -[[hql-user-defined-functions-where-clause]] -==== User-defined functions referenced in the WHERE clause - -By default, Hibernate can pass through any user-defined function that's being used in the WHERE clause -of a JPQL/HQL entity query. - -[[hql-user-defined-function-postgresql-where-clause-example]] -.User-defined function passing through the WHERE clause -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/PostgreSQLFunctionWhereClauseTest.java[tags=hql-user-defined-function-postgresql-where-clause-example] ----- -==== - -While this works just fine with Hibernate, it might be a problem with other Jakarta Persistence providers. -For this purpose, Jakarta Persistence offers the `function` JPQL keyword which works as follows. - -[[hql-user-defined-function-postgresql-jpql-example]] -.Using the JPQL `function` keyword -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/PostgreSQLFunctionWhereClauseTest.java[tags=hql-user-defined-function-postgresql-jpql-example] ----- -==== - -[[hql-user-defined-functions-select-clause]] -==== User-defined functions referenced in the SELECT clause - -When the user-defined function is referenced in the SELECT clause of a JPQL/HQL entity query, -Hibernate can no longer pass it through unless the function is registered. - -[[hql-user-defined-functions-register-metadata-builder-example]] -.Registering a user-defined function using the `MetadataBuilderContributor` -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/PostgreSQLFunctionSelectClauseTest.java[tags=hql-user-defined-functions-register-metadata-builder-example] ----- -==== - -Now that that `apply_vat` is registered, we can reference it in the JPQL SELECT clause. - -[[hql-user-defined-function-postgresql-select-clause-example]] -.User-defined function in the SELECT clause -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/PostgreSQLFunctionSelectClauseTest.java[tags=hql-user-defined-function-postgresql-select-clause-example] ----- -==== - -[[hql-collection-expressions]] -=== Collection-related expressions - -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. - -SIZE:: - Calculate the size of a collection. Equates to a subquery! -MAXELEMENT:: - Available for use on collections of basic type. - Refers to the maximum value as determined by applying the `max` SQL aggregation. -MAXINDEX:: - Available for use on indexed collections. - Refers to the maximum index (key/position) as determined by applying the `max` SQL aggregation. -MINELEMENT:: - Available for use on collections of basic type. - Refers to the minimum value as determined by applying the `min` SQL aggregation. -MININDEX:: - Available for use on indexed collections. - Refers to the minimum index (key/position) as determined by applying the `min` SQL aggregation. -ELEMENTS:: - 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. -INDICES:: - Similar to `elements` except that the `indices` expression refers to the collections indices (keys/positions) as a whole. - -[[hql-collection-expressions-example]] -.Collection-related expressions examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-collection-expressions-example] ----- -==== - -Elements of indexed collections (arrays, lists, and maps) can be referred to by index operator. - -[[hql-collection-index-operator-example]] -.Index operator examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-collection-index-operator-example] ----- -==== - -See also <> as there is a good deal of overlap. - -[[hql-entity-type-exp]] -=== Entity type - -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 be 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 parameterized, in which case the entity's Java Class reference would be bound as the parameter value. - -[[hql-entity-type-exp-example]] -.Entity type expression examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-entity-type-exp-example] ----- -==== - -[NOTE] -==== -HQL also has a legacy form of referring to an entity type using the `class` keyword, 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. -==== - -[[hql-case-expressions]] -=== CASE expressions - -Both the simple and searched forms are supported, as well as the two SQL defined abbreviated forms (`NULLIF` and `COALESCE`) - -[[hql-simple-case-expressions]] -=== Simple CASE expressions - -The simple form has the following syntax: - -[source, JAVA, indent=0] ----- -include::{extrasdir}/simple_case_bnf.txt[] ----- - -[[hql-simple-case-expressions-example]] -.Simple case expression example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-simple-case-expressions-example] ----- -==== - -[[hql-searched-case-expressions]] -=== Searched CASE expressions - -The searched form has the following syntax: - -[[hql-searched-case-expressions-bnf]] -[source, JAVA, indent=0] ----- -include::{extrasdir}/searched_case_bnf.txt[] ----- - -[[hql-searched-case-expressions-example]] -.Searched case expression example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-searched-case-expressions-example] ----- -==== - -[[hql-case-arithmetic-expressions]] -=== CASE expressions with arithmetic operations - -If you want to use arithmetic operations in the CASE expressions, you need to wrap the arithmetic operation in parentheses -as illustrated by the following example: - -[[hql-case-arithmetic-expressions-example]] -.Case expression with arithmetic operation example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-case-arithmetic-expressions-example] ----- -==== - -[IMPORTANT] -==== -Without wrapping the arithmetic expression in `(` and `)`, the entity query parser will not be able to -parse the arithmetic operators. -==== - -[[hql-nullif]] -=== NULLIF expressions - -NULLIF is an abbreviated CASE expression that returns NULL if its operands are considered equal. - -[[hql-nullif-example]] -.NULLIF example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-nullif-example] ----- -==== - -=== COALESCE expressions - -`COALESCE` is an abbreviated CASE expression that returns the first non-null operand. -We have seen a number of `COALESCE` examples above. - -[[hql-select-clause]] -=== The `SELECT` clause - -The `SELECT` clause identifies which objects and values to return as the query results. -The expressions discussed in <> are all valid select expressions, except where otherwise noted. -See the section <> 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". - -So rather than dealing with the `Object[]` (again, see <>) here, we are wrapping the values in a type-safe Java object that will be returned as the results of the query. - -[[hql-select-clause-dynamic-instantiation-example]] -.Dynamic HQL and JPQL instantiation example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/CallStatistics.java[tags=hql-select-clause-dynamic-instantiation-example] - -include::{sourcedir}/HQLTest.java[tags=hql-select-clause-dynamic-instantiation-example, indent=0] ----- -==== - -[NOTE] -==== -The projection class must be fully qualified in the entity query, and it must define a matching constructor. -==== - -[IMPORTANT] -==== -The class here need not be mapped. It can be a DTO class. - -If it does represent an entity, the resulting instances are returned in the NEW state (not managed!). -==== - -HQL supports additional "dynamic instantiation" features. -First, the query can specify to return a `List` rather than an `Object[]` for scalar results: - -[[hql-select-clause-dynamic-list-instantiation-example]] -.Dynamic instantiation example - list -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-select-clause-dynamic-list-instantiation-example] ----- -==== - -The results from this query will be a `List` as opposed to a `List` - -HQL also supports wrapping the scalar results in a `Map`. - -[[hql-select-clause-dynamic-map-instantiation-example]] -.Dynamic instantiation example - map -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-select-clause-dynamic-map-instantiation-example] ----- -==== - -The results from this query will be a `List>` as opposed to a `List`. -The keys of the map are defined by the aliases given to the select expressions. -If the user doesn't assign aliases, the key will be the index of each particular result set column (e.g. 0, 1, 2, etc). - -[[hql-conditional-expressions]] -=== Predicates - -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 `NULL` resolve typically to `UNKNOWN`. - -[[hql-relational-comparisons]] -=== Relational comparisons - -Comparisons involve one of the comparison operators: `=`, `>`, `>=`, `<`, `\<=`, `<>`. -HQL also defines `!=` as a comparison operator synonymous with `<>`. -The operands should be of the same type. - -[[hql-relational-comparisons-example]] -.Relational comparison examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-relational-comparisons-example] ----- -==== - -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. - -[[hql-all-subquery-comparison-qualifier-example]] -.ALL subquery comparison qualifier example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-all-subquery-comparison-qualifier-example] ----- -==== - -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. - -[[hql-null-predicate]] -=== Nullness predicate - -It check a value for nullness. -It can be applied to basic attribute references, entity references, and parameters. -HQL additionally allows it to be applied to component/embeddable types. - -[[hql-null-predicate-example]] -.Nullness checking examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-null-predicate-example] ----- -==== - -[[hql-like-predicate]] -=== Like predicate - -Performs a like comparison on string values. The syntax is: - -[[hql-like-predicate-bnf]] -[source, JAVA, indent=0] ----- -include::{extrasdir}/predicate_like_bnf.txt[] ----- - -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. The `_` symbol matches any single character and `%` matches any number of characters. - -[[hql-like-predicate-example]] -.Like predicate examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-like-predicate-example] ----- -==== - -The optional `escape '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 `%`. - -The syntax is formed as follows: `'like_predicate' escape 'escape_symbol'` -So, if `|` is the escape symbol and we want to match all stored procedures prefixed with `Dr_`, the like criteria becomes: `'Dr|_%' escape '|'`: - -[[hql-like-predicate-escape-example]] -.Like with escape symbol -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-like-predicate-escape-example] ----- -==== - -[[hql-ilike-predicate]] -=== Ilike predicate - -Performs a case-insensitive like comparison on string values. The syntax is: - -[[hql-ilike-predicate-bnf]] -[source, JAVA, indent=0] ----- -include::{extrasdir}/predicate_ilike_bnf.txt[] ----- - -The semantics are identical to those of the aforementioned <>, with the sole difference that the comparison is now case insensitive. - - -[[hql-between-predicate]] -=== Between predicate - -Analogous to the SQL `BETWEEN` expression, -it checks if the value is within boundaries. -All the operands should have comparable types. - -[[hql-between-predicate-example]] -.Between predicate examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-between-predicate-example] ----- -==== - -[[hql-in-predicate]] -=== In predicate - -`IN` predicates performs a check that a particular value is in a list of values. Its syntax is: - -[[hql-in-predicate-bnf]] -[source, JAVA, indent=0] ----- -include::{extrasdir}/predicate_in_bnf.txt[] ----- - -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, and, 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 <>. - -In HQL, `single_valued_expression` can refer to a far more broad set of expression types. -Single-valued association are allowed, and 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. - -[[hql-in-predicate-example]] -.In predicate examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-in-predicate-example] ----- -==== - -[[hql-exists-predicate]] -=== Exists predicate - -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. - -[[hql-empty-collection-predicate]] -=== Empty collection predicate - -The `IS [NOT] EMPTY` expression applies to collection-valued path expressions. -It checks whether the particular collection has any associated values. - -[[hql-empty-collection-predicate-example]] -.Empty collection expression examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-empty-collection-predicate-example] ----- -==== - -[[hql-member-of-collection-predicate]] -=== Member-of collection predicate - -The `[NOT] MEMBER [OF]` expression applies to collection-valued path expressions. -It checks whether a value is a member of the specified collection. - -[[hql-member-of-collection-predicate-example]] -.Member-of collection expression examples -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-member-of-collection-predicate-example] ----- -==== - -[[hql-not-predicate]] -=== NOT predicate operator - -The `NOT` operator is used to negate the predicate that follows it. -If that following predicate is true, the NOT resolves to false. - -[NOTE] -==== -If the predicate is true, NOT resolves to false. If the predicate is unknown (e.g. `NULL`), then NOT resolves to unknown as well. -==== - -[[hql-and-predicate]] -=== AND predicate operator - -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. - -[[hql-or-predicate]] -=== OR predicate operator - -The `OR` operator is used to combine 2 predicate expressions. -The result of the OR expression is true if one predicate resolves to true. -If both predicates resolve to unknown, the OR expression resolves to unknown. -Otherwise, the result is false. - -[[hql-where-clause]] -=== The `WHERE` clause - -The `WHERE` clause of a query is made up of predicates which assert whether values in each potential row match the current filtering criteria. -Thus, the where clause restricts the results returned from a select query and limits the scope of update and delete queries. - -[[hql-group-by]] -=== Group by - -The `GROUP BY` clause allows building aggregated results for various value groups. As an example, consider the following queries: - -[[hql-group-by-example]] -.Group by example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-group-by-example] ----- -==== - -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 <>, we retrieved `Call` duration totals for all persons. -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 total of more than 1000: - -[[hql-group-by-having-example]] -.Having example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-group-by-having-example] ----- -==== - -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, while the `WHERE` clause is applied before. - -[[hql-order-by]] -=== Order by - -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 indicate the desired ordering direction. -Null values can be placed in front or at the end of the sorted set using `NULLS FIRST` or `NULLS LAST` clause respectively. - -[[hql-order-by-example]] -.Order by example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-order-by-example] ----- -==== - -[[hql-read-only-entities]] -=== Read-only entities - -As explained in <> section, fetching entities in read-only mode is much more efficient than fetching read-write entities. -Even if the entities are mutable, you can still fetch them in read-only mode, and benefit from reducing the memory footprint and speeding up the flushing process. - -Read-only entities are skipped by the dirty checking mechanism as illustrated by the following example: - -[[hql-read-only-entities-example]] -.Read-only entities query example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-read-only-entities-example] ----- - -[source, SQL, indent=0] ----- -include::{extrasdir}/hql-read-only-entities-example.sql[] ----- -==== - -As you can see, there is no SQL `UPDATE` being executed. - -You can also pass the read-only hint to named queries using the Jakarta Persistence {jpaJavadocUrlPrefix}QueryHint.html[`@QueryHint`] annotation. - -[[jpa-read-only-entities-native-example]] -.Fetching read-only entities using a named query and the read-only hint -==== -[source, JAVA, indent=0] ----- -include::{modeldir}/Person.java[tags=jpa-read-only-entities-native-example] ----- -==== - -The Hibernate native API offers a `Query#setReadOnly` method, as an alternative to using a Jakarta Persistence query hint: - -[[hql-read-only-entities-native-example]] -.Read-only entities native query example -==== -[source, JAVA, indent=0] ----- -include::{sourcedir}/HQLTest.java[tags=hql-read-only-entities-native-example] ----- -==== - -[[hql-query-plan-cache]] -=== Entity query plan cache - -Any entity query, be it JPQL or Criteria API, has to be parsed into an AST (Abstract Syntax Tree) so that Hibernate can generate the proper SQL statement. The entity query compilation takes time, and for this reason, Hibernate offers a query plan cache. - -When executing an entity query, Hibernate first checks the plan cache, and only if there's no plan available, a new one will be computed right away. - -The query plan cache can be configured via the following configuration properties: - -`hibernate.query.plan_cache_max_size`:: -This setting gives the maximum number of entries of the plan cache. The default value is 2048. -`hibernate.query.plan_parameter_metadata_max_size`:: -The setting gives the maximum number of `ParameterMetadataImpl` instances maintained by the query plan cache. The `ParameterMetadataImpl` object encapsulates metadata about parameters encountered within a query. The default value is 128. - -Now, if you have many JPQL or Criteria API queries, it's a good idea to increase the query plan cache size so that the vast majority of executing entity queries can skip the compilation phase, therefore reducing execution time. - -To get a better understanding of the query plan cache effectiveness, Hibernate offers several statistics you can use. For more details, check out the <> section. diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/hql/Query.adoc b/documentation/src/main/asciidoc/userguide/chapters/query/hql/Query.adoc new file mode 100644 index 0000000000..856a0e926c --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/query/hql/Query.adoc @@ -0,0 +1,583 @@ +[[hql]] +== HQL and JPQL +:modeldir: ../../../../../../main/java/org/hibernate/userguide/model +:sourcedir: ../../../../../../test/java/org/hibernate/userguide/hql +:extrasdir: extras + +The Hibernate Query Language (HQL) and the Java Persistence Query Language (JPQL) are object-oriented query languages based on SQL and very similar in flavor to SQL. + +[NOTE] +==== +When we use the term "HQL" here, we usually mean both modern HQL, along with the standard subset defined by the specification. +==== + +HQL is not the only way to write queries in Hibernate: + +- <> offer a Java-based API with greater compile-time typesafety, and +- <> are also possible. + +However, HQL is the most convenient option for most people most of the time. + +The actual query language itself is discussed the <>. +This chapter describes the Java APIs for executing HQL and JPQL queries. + +[[hql-getting-started]] +=== HQL query basics + +A query may be provided to Hibernate as either: + +* an _inline query_: the text of the query is passed as a string to the session at runtime, or +* a _named query_: the query is specified in an annotation or XML file, and identified by name at runtime. + +The API for actually executing the query is the same in both cases. + +[TIP] +==== +One big advantage to named queries is that they are parsed by Hibernate at startup time, and so some sorts of errors are reported much earlier. +==== + +[[named-queries]] +==== Declaring named queries + +Named queries may be defined using the Jakarta Persistence annotation `@NamedQuery`. + +[[jpa-read-only-entities-native-example]] +.Declaring a named query with a query hint +==== +[source, JAVA, indent=0] +---- +include::{modeldir}/Person.java[tags=jpa-read-only-entities-native-example] +---- +==== + +Alternatively, Hibernate offers an extended +https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/NamedQuery.html[`@NamedQuery`] annotation +which allows the specification of additional properties of the query, including flush mode, cacheability, and timeout interval, in a more typesafe way. + +[[jpql-api-hibernate-named-query-example]] +.Declaring a named query using the typesafe annotation +==== +[source, JAVA, indent=0] +---- +include::{modeldir}/Phone.java[tags=jpql-api-hibernate-named-query-example, indent=0] +---- +//include::{sourcedir}/HQLTest.java[tags=jpql-api-hibernate-named-query-example, indent=0] +==== + +[[hql-examples-domain-model]] +==== Example domain model + +The code examples featured in this chapter, and the next, make use of the following annotated domain model. + +[[hql-examples-domain-model-example]] +.Examples domain model +==== +[source, JAVA, indent=0] +---- +include::{modeldir}/Person.java[tags=hql-examples-domain-model-example] + +include::{modeldir}/AddressType.java[tags=hql-examples-domain-model-example] + +include::{modeldir}/Partner.java[tags=hql-examples-domain-model-example] + +include::{modeldir}/Phone.java[tags=hql-examples-domain-model-example] + +include::{modeldir}/PhoneType.java[tags=hql-examples-domain-model-example] + +include::{modeldir}/Call.java[tags=hql-examples-domain-model-example] + +include::{modeldir}/Payment.java[tags=hql-examples-domain-model-example] + +include::{modeldir}/CreditCardPayment.java[tags=hql-examples-domain-model-example] + +include::{modeldir}/WireTransferPayment.java[tags=hql-examples-domain-model-example] +---- +==== + +[[query-api]] +==== Flavors of the Query API + +To execute a query, you'll need an instance of the Jakarta Persistence `Query` interface, or, even better, of its subinterface `TypedQuery`. + +[[jpa-query-api]] +===== Jakarta Persistence `Query` and `TypedQuery` + +The `EntityManager` offers various operations that return `Query` or `TypedQuery`, including: + +- `EntityManager#createQuery()`, which accepts a query written in HQL, and +- `EntityManager#createNamedQuery()`, which accepts the name of a named query. + +[TIP] +==== +It's better to explicitly pass the query result type as a Java `Class`. +That way, you'll obtain a `TypedQuery`, and avoid some later typecasting. +==== + +[[jpql-api-example]] +.Obtaining a Jakarta Persistence `Query` or `TypedQuery` reference +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=jpql-api-example] +---- +==== + +[[jpql-api-named-query-example]] +.Obtaining a Jakarta Persistence `Query` or `TypedQuery` reference for a named query +==== +[source, JAVA, indent=0] +---- +include::{modeldir}/Person.java[tags=jpql-api-named-query-example, indent=0] + +include::{sourcedir}/HQLTest.java[tags=jpql-api-named-query-example, indent=0] +---- +==== + +[[hql-query-api]] +===== Hibernate `Query` + +Hibernate's `Session` interface refines the return types of the operations of `EntityManager` which create query objects. + +`Session#createQuery()`, `Session#createNamedQuery()`, and other similar operations all return an instance of the extension `org.hibernate.query.Query`. + +[IMPORTANT] +==== +Some overloaded forms of these operations return a raw type, but in Hibernate 6 all of these have been deprecated, and the use of the raw type `Query` is now strongly discouraged. +Programs should migrate to the use of the typesafe overloads which accept a `Class` object and return a typed `Query`. +==== + +Hibernate's `Query` interface offers additional operations not available via `TypedQuery`, as we'll see below. + +[[hql-api-example]] +.Obtaining a Hibernate `Query` +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-api-example] +---- +==== + +[[hql-api-named-query-example]] +.Obtaining a Hibernate `Query` for a named query +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-api-named-query-example] +---- +==== + +[[query-execution]] +=== Executing HQL and JPQL queries + +Since `org.hibernate.query.Query` inherits `TypedQuery`, which in turn inherits `Query`, usage of the three interfaces is almost identical. + +[[jpql-query-parameters]] +==== Binding arguments to query parameters + +A query may have named parameters or positional parameters: + +* named parameters are specified using the syntax `:name`, and +* positional parameters are specified using the syntax `?1`, `?2`, etc. + +If the query has parameters, arguments must be bound to each parameter before the query is executed. + +[[jpql-api-parameter-example]] +.Named parameter binding +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=jpql-api-parameter-example] +---- +==== + +JPQL-style positional parameters are numbered from `1`. +Just like with named parameters, a positional parameter may appear multiple times in a query. + +[[jpql-api-positional-parameter-example]] +.Positional parameter binding +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=jpql-api-positional-parameter-example] +---- +==== + +[NOTE] +==== +It's not a good idea to mix named and positional parameters in a single query. +==== + +[[jpql-query-execution]] +==== Executing the query + +The `Query` interface is used to control the execution of the query. + +* `Query#getResultList()` is useful when the query might return zero, or more than one result. +* `Query#getSingleResult()` is only for cases where the query always returns exactly one result. + It throws an exception when zero or many results are returned by the database. +* `Query#getResultStream()` allows results to be retrieved incrementally, using a database cursor. + +[[jpql-api-list-example]] +.Executing a query using `getResultList()` +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=jpql-api-list-example] +---- +==== + +[[jpql-api-unique-result-example]] +.Executing a query using `getSingleResult()` +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=jpql-api-single-result-example] +---- +==== + +[[jpql-api-stream-example]] +.Executing a query using `getResultStream()` +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=jpql-api-stream-example] +---- +==== + +[NOTE] +==== +The `getResultStream()` method isn't usually useful. +It's almost always a bad idea to hold open a database cursor. +==== + +[[jpql-pagination]] +==== Pagination and limits + +The very important methods `Query#setMaxResults()` and `Query#setFirstResult()` are used to limit the number of results and control pagination. + +[[jpql-api-basic-usage-example]] +.Limits and pagination +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=jpql-api-basic-usage-example] +---- +==== + +[[jpql-query-hints]] +==== Using query hints to control query execution + +When working with the Jakarta Persistence API, advanced control over query execution is possible via named query hints. +For example, we may want to specify an execution timeout or control caching. + +[[jpql-api-hint-usage-example]] +.`Query` execution using a query hint +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=jpql-api-hint-usage-example] +---- +==== + +Jakarta Persistence defines some standard hints with the prefix `jakarta.persistence`, but most hints are provider specific. +Using provider-specific hints limits your program's portability to only a small degree. + +|=== +| `jakarta.persistence.query.timeout` | Defines the query timeout, in milliseconds. +| `jakarta.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 <>. +| `jakarta.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 <>. +| `org.hibernate.cacheMode` | Defines the `CacheMode` to use. See `org.hibernate.query.Query#setCacheMode`. +| `org.hibernate.cacheable` | Defines whether the query is cacheable. true/false. See `org.hibernate.query.Query#setCacheable`. +| `org.hibernate.cacheRegion` | For queries that are cacheable, defines a specific cache region to use. See `org.hibernate.query.Query#setCacheRegion`. +| `org.hibernate.comment` | Defines the comment to apply to the generated SQL. See `org.hibernate.query.Query#setComment`. +| `org.hibernate.fetchSize` | Defines the JDBC fetch-size to use. See `org.hibernate.query.Query#setFetchSize`. +| `org.hibernate.flushMode` | Defines the Hibernate-specific `FlushMode` to use. See `org.hibernate.query.Query#setFlushMode.` If possible, prefer using `jakarta.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.Query#setReadOnly`. +|=== + +For complete details, see the `Query` {jpaJavadocUrlPrefix}Query.html[Javadocs]. + +[TIP] +==== +For named queries, query hints may be specified using the Jakarta Persistence {jpaJavadocUrlPrefix}QueryHint.html[`@QueryHint`] annotation. +==== + +[[hql-api-execution]] +==== Advanced control over query execution + +When working directly with a Hibernate `Session`, the interface `org.hibernate.Query` is used to control the execution of the query. + +Whereas we needed to specify some information using query hints when working with the Jakarta Persistence API, here we have typesafe setters: + +|=== +| `Query#setTimeout()` | Sets the JDBC-level query timeout. +| `Query#setFetchSize()` | Sets the JDBC-level fetch size. +| `Query#setCacheable()` and `setCacheRegion()` | Control query caching. +| `Query#setCacheMode()` | Overrides the session-level cache mode. +| `Query#setFlushMode()` | Overrides the session-level flush mode. Flushing is covered in detail in <>. +| `Query#setLockMode()` | Overrides the session-level flush mode. Locking is covered in detail in <>. +| `Query#setReadOnly()` | Overrides the session-level default for read-only state. The concept of read-only state is covered in <>. +| `Query#setComment()` | Adds a comment to the generated SQL. +|=== + +For complete details, see the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/Query.html[Query] Javadocs. + +[[hql-api-basic-usage-example]] +.Advanced query control +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-api-basic-usage-example] +---- +==== + +[IMPORTANT] +==== +`addQueryHint()` allows specification of a database query hint. +A hint is added directly to the generated SQL according to `Dialect#getQueryHintString`. + +On the other hand, `setHint()` refers to the Jakarta Persistence notion of a query hint, a hint that targets the provider (Hibernate). +This is a completely different concept. +==== + +[[hql-api-result-transformers]] +=== Query result transformers + +A program may hook into the process of building the query results by providing a `org.hibernate.transform.ResultListTransformer` or `org.hibernate.transform.TupleTransformer`. + +Hibernate provides several some built-in implementations of these interfaces, for example: + +[[hql-api-result-transformers-example]] +.Using a `ResultListTransformer` +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SelectDistinctTest.java[tags=hql-distinct-entity-resulttransformer-example] +---- +==== + +See the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/transform/ResultListTransformer.html[Javadocs] along with the built-in implementations for additional details. + +//[[hql-api-parameters]] +//==== Binding arguments to parameters +// +//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. +// +//[[hql-api-parameter-example]] +//.Hibernate name parameter binding +//==== +//[source, JAVA, indent=0] +//---- +//include::{sourcedir}/HQLTest.java[tags=hql-api-parameter-example] +//---- +//==== +// +//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. +// +//[[hql-api-parameter-inferred-type-example]] +//.Hibernate name parameter binding (inferred type) +//==== +//[source, JAVA, indent=0] +//---- +//include::{sourcedir}/HQLTest.java[tags=hql-api-parameter-inferred-type-example] +//---- +//==== +// +//There are also short hand forms for binding common types such as strings, booleans, integers, etc. +// +//[[hql-api-parameter-short-form-example]] +//.Hibernate name parameter binding (short forms) +//==== +//[source, JAVA, indent=0] +//---- +//include::{sourcedir}/HQLTest.java[tags=hql-api-parameter-short-form-example] +//---- +//==== +// +//[IMPORTANT] +//==== +//Traditionally, Hibernate used to support a JDBC positional parameter syntax form via a `?` symbol without a following ordinal. +// +//There was no way to relate two such positional parameters as being "the same" aside from binding the same value to each and, for this reason, this form is no longer supported. +// +//[source, JAVA, indent=0] +//---- +//include::{sourcedir}/HQLTest.java[tags=hql-api-positional-parameter-example] +//---- +//==== +// +//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. +// +//[[hql-api-list-example]] +//.Hibernate `list()` result +//==== +//[source, JAVA, indent=0] +//---- +//include::{sourcedir}/HQLTest.java[tags=hql-api-list-example] +//---- +//==== +// +//It is also possible to extract a single result from a `Query`. +// +//[[hql-api-unique-result-example]] +//.Hibernate `uniqueResult()` +//==== +//[source, JAVA, indent=0] +//---- +//include::{sourcedir}/HQLTest.java[tags=hql-api-unique-result-example] +//---- +//==== +// +//[NOTE] +//==== +//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 <> for more information on this topic. +//==== +// + +[[hql-read-only-entities]] +==== Querying for read-only entities + +As explained in <>, fetching entities in read-only mode is more efficient than fetching entities whose state changes might need to be written to the database. +Fortunately, even mutable entities may be fetched in read-only mode, with the benefit of reduced memory footprint and of a faster flushing process. + +Read-only entities are skipped by the dirty checking mechanism as illustrated by the following example: + +[[hql-read-only-entities-example]] +.Read-only entities query example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-read-only-entities-example] +---- + +[source, SQL, indent=0] +---- +include::{extrasdir}/hql-read-only-entities-example.sql[] +---- +==== + +In this example, no SQL `UPDATE` was executed. + +The method `Query#setReadOnly()` is an alternative to using a Jakarta Persistence query hint: + +[[hql-read-only-entities-native-example]] +.Read-only entities native query example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-read-only-entities-native-example] +---- +==== + +[[hql-api-incremental]] +=== Scrolling and streaming results + +The `org.hibernate.Query` interface offers two specialized operations for reading query results incrementally, while maintaining an open JDBC `ResultSet` mapped to a server-side cursor. + +[[hql-api-scroll]] +==== Scrollable result sets + +`Query#scroll()` returns a `org.hibernate.ScrollableResults` which wraps an underlying JDBC scrollable `ResultSet`. +Depending on the specified `ScrollMode`, and on the capabilities of the JDBC driver, the `ScrollableResults` may allow navigation of the `ResultSet` in either direction. + +[[hql-api-scroll-example]] +.Scrolling through a `ResultSet` containing entities +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-api-scroll-example] +---- +==== + +If a `ScrollableResults` is left unclosed by the application, Hibernate will automatically close the underlying resources when the transaction ends. +However, it's much better to close the `ResultSet` as soon as possible. + +[IMPORTANT] +==== +Since this method holds the JDBC `ResultSet` open, the program should always close a `ScrollableResults` either explicitly, by calling `close()`, or using a https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html[try-with-resources] block. +==== + +[NOTE] +==== +If you plan to use `Query#scroll` with collection fetching, it's important that your query explicitly order the results so that the JDBC results contain the related rows sequentially. +==== + +[[jpql-api-stream]] +==== Streamed result sets + +Similarly, `getResultStream()` is a specialized operation for reading query results incrementally, while maintaining an open JDBC `ResultSet` mapped to a server-side cursor. + +[IMPORTANT] +==== +The `getResultStream()` method is not just a convenient way to obtain a Java `Stream`. +For that, use `getResultList().stream()`. +==== + +[[hql-api-stream-projection-example]] +.Hibernate `getResultStream()` with a projection result type +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-api-stream-projection-example] +---- +==== + +[[hql-api-stream-example]] +.Hibernate `getResultStream()` with an entity result type +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-api-stream-example] +---- +==== + +Hibernate will automatically close the underlying resources (the JDBC `ResultSet`) when the transaction ends. +However, it's much better to close the `ResultSet` as soon as possible. + +[IMPORTANT] +==== +The program should always close a `Stream` either explicitly, by calling `close()`, or using a https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html[try-with-resources] block. +==== + +There's one exception to this rule: the `Stream` is automatically closed after a terminal operation, as illustrated by the following example: + +[[jpql-api-stream-terminal-operation]] +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=jpql-api-stream-terminal-operation] +---- +==== + +Here, the `Stream` is closed automatically after the `collect()` method is called. + + +[[hql-query-plan-cache]] +=== Entity query plan cache + +Any entity query, be it JPQL or Criteria API, has to be parsed into an AST (Abstract Syntax Tree) so that Hibernate can generate the proper SQL statement. The entity query compilation takes time, and for this reason, Hibernate offers a query plan cache. + +When executing an entity query, Hibernate first checks the plan cache, and only if there's no plan available, a new one will be computed right away. + +The query plan cache can be configured via the following configuration properties: + +`hibernate.query.plan_cache_max_size`:: +This setting gives the maximum number of entries of the plan cache. The default value is 2048. +`hibernate.query.plan_parameter_metadata_max_size`:: +The setting gives the maximum number of `ParameterMetadataImpl` instances maintained by the query plan cache. The `ParameterMetadataImpl` object encapsulates metadata about parameters encountered within a query. The default value is 128. + +Now, if you have many JPQL or Criteria API queries, it's a good idea to increase the query plan cache size so that the vast majority of executing entity queries can skip the compilation phase, therefore reducing execution time. + +To get a better understanding of the query plan cache effectiveness, Hibernate offers several statistics you can use. For more details, check out the <> section. diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc b/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc new file mode 100644 index 0000000000..feb72cf429 --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc @@ -0,0 +1,1666 @@ +[[query-language]] +== Hibernate Query Language +:modeldir: ../../../../../../main/java/org/hibernate/userguide/model +:sourcedir: ../../../../../../test/java/org/hibernate/userguide/hql +:extrasdir: extras + +This chapter describes Hibernate Query Language (HQL) and Jakarta Persistence Query Language (JPQL). + +[NOTE] +==== +JPQL was inspired by early versions of HQL, and is a subset of modern HQL. +Here we focus on describing the complete, more powerful HQL language as it exists today. + +If strict Jakarta Persistence compliance is desired, use the setting `hibernate.jpa.compliance.query=true`. +With this configuration, any attempt to use HQL features beyond the JPQL subset will result in an exception. +We don't recommend the use of this setting. +==== + +HQL (and JPQL) are loosely based on SQL and are easy to learn for anyone familiar with SQL. + +[[hql-case-sensitivity]] +=== Case Sensitivity + +Case sensitivity depends on the language element: + +- keywords and function names are case-insensitive, but +- Java class names, and the names of attributes of Java classes, are case-sensitive. + +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`. + +[NOTE] +==== +This documentation uses lowercase keywords as a convention in query examples. +==== + +[[hql-statement-types]] +=== Statement types + +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 FROM SELECT`. + +[IMPORTANT] +==== +Care should be taken as to when an `UPDATE` or `DELETE` statement is executed. + +[quote, Section 4.10 of the Java Persistence 2.0 Specification] +____ +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 context +or before fetching or accessing entities whose state might be affected by such operations. +____ +==== + +[[hql-select]] +==== Select statements + +The https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form[BNF] for `SELECT` statements in HQL is: + +[[hql-select-bnf-example]] +==== +[source, SQL, indent=0] +---- +include::{extrasdir}/statement_select_bnf.txt[] +---- +==== + +The simplest possible HQL query has no `SELECT` clause at all: + +[[hql-select-simplest-example]] +==== +[source, SQL, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-select-simplest-example] +---- +==== + +[NOTE] +==== +Note that JPQL requires a `select_clause`, whereas HQL does not. +The previous query is equivalent to: + +[source, SQL, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-select-simplest-jpql-example] +---- + +For complicated queries, it's probably best to explicitly specify a `SELECT` list. +==== + +An alternative "simplest possible" select statement has _only_ a `SELECT` list: + +[[hql-select-simplest-example-alt]] +==== +[source, SQL, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-select-simplest-example-alt] +---- +==== + +This results in a SQL `FROM DUAL` query (or equivalent). + +[[hql-update]] +==== Update statements + +The BNF for `UPDATE` statements is the same in HQL and JPQL: + +[[hql-update-bnf-example]] +==== +[source, SQL, indent=0] +---- +include::{extrasdir}/statement_update_bnf.txt[] +---- +==== + +`UPDATE` statements, by default, do not affect 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] +==== +Versioned updates 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 an `update versioned` statement. +==== + +An `UPDATE` statement is executed using the `executeUpdate()` of either `org.hibernate.query.Query` or `jakarta.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 affected by the operation. +This may or may not correlate to the number of rows affected 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". + +[[hql-update-example]] +.UPDATE query statements +==== +[source, SQL, indent=0] +---- +include::{sourcedir}/../batch/BatchTest.java[tags=batch-bulk-jpql-update-example] + +include::{sourcedir}/../batch/BatchTest.java[tags=batch-bulk-hql-update-example] + +include::{sourcedir}/../batch/BatchTest.java[tags=batch-bulk-hql-update-version-example] +---- +==== + +[IMPORTANT] +==== +Neither `UPDATE` nor `DELETE` statements allow implicit joins. Their form already disallows explicit joins too. +==== + +[[hql-delete]] +==== Delete statements + +The BNF for `DELETE` statements is the same in HQL and JPQL: + +[[hql-delete-bnf-example]] +==== +[source, SQL, indent=0] +---- +include::{extrasdir}/statement_delete_bnf.txt[] +---- +==== + +A `DELETE` statement is also executed using the `executeUpdate()` method of either `org.hibernate.query.Query` or `jakarta.persistence.Query`. + +[[hql-insert]] +==== Insert statements + +HQL adds the ability to define `INSERT` statements as well. + +[NOTE] +==== +There is no JPQL equivalent to HQL-style INSERT statements. +==== + +The BNF for an HQL `INSERT` statement is: + +[[hql-insert-bnf-example]] +==== +[source, SQL, indent=0] +---- +include::{extrasdir}/statement_insert_bnf.txt[] +---- +==== + +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 delegate to the database. +This may cause problems between Hibernate Types which are _equivalent_ as opposed to __equal__. +For example, this might lead to issues with mismatches between an attribute mapped as a `org.hibernate.type.StandardBasicTypes.DATE` and an attribute defined as a `org.hibernate.type.StandardBasicTypes.TIMESTAMP`, +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. + +[[hql-insert-example]] +.INSERT query statements +==== +[source, SQL, indent=0] +---- +include::{sourcedir}/../batch/BatchTest.java[tags=batch-bulk-hql-insert-example] +---- +==== + +[[hql-literals]] +=== Literals + +The most important literal value in the language is `null`. + +[[hql-boolean-literals]] +==== Boolean literals + +The boolean literal values are the (case-insensitive) keywords `true` and `false`. + +[[hql-string-literals]] +==== String literals + +String literals are enclosed in single quotes. + +To escape a single quote within a string literal, use a doubled single quote: `''`. + +[[hql-string-literals-example]] +.String literals examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-string-literals-example] +---- +==== + +==== Numeric literals + +Numeric literals come in several different forms. + +[[hql-numeric-literals-example]] +.Numeric literal examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-numeric-literals-example] +---- +==== + +The type of a numeric literal may be specified using a Java-style postfix: +|=== +| Postfix | Type | Java type + +| `L` or `l` | long integer | `long` +| `D` or `d` | double precision | `double` +| `F` or `f` | single precision | `float` +| `BI` or `bi` | large integer | `BigInteger` +| `BD` or `bd` | exact decimal | `BigDecimal` +|=== + +It's not usually necessary to specify the precision explicitly. + +[NOTE] +==== +In a literal with an exponent, the `E` is case-insensitive. +Similarly, the Java-style postfix is case-insensitive. +==== + +Hexadecimal literals may be written using the same syntax as Java: `0X1A2B` or `0x1a2b`. + +[[hql-datetime-literals]] +==== Date and time literals + +According to the JPQL specification, date and time literals may be specified using the JDBC escape syntax. +Since this syntax is rather unpleasant to look at, HQL provides not one, but two alternatives. + +|=== +| Date/time type | Recommended Java type | JDBC escape syntax | Braced literal syntax | Explicitly typed literal syntax + +| Date | `LocalDate` | `{d 'yyyy-mm-dd'}` | `{yyyy-mm-dd}` | `date yyyy-mm-dd` +| Time | `LocalTime` | `{t 'hh:mm'}` | `{hh:mm}` | `time hh:mm` +| Time with seconds | `LocalTime` | `{t 'hh:mm:ss'}` | `{hh:mm:ss}` | `time hh:mm:ss` +| Datetime | `LocalDateTime` | `{ts 'yyyy-mm-dd hh:mm:ss'}` | `{yyyy-mm-dd hh:mm:ss}` | `datetime yyyy-mm-dd hh:mm:ss` +| Datetime with milliseconds | `LocalDateTime` | `{ts 'yyyy-mm-dd hh:mm:ss.millis'}`| `{yyyy-mm-dd hh:mm:ss.millis}` | `datetime yyyy-mm-dd hh:mm:ss.millis` +|=== + +Literals referring to the current date and time are also provided. +Again there is some flexibility. + +|=== +| Date/time type | Java type | Underscore syntax | Spaced syntax + +| Date | `java.time.LocalDate` | `local_date` | `local date` +| Time | `java.time.LocalTime` | `local_time` | `local time` +| Datetime | `java.time.LocalDateTime` | `local_datetime` | `local datetime` +| Offset datetime | `java.time.OffsetDateTime`| `offset_datetime` | `offset datetime` +| Instant | `java.time.Instant` | `instant` | `instant` +| Date | `java.sql.Date` | `current_date` | `current date` +| Time | `java.sql.Time` | `current_time` | `current time` +| Datetime | `java.sql.Timestamp` | `current_timestamp` | `current timestamp` +|=== + +[IMPORTANT] +==== +Of these, only `current_date`, `current_time`, and `current_timestamp` are defined by the JPQL specification. + +However, the use of date and time types from the `java.sql` package is strongly discouraged, so we encourage the use of the HQL extensions. +==== + +[[hql-duration-literals]] +==== Duration literals + +There are two sorts of duration in HQL: + +* year/quarter/month/week/day durations, and +* week/day/hour/minute/second/nanosecond durations. + +Literal duration expressions are of form `n unit`, for example `1 day` or `10 year` or `100 nanosecond`. + +[NOTE] +==== +A HQL duration is considered to map to a Java `java.time.Duration`, but semantically they're perhaps more similar to an ANSI SQL `INTERVAL` type. +==== + +[[hql-binary-literals]] +==== Binary string literals + +HQL also provides a choice of formats for binary strings: + +* the braced syntax `{0xDE, 0xAD, 0xBE, 0xEF}`, a list of Java-style hexadecimal byte literals, or +* the quoted syntax `X'DEADBEEF'` or `x'deadbeef'`, similar to SQL. + +[[hql-enum-literals]] +==== Enum literals + +Literal values of a Java enum must be written with the fully-qualified enum class name, for example, `com.mycompany.Status.OPEN`. + +HQL allows Java `static` constants to be used in the same way, for example, `java.lang.Integer.MAX_VALUE`. + +[[hql-entity-name-literals]] +==== Literal entity names + +Entity names may also occur as a literal value. They do not need to be qualified. See <>. + +[[hql-expressions]] +=== Expressions + +Essentially, expressions are references that resolve to basic or tuple values. + +[[hql-concatenation]] +==== String concatenation + +HQL defines two ways to concatenate strings: + +* the SQL-style concatenation operator, `||`, and +* the JPQL-standard `concat()` function. + +See <> for details of the `concat()` function. + +[[hql-concatenation-example]] +.Concatenation operation example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-concatenation-example] +---- +==== + +Many more operations on strings are defined below, in <>. + +[[hql-numeric-arithmetic]] +==== Numeric arithmetic + +The basic SQL arithmetic operators, `+`,`-`,`*`, and `/` are joined by the remainder operator `%`. + +[[hql-numeric-arithmetic-example]] +.Numeric arithmetic examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-numeric-arithmetic-example] +---- +==== + +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) + +Many more numeric operations are defined below, in <>. + +[[hql-Datetime-arithmetic]] +==== Datetime arithmetic + +Arithmetic involving dates, datetimes, and durations is quite subtle. +Here we list the basic operations. + +|=== +| Operator | Expression type | Example | Resulting type + +| `-` | Difference between two dates | `your.birthday - local date` | year/quarter/month/week/day duration +| `-` | Difference between two datetimes | `local datetime - record.lastUpdated` | week/day/hour/minute/second/nanosecond duration +| `+` | Sum of a date and a year/quarter/month/week/day duration | `local date + 1 week` | date +| `+` | Sum of a datetime and a week/day/hour/minute/second/nanosecond duration | `record.lastUpdated + 1 second` | datetime +| `*` | Product of an integer and a duration | `billing.cycles * 30 day` | duration +| `by unit` | Convert a duration to an integer | `(1 year) by day` | integer +|=== + +The `by unit` operator converts a duration to an integer, for example: `(local date - your.birthday) by day` evaluates to the number of days you still have to wait. + +The function `extract(unit from ...)` extracts a field from a date, time, or datetime type, for example, `extract(year from your.birthday)` produces the year in which you were born, and throws away important information about your birthday. + +[IMPORTANT] +==== +Please carefully note the difference between these two operations: `by` and `extract()` both evaluate to an integer, but they have very different uses. +==== + +Additional datetime operations, including the useful `format()` function, are defined below, in <>. + +[[hql-identification-variable]] +==== Identification variable + +See <>. + +[[hql-path-expressions]] +==== Path expressions + +Again, see <>. + +[[hql-entity-type-exp]] +==== Entity type + +The special function `type()`, applied to an identification variable, evaluates to the entity name of the referenced entity. +This is mainly useful when dealing with entity inheritance hierarchies. + +[[hql-entity-type-exp-example]] +.Entity type expression examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-entity-type-exp-example] +---- +==== + +[[hql-case-expressions]] +==== `CASE` expressions + +Just like in standard SQL, there are two forms of the case expression: + +* the _simple_ case expression, and +* the so-called _searched_ case expression. + +[TIP] +==== +Case expressions are verbose. +It's often simpler to use the `coalesce()`, `nullif()`, or `ifnull()` functions. +==== + +[[hql-simple-case-expressions]] +===== Simple CASE expressions + +The syntax of the simple form is defined by: + +[source, JAVA, indent=0] +---- +include::{extrasdir}/simple_case_bnf.txt[] +---- + +For example: + +[[hql-simple-case-expressions-example]] +.Simple case expression example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-simple-case-expressions-example] +---- +==== + +[[hql-searched-case-expressions]] +===== Searched CASE expressions + +The searched form has the following syntax: + +[[hql-searched-case-expressions-bnf]] +[source, JAVA, indent=0] +---- +include::{extrasdir}/searched_case_bnf.txt[] +---- + +For example: + +[[hql-searched-case-expressions-example]] +.Searched case expression example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-searched-case-expressions-example] +---- +==== + +[[hql-case-arithmetic-expressions]] +===== CASE expressions with arithmetic operations + +If you want to use arithmetic operations in the CASE expressions, you need to wrap the arithmetic operation in parentheses +as illustrated by the following example: + +[[hql-case-arithmetic-expressions-example]] +.Case expression with arithmetic operation example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-case-arithmetic-expressions-example] +---- +==== + +[IMPORTANT] +==== +If the arithmetic expression was not enclosed in parentheses, the parser would not be able to parse the expression. +==== + +[[hql-exp-functions]] +=== Functions + +Both HQL and JPQL define some standard functions that are portable between databases. + +In addition, there are several ways to use a database function that's not known to Hibernate. + +[[jpql-standardized-functions]] +==== JPQL standard functions + +Here we present the list of functions defined by JPQL. + +[TIP] +==== +A program that wishes to remain portable between Jakarta Persistence providers should in principle limit itself to the use of these functions. + +On the other hand, this is an extremely short list. Any nontrivial program will probably need to look beyond it. +==== + +NULLIF:: + +`NULLIF` is an abbreviated `CASE` expression that evaluates to null if its operands are equal. + +[[hql-nullif-example]] +.NULLIF example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-nullif-example] +---- +==== + +COALESCE:: + +`COALESCE` is an abbreviated `CASE` expression that returns the first non-null operand. +We have seen a number of `COALESCE` examples above. + +CONCAT:: +String concatenation function. Variable argument length of 2 or more string values to be concatenated together. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-concat-function-example] +---- +==== + +SUBSTRING:: +Extracts a portion of a string value. +The second argument denotes the starting position, where 1 is the first character of the string. +The third (optional) argument denotes the length. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-substring-function-example] +---- +==== + +UPPER:: +Upper cases the specified string. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-upper-function-example] +---- +==== + +LOWER:: +Lower cases the specified string. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-lower-function-example] +---- +==== + +TRIM:: +Follows the semantics of the SQL trim function. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-trim-function-example] +---- +==== + +LENGTH:: +Returns the length of a string. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-length-function-example] +---- +==== + +LOCATE:: +Locates a string within another string. +The third argument (optional) is used to denote a position from which to start looking. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-locate-function-example] +---- +==== + +ABS:: +The magnitude of a numeric value. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-abs-function-example] +---- +==== + +MOD:: +Calculates the remainder of dividing the first argument by the second. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-mod-function-example] +---- +==== + +SQRT:: +The square root of a numeric value. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-sqrt-function-example] +---- +==== +// +//CURRENT_DATE:: +//Returns the database current date. +// +//==== +//[source, JAVA, indent=0] +//---- +//include::{sourcedir}/HQLTest.java[tags=hql-current-date-function-example] +//---- +//==== +// +//CURRENT_TIME:: +//Returns the database current time. +// +//==== +//[source, JAVA, indent=0] +//---- +//include::{sourcedir}/HQLTest.java[tags=hql-current-time-function-example] +//---- +//==== +// +//CURRENT_TIMESTAMP:: +//Returns the database current timestamp. +// +//==== +//[source, JAVA, indent=0] +//---- +//include::{sourcedir}/HQLTest.java[tags=hql-current-timestamp-function-example] +//---- +//==== + +[[hql-functions]] +==== Important HQL functions + +Beyond the JPQL standardized functions, HQL makes some additional functions available regardless of the underlying database in use. + +CAST:: +A typecast for basic-typed values. + +The target type is an unqualified Java class name: +`String`, `Long`, `Integer`, `Double`, `Float`, `Character`, `Byte`, `BigInteger`, `BigDecimal`, `LocalDate`, `LocalTime`, `LocalDateTime`, etc. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-cast-function-example] +---- +==== + +The function `str(x)` is a synonym for `cast(x as String)`. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-str-function-example] +---- +==== + +EXTRACT:: +Extracts a field of a datetime. + +Field types include: `day`, `month`, `year`, `second`, `minute`, `hour`, `day of week`, `day of month`, `week of year`, `date`, `time` and more. +For a full list of field types, see the Javadoc for https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/query/TemporalUnit.html[`TemporalUnit`]. + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-extract-function-example] +---- +==== + +The following synonyms for `extract()` are also provided: + +|=== +| `year(x)` | `extract(year from x)` +| `month(x)` | `extract(month from x)` +| `day(x)` | `extract(day from x)` +| `hour(x)` | `hour(year from x)` +| `minute(x)` | `minute(year from x)` +| `second(x)` | `second(year from x)` +|=== + +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-year-function-example] +---- +==== + +FORMAT:: +Formats a date, time, or datetime according to a pattern. + +The syntax is `format(datetime as pattern)`, and the pattern must be written in a subset of the pattern language defined by Java's `java.time.format.DateTimeFormatter`. + +For a full list of `format()` pattern elements, see the Javadoc for https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/dialect/Dialect.html#appendDatetimeFormat[`Dialect#appendDatetimeFormat`]. + +[[hql-more-functions]] +==== More HQL functions + +Here we summarize the remaining standard functions defined by HQL. + +|=== +| HQL Function | Purpose | Syntax | Notes on syntax + +| `position()` | Similar to `locate()`. | `position(pattern in string)` | Standard ANSI SQL +| `substring()` | An alternative syntax for JPQL's `substring()`. +| `substring(string from start)`, `substring(string from start for length)` | Standard ANSI SQL +| `overlay()` | For replacing a substring. +| `overlay(string placing replacement from start)`, `overlay(string placing replacement from start for length)` | Standard ANSI SQL +| `pad()` | Pads a string with whitespace, or with a specified character. +| `pad(string with length)`, `pad(string with length leading)`, `pad(string with length trailing)`, or `pad(string with length leading character)` +| Designed to look like `trim()` +| `left()` | The leftmost characters of a string. | `left(string, length)` | Common in SQL dialects +| `right()` | The rightmost characters of a string. | `right(string, length)` | Common in SQL dialects +| `replace()` | Replace every occurrence of a pattern in a string. | `replace(string, pattern, replacement)` | Common in SQL dialects +| `sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()` | Basic trigonometric functions. | `sin(theta)`, `cos(theta)`, `atan2(opposite, adjacent)` | Very common in SQL dialects +| `round()` | Numeric rounding. | As usual: `round(number, places)` | Very common in SQL dialects +| `least()` | Return the smallest of the given arguments. | `least(x, y, z)` | +| `greatest()` | Return the largest of the given arguments. | `greatest(x, y, z)` | +|=== + +There are also several specialized functions for working with collection-valued associations. + +|=== +| HQL Function | Applies to | Purpose + +| `size()` | Any collection | The size of a collection. Results in a subquery. +| `maxelement()` | Collections of basic type | The maximum element as determined by applying the `max()` SQL aggregation. +| `minelement()` | Collections of basic type | The minimum element as determined by applying the `min()` SQL aggregation. +| `maxindex()` | Indexed collections (lists and maps) | The maximum index (key/position) as determined by applying the `max()` SQL aggregation. +| `minindex()` | Indexed collections (lists and maps) | The minimum index (key/position) as determined by applying the `min()` SQL aggregation. +| `elements()` | Any collection | Refers to the elements of a collection as a whole. + +Only allowed in the `WHERE` clause. + +Usually used in conjunction with `all`, `any` or `some` restrictions. +| `indices()` | Indexed collections (lists and maps) | Similar to `elements()` but refers to the collections indices (keys/positions) as a whole. +|=== + +[[hql-collection-expressions-example]] +.Collection-related expressions examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-collection-expressions-example] +---- +==== + +[TIP] +==== +These operations can almost always be written in another way, without the use of these convenience functions. +==== + +See also <>. + +[[hql-user-defined-functions]] +==== Native and user-defined functions + +There are several ways to call native or user-defined SQL functions. + +- A native or user-defined function may be called using JPQL's `function` syntax, for example, ``function('sinh', phi)``. +- A user-written `FunctionContributor` may register user-defined functions. +- A custom `Dialect` may register additional native functions be overriding `initializeFunctionRegistry()`. + +Registering a function isn't hard, but is beyond the scope of this chapter. + +[[hql-conditional-expressions]] +=== Predicates + +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 `NULL` resolve typically to `UNKNOWN`. + +[[hql-relational-comparisons]] +==== Relational comparisons + +Comparisons involve one of the comparison operators: `=`, `>`, `>=`, `<`, `\<=`, `<>`. +HQL also defines `!=` as a comparison operator synonymous with `<>`. +The operands should be of the same type. + +[[hql-relational-comparisons-example]] +.Relational comparison examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-relational-comparisons-example] +---- +==== + +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. + +[[hql-all-subquery-comparison-qualifier-example]] +.ALL subquery comparison qualifier example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-all-subquery-comparison-qualifier-example] +---- +==== + +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. + +[[hql-null-predicate]] +==== Nullness predicate + +It check a value for nullness. +It can be applied to basic attribute references, entity references, and parameters. +HQL additionally allows it to be applied to component/embeddable types. + +[[hql-null-predicate-example]] +.Nullness checking examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-null-predicate-example] +---- +==== + +[[hql-like-predicate]] +==== Like predicate + +Performs a like comparison on string values. The syntax is: + +[[hql-like-predicate-bnf]] +[source, JAVA, indent=0] +---- +include::{extrasdir}/predicate_like_bnf.txt[] +---- + +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. The `_` symbol matches any single character and `%` matches any number of characters. + +[[hql-like-predicate-example]] +.Like predicate examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-like-predicate-example] +---- +==== + +The optional `escape '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 `%`. + +The syntax is formed as follows: `'like_predicate' escape 'escape_symbol'` +So, if `|` is the escape symbol and we want to match all stored procedures prefixed with `Dr_`, the like criteria becomes: `'Dr|_%' escape '|'`: + +[[hql-like-predicate-escape-example]] +.Like with escape symbol +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-like-predicate-escape-example] +---- +==== + +[[hql-ilike-predicate]] +==== Ilike predicate + +Performs a case-insensitive like comparison on string values. The syntax is: + +[[hql-ilike-predicate-bnf]] +[source, JAVA, indent=0] +---- +include::{extrasdir}/predicate_ilike_bnf.txt[] +---- + +The semantics are identical to those of the aforementioned <>, with the sole difference that the comparison is now case insensitive. + +[[hql-between-predicate]] +==== Between predicate + +Analogous to the SQL `BETWEEN` expression, +it checks if the value is within boundaries. +All the operands should have comparable types. + +[[hql-between-predicate-example]] +.Between predicate examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-between-predicate-example] +---- +==== + +[[hql-in-predicate]] +==== In predicate + +`IN` predicates performs a check that a particular value is in a list of values. Its syntax is: + +[[hql-in-predicate-bnf]] +[source, JAVA, indent=0] +---- +include::{extrasdir}/predicate_in_bnf.txt[] +---- + +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, and, 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 <>. + +In HQL, `single_valued_expression` can refer to a far more broad set of expression types. +Single-valued association are allowed, and 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. + +[[hql-in-predicate-example]] +.In predicate examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-in-predicate-example] +---- +==== + +[[hql-exists-predicate]] +==== Exists predicate + +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. + +[[hql-empty-collection-predicate]] +==== Empty collection predicate + +The `IS [NOT] EMPTY` expression applies to collection-valued path expressions. +It checks whether the particular collection has any associated values. + +[[hql-empty-collection-predicate-example]] +.Empty collection expression examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-empty-collection-predicate-example] +---- +==== + +[[hql-member-of-collection-predicate]] +==== Member-of collection predicate + +The `[NOT] MEMBER [OF]` expression applies to collection-valued path expressions. +It checks whether a value is a member of the specified collection. + +[[hql-member-of-collection-predicate-example]] +.Member-of collection expression examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-member-of-collection-predicate-example] +---- +==== + +[[hql-not-predicate]] +==== NOT predicate operator + +The `NOT` operator is used to negate the predicate that follows it. +If that following predicate is true, the NOT resolves to false. + +[NOTE] +==== +If the predicate is true, NOT resolves to false. If the predicate is unknown (e.g. `NULL`), then NOT resolves to unknown as well. +==== + +[[hql-and-predicate]] +==== AND predicate operator + +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. + +[[hql-or-predicate]] +==== OR predicate operator + +The `OR` operator is used to combine 2 predicate expressions. +The result of the OR expression is true if one predicate resolves to true. +If both predicates resolve to unknown, the OR expression resolves to unknown. +Otherwise, the result is false. + +[[hql-from-clause]] +=== The `FROM` clause + +The `FROM` clause is responsible for defining the scope of object model types available to the rest of the query. +It is also responsible for defining all the "identification variables" available to the rest of the query. + +[[hql-identification-variables]] +==== Identification variables + +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 throughout 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. + +[[hql-root-reference]] +==== Root entity references + +A root entity reference, or what Jakarta Persistence 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, as later discussed. + +The BNF for a root entity reference is: + +[[hql-root-reference-bnf-example]] +==== +[source, SQL, indent=0] +---- +include::{extrasdir}/root_entity_ref_bnf.txt[] +---- +==== + +[[hql-root-reference-jpql-fqn-example]] +.Simple query example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-select-simplest-jpql-fqn-example] +---- +==== + +We see that the query is defining a root entity reference to the `org.hibernate.userguide.model.Person` object model type. +Additionally, it declares an alias of `p` to that `org.hibernate.userguide.model.Person` reference, which is the identification variable. + +Usually, the root entity reference represents just the `entity name` rather than the entity class FQN (fully-qualified name). +By default, the entity name is the unqualified entity class name, here `Person`. + +[[hql-root-reference-jpql-example]] +.Simple query using entity name for root entity reference +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-select-simplest-jpql-example] +---- +==== + +Multiple root entity references can also be specified, even when naming the same entity. + +[[hql-multiple-root-reference-jpql-example]] +.Simple query using multiple root entity references +==== +[source, SQL, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-multiple-root-reference-jpql-example] +---- + +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-multiple-same-root-reference-jpql-example] +---- +==== + +[[hql-polymorphism]] +==== Polymorphism + +HQL and JPQL queries are inherently polymorphic. + +[[hql-polymorphism-example]] +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-polymorphism-example, indent=0] +---- +==== + +This query names the `Payment` entity explicitly. +However, all subclasses of `Payment` are also available to the query. +So, if the `CreditCardPayment` and `WireTransferPayment` entities extend the `Payment` class, all three types would be available to the entity query, +and the query would return instances of all three. + + +This behavior can be altered in two ways: + +- by limiting the query to select only from the subclass entity. +- by using either the `org.hibernate.annotations.Polymorphism` annotation (global, and Hibernate-specific). See the <> for more info about this use case. + +[NOTE] +==== +The HQL query `from java.lang.Object` is totally valid (although not very practical from a performance perspective)! + +It returns every object of every entity type defined by your application mappings. +==== + +[[hql-join]] +=== Joins + +Joins allow us to navigate from one entity to another, via its associations, or via explicit join conditions. + +[[hql-explicit-join]] +==== Explicit joins + +The `FROM` clause may have explicit association joins declared using the `join` keyword. +These joins may be either `inner` or `left outer` style joins. + +[[hql-explicit-inner-join-example]] +.Explicit inner join examples +==== +[source, SQL, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-explicit-inner-join-example] +---- +==== + +[[hql-explicit-outer-join-example]] +.Explicit left (outer) join examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-explicit-outer-join-example] +---- +==== + +[[hql-explicit-join-conditions]] +==== Explicit joins with join conditions + +HQL also defines a `WITH` clause to qualify the join conditions. + +[NOTE] +==== +The HQL-style WITH keyword is specific to Hibernate. JPQL defines the `ON` clause for this feature. +==== + +[[hql-explicit-join-with-example]] +.HQL `WITH` clause join example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-explicit-join-with-example] +---- +==== + +[[hql-explicit-join-jpql-on-example]] +.JPQL `ON` clause join example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-explicit-join-jpql-on-example] +---- +==== + +[NOTE] +==== +Conditions occurring in the `WITH` or `ON` clause result in an `ON` clause in the generated SQL. +==== + +Explicit joins may reference associations or attributes of embeddable type. +In the case of embedded attributes, the join is purely logical and does not result in a join in the generated SQL. +For further information about collection-valued association references, see <>. + +[[hql-explicit-fetch-join]] +==== `FETCH JOIN` for association fetching + +An important use case for explicit joins is to define ``FETCH JOIN``s which override the laziness of the joined association. +As an example, given an entity named `Person` with a collection-valued association named `phones`, the `JOIN FETCH` will also load the child collection in the same SQL query: + +[[hql-explicit-fetch-join-example]] +.Fetch join example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-explicit-fetch-join-example] +---- +==== + +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 also wanted to return customers who have no orders. + +Inner joins can also be fetched, but inner joins filter out the root entity. +In the example, using an inner join instead would have resulted in customers without any orders being filtered out of the result. + +[NOTE] +==== +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 not to assign an identification variable to fetched joins except for the purpose of specifying nested fetch joins. + +[IMPORTANT] +==== +Fetch joins should not be used in paged queries (`setFirstResult()` or `setMaxResults()`). + +Nor should they be used with the `scroll()` or `stream()` methods. +==== + +[[hql-implicit-join]] +==== Implicit joins (path expressions) + +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. + +[[hql-implicit-join-example]] +.Simple implicit join example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-implicit-join-example] +---- +==== + +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 `ph` which refers to the `Phone` entity. +The `ph.person` reference then refers to the `person` attribute of the `Phone` entity. +`person` is an association type so we further navigate to its age attribute. + +[NOTE] +==== +If the attribute represents an entity association (non-collection) or a component/embedded, that reference may be further navigated. + +Basic values and collection-valued associations cannot be further navigated. +==== + +As shown in the example, implicit joins often appear outside the `FROM` clause of the HQL query. +However, they always affect the `FROM` clause of the SQL query. + +Note that: + +* 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. + +[[hql-implicit-join-alias-example]] +.Reused implicit join +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-implicit-join-alias-example] +---- +==== + +Just as with explicit joins, implicit joins may reference association or component/embedded attributes. +For further information about collection-valued association references, see <>. + +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. + +[[hql-collection-valued-associations]] +==== Collection member references + +References to collection-valued associations actually refer to the _elements_ of that collection. + +[[hql-collection-valued-associations-example]] +.Collection references example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-collection-valued-associations] +---- +==== + +In the example, the identification variable `ph` actually refers to the object model type `Phone`, which is the type of the elements of the `Person#phones` 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. + +[[hql-collection-qualification]] +==== Collection elements, map keys, and list indexes + +We said earlier that collection-valued path expressions refer to the _elements_ of that collection. +The following functions may be applied to a collection valued path expression to obtain a reference to a list index or map key. + +|=== +| `value()` | Refers to the collection element (or map entry value). +Same as not specifying a qualifier. +Useful to explicitly show intent. +Valid for any type of collection-valued reference. +| `index()` | Applies to any ``List``s with an index column. Refers to the list index. + +(For backward compatibility, Hibernate also allows it as an alternative to ``key()``, to refer to the key of a map entry.) +| `key()` | Applies only to ``Map``s. +Refers to the map's key. If the key is itself an entity, it may be further navigated. +| `entry()` | Applies only to ``Map``s. +Refers to the map's logical `java.util.Map.Entry` pair (the combination of its key and value). +`entry()` is only valid as a terminal path and is allowed in the `SELECT` clause only. +|=== + +[[hql-collection-qualification-example]] +.Qualified collection references example +==== +[source, JAVA, indent=0] +---- +include::{modeldir}/Phone.java[tags=hql-collection-qualification-example, indent=0] + +include::{sourcedir}/HQLTest.java[tags=hql-collection-qualification-example, indent=0] +---- +==== + +An element of and indexed collections (array, list, or map) may even be identified using the index operator: + +[[hql-collection-index-operator-example]] +.Index operator examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-collection-index-operator-example] +---- +==== + +See <> for additional collection-related functions. + +[[hql-select-clause]] +=== The `SELECT` clause + +The `SELECT` list identifies which objects and values to return as the query results. +If there are multiple expressions in the select list, then, by default, each query result is packaged as an array of type `Object[]`. + +Any of the expression types discussed in <> may occur in the select list, unless otherwise noted. + +[[hql-select-new]] +==== `SELECT NEW` + +There's one particular expression type that's only legal in the select clause. +`SELECT NEW` packages the query results into a user-written Java class instead of an array. + +[[hql-select-clause-dynamic-instantiation-example]] +.Query results via `select new` +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/CallStatistics.java[tags=hql-select-clause-dynamic-instantiation-example] + +include::{sourcedir}/HQLTest.java[tags=hql-select-clause-dynamic-instantiation-example, indent=0] +---- +==== + +The projection class must be specified by its fully qualified in the query, and it must have a matching constructor. + +[IMPORTANT] +==== +The class does not need to be mapped or annotated in any way. + +Even if the class _is_ an entity class, the resulting instances are _not_ managed entities associated with the session. +==== + +Alternatively, the query may specify that each result should be packaged as a list or map instead of as an array. +Then the query results are returned as a `List>` or `List>` instead of as a `List`. + +[[hql-select-clause-dynamic-list-instantiation-example]] +.Query results as lists +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-select-clause-dynamic-list-instantiation-example] +---- +==== + +[[hql-select-clause-dynamic-map-instantiation-example]] +.Query results as maps +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-select-clause-dynamic-map-instantiation-example] +---- +==== + +In the case of a map, the keys of the map are defined by the aliases given to the select expressions. +If no aliases are specified, the keys will be column indexes: 0, 1, 2, etc. + +[[hql-distinct]] +==== `DISTINCT` + +The `DISTINCT` keyword removes duplicate results from the query result list. +There are two different ways that duplicate results can arise: + +* when the database-level result set itself contains duplicate rows, or +* when `JOIN FETCH` is used to fetch collections which do not occur in the `SELECT` list. + +Therefore, `DISTINCT` results in removal of duplicates at two different levels: + +1. `DISTINCT` is added to the generated SQL, to remove duplicates from the database result set, and +2. duplicate results are removed by Hibernate in memory _after_ reading the database results and materializing entity instances as Java objects. + +In this example, without `DISTINCT`, there might be duplicate rows in the result set: + +[[hql-distinct-projection-query-example]] +.Using `DISTINCT` to remove duplicate rows +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SelectDistinctTest.java[tags=hql-distinct-projection-query-example] +---- +==== + +With `DISTINCT`, this is the resulting SQL query: + +==== +[source, SQL, indent=0] +---- +include::{extrasdir}/hql-distinct-projection-query-example.sql[] +---- +==== + +In this second example, there are no duplicate rows, but there might be multiple fetched ``Book``s associated with each `Person`: + +[[hql-distinct-entity-query-example]] +.Using DISTINCT to remove duplicate root entity instances +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SelectDistinctTest.java[tags=hql-distinct-entity-query-example] +---- +==== + +For example, suppose there are three ``Person``s in the database and each person has two ``Book``s. + +* Without `DISTINCT` this query would return a list containing six elements, with each ``Person`` instance occurring three times, +since the database-level result set size is given by the total number of joined ``Book``s. +* With `DISTINCT`, the result list has size three, and each ``Person`` instance occurs only once. + +However, the `DISTINCT` keyword is still passed along to the database: + +==== +[source, SQL, indent=0] +---- +include::{extrasdir}/hql-distinct-entity-query-example.sql[] +---- +==== + +In this case, the use of `DISTINCT` in the generated SQL may be undesirable, since it results in redundant sorting of the result set. + +[TIP] +===== +Fortunately, you really don't have to use `DISTINCT` in HQL. +It's really very easy to remove duplicate results from a Java list: + +* using `new HashSet(query.getResultList())`, +* using `query.getResultList().stream().distinct()`, or +* with `query.setResultListTransformer(DistinctResultTransformer.INSTANCE).getResultList()`. +===== + +[[hql-aggregate-functions]] +==== Aggregate functions + +It's common to have aggregate functions like `count()`, `sum()`, and `max()` in a select list. +Aggregate functions are special functions that reduce the size of the result set. + +|=== +| Aggregate function | Result type + +| `count()`, including `count(distinct)`, `count(all)`, and `count(*)` | `Long` +| `avg()` | `Double` +| `min()` | Same as the argument type +| `max()` | Same as the argument type +| `sum()` | Depends on the type of the values being summed +| `any()` | `Boolean` +| `every()` | `Boolean` +|=== + +In the case of `sum()`: + +* 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`. + +Aggregate functions often appear in queries with a `GROUP BY` clause, as described <>. + +[[hql-aggregate-functions-example]] +.Aggregate function examples +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-aggregate-functions-example] +---- +==== + +[[hql-aggregate-functions-filter]] +==== `FILTER` + +All aggregate functions support the inclusion of a _filter clause_, a sort of mini-`WHERE`-clause applying to just one item of the select list: + +[[hql-aggregate-functions-filter-example]] +.Using FILTER with aggregate functions +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-aggregate-functions-simple-filter-example] +---- + +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-aggregate-functions-filter-example] +---- +==== + +[[hql-where-clause]] +=== The `WHERE` clause + +The `WHERE` clause restricts the results returned from a select query or limits the scope of update and delete queries. + +It contains a single predicate. + +[[hql-group-by]] +=== The `GROUP BY` clause + +The `GROUP BY` clause divides the result set into groups, so that a query with aggregate functions in the select list returns not a single result, but one result for each group. +The result set is grouped by the values of expressions that occur in the `GROUP BY` clause. + +As an example, consider the following queries: + +[[hql-group-by-example]] +.Group by example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-group-by-example] +---- +==== + +The first query retrieves the complete total of all orders. +The second retrieves the total for each customer, grouped after grouping the orders by customer. + +[[hql-group-by-rollup-cube]] +==== `ROLLUP` and `CUBE` + +The special functions `rollup()` and `cube()` may be used in the `GROUP BY` clause, when supported by the database. +The semantics are identical to SQL. + +These functions are especially useful for reporting: + +* A `group by` clause with `rollup()` is used to produce subtotals and grand totals. +* A `group by` clause with `cube()` allows totals for every combination of columns. + +[[hql-having]] +=== The `HAVING` clause + +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 <>, we retrieved `Call` duration totals for all persons. +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 total of more than 1000: + +[[hql-group-by-having-example]] +.Having example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-group-by-having-example] +---- +==== + +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, while the `WHERE` clause is applied before. + +[[hql-order-by]] +=== The `ORDER BY` clause + +By default, the results of the query are returned in an arbitrary order. +The `ORDER BY` clause specifies a list of selected items used to order the results. +The types of expressions considered valid as part of the `ORDER BY` clause include: + +* attributes of an entity or embeddable class, +* scalar expressions such as arithmetic operations, functions, etc, and +* identification variables declared in the select clause. + +[NOTE] +==== +The JPQL specification requires that all expressions occurring the `ORDER BY` clause must also occur in the `SELECT` clause. +HQL does not enforce this restriction, but applications desiring database portability should be aware that some databases _do_. +==== + +[[hql-order-by-example]] +.Order by example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/HQLTest.java[tags=hql-order-by-example] +---- +==== + +Each item listed in the `ORDER BY` clause may explicitly specify a direction, either: + +* `ASC` for ascending order, or +* `DESC` for descending order. + +If no direction is explicitly specified, the results are returned in ascending order. + +Of course, there is an ambiguity with respect to null values. +Therefore, the order of null values may also be explicitly specified: + +* `NULLS FIRST` puts null values at the beginning of the result set, and +* `NULLS LAST` puts them last. + +In the next chapter we'll see a completely different way to write queries in Hibernate. diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-entity-query-example.sql b/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-entity-query-example.sql index 55e9dbfc15..13b00ca5fa 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-entity-query-example.sql +++ b/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-entity-query-example.sql @@ -1,11 +1,12 @@ -SELECT DISTINCT - p.id as id1_1_0_, - b.id as id1_0_1_, - p.first_name as first_na2_1_0_, - p.last_name as last_nam3_1_0_, - b.author_id as author_i3_0_1_, - b.title as title2_0_1_, - b.author_id as author_i3_0_0__, - b.id as id1_0_0__ -FROM person p -LEFT OUTER JOIN book b ON p.id=b.author_id \ No newline at end of file + select distinct + p.id, + b.author_id, + b.id, + b.title, + p.first_name, + p.last_name + from + person p + left join + book b + on p.id=b.author_id diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-entity-query-hint-example.sql b/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-entity-query-hint-example.sql index b5c4a118bd..03006d1463 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-entity-query-hint-example.sql +++ b/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-entity-query-hint-example.sql @@ -1,11 +1,12 @@ -SELECT - p.id as id1_1_0_, - b.id as id1_0_1_, - p.first_name as first_na2_1_0_, - p.last_name as last_nam3_1_0_, - b.author_id as author_i3_0_1_, - b.title as title2_0_1_, - b.author_id as author_i3_0_0__, - b.id as id1_0_0__ -FROM person p -LEFT OUTER JOIN book b ON p.id=b.author_id \ No newline at end of file +select + p.id, + b.author_id, + b.id, + b.title, + p.first_name, + p.last_name +from + person p +left join + book b + on p.id=b.author_id \ No newline at end of file diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-projection-query-example.sql b/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-projection-query-example.sql index b0fcbf74e4..ce9cd996aa 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-projection-query-example.sql +++ b/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-distinct-projection-query-example.sql @@ -1,3 +1,4 @@ -SELECT DISTINCT - p.last_name as col_0_0_ -FROM person p \ No newline at end of file +select distinct + p.last_name +from + person p diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-read-only-entities-example.sql b/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-read-only-entities-example.sql index bd63c818a7..8d359df31d 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-read-only-entities-example.sql +++ b/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/hql-read-only-entities-example.sql @@ -1,7 +1,12 @@ -SELECT c.id AS id1_5_ , - c.duration AS duration2_5_ , - c.phone_id AS phone_id4_5_ , - c.call_timestamp AS call_tim3_5_ -FROM phone_call c -INNER JOIN phone p ON c.phone_id = p.id -WHERE p.phone_number = '123-456-7890' \ No newline at end of file +select + c.id, + c.duration, + c.phone_id, + c.call_timestamp +from + phone_call c +join + Phone p + on p.id=c.phone_id +where + p.phone_number='123-456-7890' diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/statement_select_bnf.txt b/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/statement_select_bnf.txt index c0e97e8b3c..0ee93b7c01 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/statement_select_bnf.txt +++ b/documentation/src/main/asciidoc/userguide/chapters/query/hql/extras/statement_select_bnf.txt @@ -1,6 +1,6 @@ select_statement :: = [select_clause] - from_clause + [from_clause] [where_clause] [groupby_clause] [having_clause] diff --git a/documentation/src/main/java/org/hibernate/userguide/model/Call.java b/documentation/src/main/java/org/hibernate/userguide/model/Call.java index 932138aefd..3957e79881 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/Call.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/Call.java @@ -6,6 +6,7 @@ */ package org.hibernate.userguide.model; +import java.time.LocalDateTime; import java.util.Date; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -30,7 +31,7 @@ public class Call { private Phone phone; @Column(name = "call_timestamp") - private Date timestamp; + private LocalDateTime timestamp; private int duration; @@ -55,11 +56,11 @@ public class Call { this.phone = phone; } - public Date getTimestamp() { + public LocalDateTime getTimestamp() { return timestamp; } - public void setTimestamp(Date timestamp) { + public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; } diff --git a/documentation/src/main/java/org/hibernate/userguide/model/Person.java b/documentation/src/main/java/org/hibernate/userguide/model/Person.java index ebe9888f7c..cf9ab42945 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/Person.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/Person.java @@ -6,6 +6,7 @@ */ package org.hibernate.userguide.model; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -42,155 +43,146 @@ import jakarta.persistence.Version; /** * @author Vlad Mihalcea */ -@NamedNativeQueries({ - //tag::sql-scalar-NamedNativeQuery-example[] - @NamedNativeQuery( - name = "find_person_name", - query = - "SELECT name " + - "FROM Person " - ), - //end::sql-scalar-NamedNativeQuery-example[] - //tag::sql-multiple-scalar-values-NamedNativeQuery-example[] - @NamedNativeQuery( - name = "find_person_name_and_nickName", - query = - "SELECT " + - " name, " + - " nickName " + - "FROM Person " - ), - //end::sql-multiple-scalar-values-NamedNativeQuery-example[] - // tag::sql-multiple-scalar-values-dto-NamedNativeQuery-example[] - @NamedNativeQuery( - name = "find_person_name_and_nickName_dto", - query = - "SELECT " + - " name, " + - " nickName " + - "FROM Person ", - resultSetMapping = "name_and_nickName_dto" - ), - //end::sql-multiple-scalar-values-dto-NamedNativeQuery-example[] - //tag::sql-entity-NamedNativeQuery-example[] - @NamedNativeQuery( - name = "find_person_by_name", - query = - "SELECT " + - " p.id AS \"id\", " + - " p.name AS \"name\", " + - " p.nickName AS \"nickName\", " + - " p.address AS \"address\", " + - " p.createdOn AS \"createdOn\", " + - " p.version AS \"version\" " + - "FROM Person p " + - "WHERE p.name LIKE :name", - resultClass = Person.class - ), - //end::sql-entity-NamedNativeQuery-example[] - //tag::sql-entity-associations-NamedNativeQuery-example[] - @NamedNativeQuery( - name = "find_person_with_phones_by_name", - query = - "SELECT " + - " pr.id AS \"pr.id\", " + - " pr.name AS \"pr.name\", " + - " pr.nickName AS \"pr.nickName\", " + - " pr.address AS \"pr.address\", " + - " pr.createdOn AS \"pr.createdOn\", " + - " pr.version AS \"pr.version\", " + - " ph.id AS \"ph.id\", " + - " ph.person_id AS \"ph.person_id\", " + - " ph.phone_number AS \"ph.number\", " + - " ph.phone_type AS \"ph.type\" " + - "FROM Person pr " + - "JOIN Phone ph ON pr.id = ph.person_id " + - "WHERE pr.name LIKE :name", - resultSetMapping = "person_with_phones" +//tag::sql-scalar-NamedNativeQuery-example[] +@NamedNativeQuery( + name = "find_person_name", + query = + "SELECT name " + + "FROM Person " +) +//end::sql-scalar-NamedNativeQuery-example[] +//tag::sql-multiple-scalar-values-NamedNativeQuery-example[] +@NamedNativeQuery( + name = "find_person_name_and_nickName", + query = + "SELECT " + + " name, " + + " nickName " + + "FROM Person " +) +//end::sql-multiple-scalar-values-NamedNativeQuery-example[] +// tag::sql-multiple-scalar-values-dto-NamedNativeQuery-example[] +@NamedNativeQuery( + name = "find_person_name_and_nickName_dto", + query = + "select " + + " name, " + + " nickName " + + "from Person ", + resultSetMapping = "name_and_nickName_dto" +) +//end::sql-multiple-scalar-values-dto-NamedNativeQuery-example[] +//tag::sql-entity-NamedNativeQuery-example[] +@NamedNativeQuery( + name = "find_person_by_name", + query = + "select " + + " p.id AS \"id\", " + + " p.name AS \"name\", " + + " p.nickName AS \"nickName\", " + + " p.address AS \"address\", " + + " p.createdOn AS \"createdOn\", " + + " p.version AS \"version\" " + + "from Person p " + + "where p.name LIKE :name", + resultClass = Person.class +) +//end::sql-entity-NamedNativeQuery-example[] +//tag::sql-entity-associations-NamedNativeQuery-example[] +@NamedNativeQuery( + name = "find_person_with_phones_by_name", + query = + "select " + + " pr.id AS \"pr.id\", " + + " pr.name AS \"pr.name\", " + + " pr.nickName AS \"pr.nickName\", " + + " pr.address AS \"pr.address\", " + + " pr.createdOn AS \"pr.createdOn\", " + + " pr.version AS \"pr.version\", " + + " ph.id AS \"ph.id\", " + + " ph.person_id AS \"ph.person_id\", " + + " ph.phone_number AS \"ph.number\", " + + " ph.phone_type AS \"ph.type\" " + + "from Person pr " + + "join Phone ph ON pr.id = ph.person_id " + + "where pr.name LIKE :name", + resultSetMapping = "person_with_phones" +) +//end::sql-entity-associations-NamedNativeQuery-example[] +//tag::sql-entity-associations-NamedNativeQuery-example[] +@SqlResultSetMapping( + name = "person_with_phones", + entities = { + @EntityResult( + entityClass = Person.class, + fields = { + @FieldResult( name = "id", column = "pr.id" ), + @FieldResult( name = "name", column = "pr.name" ), + @FieldResult( name = "nickName", column = "pr.nickName" ), + @FieldResult( name = "address", column = "pr.address" ), + @FieldResult( name = "createdOn", column = "pr.createdOn" ), + @FieldResult( name = "version", column = "pr.version" ), + } + ), + @EntityResult( + entityClass = Phone.class, + fields = { + @FieldResult( name = "id", column = "ph.id" ), + @FieldResult( name = "person", column = "ph.person_id" ), + @FieldResult( name = "number", column = "ph.number" ), + @FieldResult( name = "type", column = "ph.type" ), + } + ) + } + ) +//end::sql-entity-associations-NamedNativeQuery-example[] +//tag::sql-multiple-scalar-values-dto-NamedNativeQuery-example[] +@SqlResultSetMapping( + name = "name_and_nickName_dto", + classes = @ConstructorResult( + targetClass = PersonNames.class, + columns = { + @ColumnResult(name = "name"), + @ColumnResult(name = "nickName") + } ) - //end::sql-entity-associations-NamedNativeQuery-example[] -}) -@SqlResultSetMappings({ - //tag::sql-entity-associations-NamedNativeQuery-example[] - @SqlResultSetMapping( - name = "person_with_phones", - entities = { - @EntityResult( - entityClass = Person.class, - fields = { - @FieldResult( name = "id", column = "pr.id" ), - @FieldResult( name = "name", column = "pr.name" ), - @FieldResult( name = "nickName", column = "pr.nickName" ), - @FieldResult( name = "address", column = "pr.address" ), - @FieldResult( name = "createdOn", column = "pr.createdOn" ), - @FieldResult( name = "version", column = "pr.version" ), - } - ), - @EntityResult( - entityClass = Phone.class, - fields = { - @FieldResult( name = "id", column = "ph.id" ), - @FieldResult( name = "person", column = "ph.person_id" ), - @FieldResult( name = "number", column = "ph.number" ), - @FieldResult( name = "type", column = "ph.type" ), - } - ) - } - ), - //end::sql-entity-associations-NamedNativeQuery-example[] - //tag::sql-multiple-scalar-values-dto-NamedNativeQuery-example[] - @SqlResultSetMapping( - name = "name_and_nickName_dto", - classes = @ConstructorResult( - targetClass = PersonNames.class, - columns = { - @ColumnResult(name = "name"), - @ColumnResult(name = "nickName") - } - ) - ) - //end::sql-multiple-scalar-values-dto-NamedNativeQuery-example[] -}) +) +//end::sql-multiple-scalar-values-dto-NamedNativeQuery-example[] //tag::hql-examples-domain-model-example[] -@NamedQueries({ - //tag::jpql-api-named-query-example[] - @NamedQuery( - name = "get_person_by_name", - query = "select p from Person p where name = :name" - ) - //end::jpql-api-named-query-example[] - , - // tag::jpa-read-only-entities-native-example[] - @NamedQuery( - name = "get_read_only_person_by_name", - query = "select p from Person p where name = :name", - hints = { - @QueryHint( - name = "org.hibernate.readOnly", - value = "true" - ) - } - ) - //end::jpa-read-only-entities-native-example[] -}) +//tag::jpql-api-named-query-example[] +@NamedQuery( + name = "get_person_by_name", + query = "select p from Person p where name = :name" +) +//end::jpql-api-named-query-example[] +// tag::jpa-read-only-entities-native-example[] +@NamedQuery( + name = "get_read_only_person_by_name", + query = "select p from Person p where name = :name", + hints = { + @QueryHint( + name = "org.hibernate.readOnly", + value = "true" + ) + } +) +//end::jpa-read-only-entities-native-example[] //tag::sql-sp-ref-cursor-oracle-named-query-example[] -@NamedStoredProcedureQueries( - @NamedStoredProcedureQuery( - name = "sp_person_phones", - procedureName = "sp_person_phones", - parameters = { - @StoredProcedureParameter( - name = "personId", - type = Long.class, - mode = ParameterMode.IN - ), - @StoredProcedureParameter( - name = "personPhones", - type = Class.class, - mode = ParameterMode.REF_CURSOR - ) - } - ) +@NamedStoredProcedureQuery( + name = "sp_person_phones", + procedureName = "sp_person_phones", + parameters = { + @StoredProcedureParameter( + name = "personId", + type = Long.class, + mode = ParameterMode.IN + ), + @StoredProcedureParameter( + name = "personPhones", + type = Class.class, + mode = ParameterMode.REF_CURSOR + ) + } ) //end::sql-sp-ref-cursor-oracle-named-query-example[] @Entity @@ -206,8 +198,7 @@ public class Person { private String address; - @Temporal(TemporalType.TIMESTAMP ) - private Date createdOn; + private LocalDateTime createdOn; @OneToMany(mappedBy = "person", cascade = CascadeType.ALL) @OrderColumn(name = "order_id") @@ -254,11 +245,11 @@ public class Person { this.address = address; } - public Date getCreatedOn() { + public LocalDateTime getCreatedOn() { return createdOn; } - public void setCreatedOn(Date createdOn) { + public void setCreatedOn(LocalDateTime createdOn) { this.createdOn = createdOn; } diff --git a/documentation/src/main/java/org/hibernate/userguide/model/Phone.java b/documentation/src/main/java/org/hibernate/userguide/model/Phone.java index 3844f85fb5..7c57a3f93d 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/Phone.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/Phone.java @@ -6,6 +6,7 @@ */ package org.hibernate.userguide.model; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -17,18 +18,14 @@ import jakarta.persistence.ColumnResult; import jakarta.persistence.ConstructorResult; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; -import jakarta.persistence.EntityResult; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.FetchType; -import jakarta.persistence.FieldResult; import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; import jakarta.persistence.MapKey; -import jakarta.persistence.MapKeyTemporal; import jakarta.persistence.OneToMany; import jakarta.persistence.SqlResultSetMapping; -import jakarta.persistence.TemporalType; import org.hibernate.annotations.NamedNativeQueries; import org.hibernate.annotations.NamedNativeQuery; @@ -39,30 +36,26 @@ import org.hibernate.annotations.NamedQuery; * @author Vlad Mihalcea */ //tag::jpql-api-hibernate-named-query-example[] -@NamedQueries({ - @NamedQuery( - name = "get_phone_by_number", - query = "select p " + - "from Phone p " + - "where p.number = :number", - timeout = 1, - readOnly = true - ) -}) +@NamedQuery( + name = "get_phone_by_number", + query = "select p " + + "from Phone p " + + "where p.number = :number", + timeout = 1, + readOnly = true +) //end::jpql-api-hibernate-named-query-example[] //tag::sql-multiple-scalar-values-dto-NamedNativeQuery-hibernate-example[] -@NamedNativeQueries({ - @NamedNativeQuery( - name = "get_person_phone_count", - query = "SELECT pr.name AS name, count(*) AS phoneCount " + - "FROM Phone p " + - "JOIN Person pr ON pr.id = p.person_id " + - "GROUP BY pr.name", - resultSetMapping = "person_phone_count", - timeout = 1, - readOnly = true - ), -}) +@NamedNativeQuery( + name = "get_person_phone_count", + query = "select pr.name AS name, count(*) AS phoneCount " + + "from Phone p " + + "join Person pr ON pr.id = p.person_id " + + "group BY pr.name", + resultSetMapping = "person_phone_count", + timeout = 1, + readOnly = true +) @SqlResultSetMapping( name = "person_phone_count", classes = @ConstructorResult( @@ -97,12 +90,11 @@ public class Phone { //tag::hql-collection-qualification-example[] @OneToMany(mappedBy = "phone") @MapKey(name = "timestamp") - @MapKeyTemporal(TemporalType.TIMESTAMP ) - private Map callHistory = new HashMap<>(); + private Map callHistory = new HashMap<>(); //end::hql-collection-qualification-example[] @ElementCollection - private List repairTimestamps = new ArrayList<>( ); + private List repairTimestamps = new ArrayList<>( ); //Getters and setters are omitted for brevity @@ -145,11 +137,11 @@ public class Phone { return calls; } - public Map getCallHistory() { + public Map getCallHistory() { return callHistory; } - public List getRepairTimestamps() { + public List getRepairTimestamps() { return repairTimestamps; } diff --git a/documentation/src/test/java/org/hibernate/userguide/criteria/CriteriaTest.java b/documentation/src/test/java/org/hibernate/userguide/criteria/CriteriaTest.java index 8acd7eb668..db5b3817b8 100644 --- a/documentation/src/test/java/org/hibernate/userguide/criteria/CriteriaTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/criteria/CriteriaTest.java @@ -7,9 +7,7 @@ package org.hibernate.userguide.criteria; import java.math.BigDecimal; -import java.sql.Timestamp; import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.List; import jakarta.persistence.Entity; import jakarta.persistence.Id; @@ -67,14 +65,14 @@ public class CriteriaTest extends BaseEntityManagerFunctionalTestCase { Person person1 = new Person("John Doe" ); person1.setNickName( "JD" ); person1.setAddress( "Earth" ); - person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ; + person1.setCreatedOn( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ) ; person1.getAddresses().put( AddressType.HOME, "Home address" ); person1.getAddresses().put( AddressType.OFFICE, "Office address" ); entityManager.persist(person1); Person person2 = new Person("Mrs. John Doe" ); person2.setAddress( "Earth" ); - person2.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 2, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ; + person2.setCreatedOn( LocalDateTime.of( 2000, 1, 2, 12, 0, 0 ) ) ; entityManager.persist(person2); Person person3 = new Person("Dr_ John Doe" ); @@ -84,16 +82,16 @@ public class CriteriaTest extends BaseEntityManagerFunctionalTestCase { phone1.setId( 1L ); phone1.setType( PhoneType.MOBILE ); person1.addPhone( phone1 ); - phone1.getRepairTimestamps().add( Timestamp.from( LocalDateTime.of( 2005, 1, 1, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); - phone1.getRepairTimestamps().add( Timestamp.from( LocalDateTime.of( 2006, 1, 1, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + phone1.getRepairTimestamps().add( LocalDateTime.of( 2005, 1, 1, 12, 0, 0 ) ); + phone1.getRepairTimestamps().add( LocalDateTime.of( 2006, 1, 1, 12, 0, 0 ) ); Call call11 = new Call(); call11.setDuration( 12 ); - call11.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + call11.setTimestamp( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ); Call call12 = new Call(); call12.setDuration( 33 ); - call12.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + call12.setTimestamp( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ) ); phone1.addCall(call11); phone1.addCall(call12); diff --git a/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java b/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java index 60d5b43d58..ed5c8641e6 100644 --- a/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java @@ -7,9 +7,9 @@ package org.hibernate.userguide.hql; import java.math.BigDecimal; -import java.sql.Timestamp; +import java.time.Instant; import java.time.LocalDateTime; -import java.time.ZoneOffset; +import java.time.ZoneId; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -18,7 +18,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import jakarta.persistence.FlushModeType; import jakarta.persistence.Query; -import jakarta.persistence.TemporalType; import jakarta.persistence.TypedQuery; import org.hibernate.CacheMode; @@ -33,6 +32,7 @@ import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.TiDBDialect; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; +import org.hibernate.query.QueryProducer; import org.hibernate.type.StandardBasicTypes; import org.hibernate.userguide.model.AddressType; import org.hibernate.userguide.model.Call; @@ -58,6 +58,7 @@ import static org.junit.Assert.fail; /** * @author Vlad Mihalcea */ +@SuppressWarnings("unused") public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Override @@ -77,14 +78,14 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { Person person1 = new Person("John Doe" ); person1.setNickName( "JD" ); person1.setAddress( "Earth" ); - person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ; + person1.setCreatedOn( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ) ; person1.getAddresses().put( AddressType.HOME, "Home address" ); person1.getAddresses().put( AddressType.OFFICE, "Office address" ); entityManager.persist(person1); Person person2 = new Person("Mrs. John Doe" ); person2.setAddress( "Earth" ); - person2.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 2, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ; + person2.setCreatedOn( LocalDateTime.of( 2000, 1, 2, 12, 0, 0 ) ) ; entityManager.persist(person2); Person person3 = new Person("Dr_ John Doe" ); @@ -94,16 +95,16 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { phone1.setId( 1L ); phone1.setType( PhoneType.MOBILE ); person1.addPhone( phone1 ); - phone1.getRepairTimestamps().add( Timestamp.from( LocalDateTime.of( 2005, 1, 1, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); - phone1.getRepairTimestamps().add( Timestamp.from( LocalDateTime.of( 2006, 1, 1, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + phone1.getRepairTimestamps().add( LocalDateTime.of( 2005, 1, 1, 12, 0, 0 ) ); + phone1.getRepairTimestamps().add( LocalDateTime.of( 2006, 1, 1, 12, 0, 0 ) ); Call call11 = new Call(); call11.setDuration( 12 ); - call11.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + call11.setTimestamp( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ); Call call12 = new Call(); call12.setDuration( 33 ); - call12.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + call12.setTimestamp( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ) ); phone1.addCall(call11); phone1.addCall(call12); @@ -140,14 +141,22 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { doInJPA( this::entityManagerFactory, entityManager -> { Session session = entityManager.unwrap( Session.class ); List objects = session.createQuery( - "from java.lang.Object" ) - .list(); + "from java.lang.Object", + Object.class ) + .getResultList(); //tag::hql-select-simplest-example[] List persons = session.createQuery( - "from Person" ) - .list(); + "from Person", Person.class ) + .getResultList(); //end::hql-select-simplest-example[] + + //tag::hql-select-simplest-example-alt[] + LocalDateTime datetime = session.createQuery( + "select local datetime", + LocalDateTime.class ) + .getSingleResult(); + //end::hql-select-simplest-example-alt[] }); } @@ -169,7 +178,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-select-simplest-jpql-fqn-example[] List persons = entityManager.createQuery( "select p " + - "from org.hibernate.userguide.model.Person p", Person.class ) + "from org.hibernate.userguide.model.Person p", + Person.class ) .getResultList(); //end::hql-select-simplest-jpql-fqn-example[] }); @@ -182,7 +192,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select distinct pr, ph " + "from Person pr, Phone ph " + - "where ph.person = pr and ph is not null", Object[].class) + "where ph.person = pr and ph is not null", + Object[].class) .getResultList(); //end::hql-multiple-root-reference-jpql-example[] assertEquals(3, persons.size()); @@ -198,7 +209,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "from Person pr1, Person pr2 " + "where pr1.id <> pr2.id " + " and pr1.address = pr2.address " + - " and pr1.createdOn < pr2.createdOn", Person.class ) + " and pr1.createdOn < pr2.createdOn", + Person.class ) .getResultList(); //end::hql-multiple-same-root-reference-jpql-example[] assertEquals(1, persons.size()); @@ -213,7 +225,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "select distinct pr " + "from Person pr " + "join pr.phones ph " + - "where ph.type = :phoneType", Person.class ) + "where ph.type = :phoneType", + Person.class ) .setParameter( "phoneType", PhoneType.MOBILE ) .getResultList(); //end::hql-explicit-inner-join-example[] @@ -231,7 +244,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "select distinct pr " + "from Person pr " + "inner join pr.phones ph " + - "where ph.type = :phoneType", Person.class ) + "where ph.type = :phoneType", + Person.class ) .setParameter( "phoneType", PhoneType.MOBILE ) .getResultList(); //end::hql-explicit-inner-join-example[] @@ -248,7 +262,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "from Person pr " + "left join pr.phones ph " + "where ph is null " + - " or ph.type = :phoneType", Person.class ) + " or ph.type = :phoneType", + Person.class ) .setParameter( "phoneType", PhoneType.LAND_LINE ) .getResultList(); //end::hql-explicit-outer-join-example[] @@ -267,7 +282,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "from Person pr " + "left outer join pr.phones ph " + "where ph is null " + - " or ph.type = :phoneType", Person.class ) + " or ph.type = :phoneType", + Person.class ) .setParameter( "phoneType", PhoneType.LAND_LINE ) .getResultList(); //end::hql-explicit-outer-join-example[] @@ -284,7 +300,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select distinct pr " + "from Person pr " + - "left join fetch pr.phones ", Person.class ) + "left join fetch pr.phones ", + Person.class ) .getResultList(); //end::hql-explicit-fetch-join-example[] assertEquals(3, persons.size()); @@ -299,9 +316,10 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List personsAndPhones = session.createQuery( "select pr.name, ph.number " + "from Person pr " + - "left join pr.phones ph with ph.type = :phoneType " ) + "left join pr.phones ph with ph.type = :phoneType ", + Object[].class ) .setParameter( "phoneType", PhoneType.LAND_LINE ) - .list(); + .getResultList(); //end::hql-explicit-join-with-example[] assertEquals(4, personsAndPhones.size()); }); @@ -314,7 +332,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List personsAndPhones = entityManager.createQuery( "select pr.name, ph.number " + "from Person pr " + - "left join pr.phones ph on ph.type = :phoneType " ) + "left join pr.phones ph on ph.type = :phoneType ", + Object[].class ) .setParameter( "phoneType", PhoneType.LAND_LINE ) .getResultList(); //end::hql-explicit-join-jpql-on-example[] @@ -330,7 +349,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List phones = entityManager.createQuery( "select ph " + "from Phone ph " + - "where ph.person.address = :address ", Phone.class ) + "where ph.person.address = :address ", + Phone.class ) .setParameter( "address", address ) .getResultList(); //end::hql-implicit-join-example[] @@ -349,7 +369,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "select ph " + "from Phone ph " + "join ph.person pr " + - "where pr.address = :address ", Phone.class ) + "where pr.address = :address ", + Phone.class ) .setParameter( "address", address) .getResultList(); //end::hql-implicit-join-example[] @@ -361,13 +382,14 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { public void test_hql_implicit_join_alias_example_1() { doInJPA( this::entityManagerFactory, entityManager -> { String address = "Earth"; - Date timestamp = new Date(0); + LocalDateTime timestamp = LocalDateTime.ofInstant( Instant.EPOCH, ZoneId.systemDefault() ); //tag::hql-implicit-join-alias-example[] List phones = entityManager.createQuery( "select ph " + "from Phone ph " + "where ph.person.address = :address " + - " and ph.person.createdOn > :timestamp", Phone.class ) + " and ph.person.createdOn > :timestamp", + Phone.class ) .setParameter( "address", address ) .setParameter( "timestamp", timestamp ) .getResultList(); @@ -380,7 +402,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { public void test_hql_implicit_join_alias_example_2() { doInJPA( this::entityManagerFactory, entityManager -> { String address = "Earth"; - Date timestamp = new Date(0); + LocalDateTime timestamp = LocalDateTime.ofInstant( Instant.EPOCH, ZoneId.systemDefault() ); //tag::hql-implicit-join-alias-example[] //same as @@ -389,7 +411,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "from Phone ph " + "inner join ph.person pr " + "where pr.address = :address " + - " and pr.createdOn > :timestamp", Phone.class ) + " and pr.createdOn > :timestamp", + Phone.class ) .setParameter( "address", address ) .setParameter( "timestamp", timestamp ) .getResultList(); @@ -410,7 +433,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "join pr.phones ph " + "join ph.calls c " + "where pr.address = :address " + - " and c.duration > :duration", Phone.class ) + " and c.duration > :duration", + Phone.class ) .setParameter( "address", address ) .setParameter( "duration", duration ) .getResultList(); @@ -422,12 +446,14 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { } @Test + // we do not need to document this syntax, which is a + // legacy of EJB entity beans, prior to JPA / EJB 3 public void test_hql_collection_valued_associations_2() { doInJPA( this::entityManagerFactory, entityManager -> { Session session = entityManager.unwrap( Session.class ); String address = "Earth"; int duration = 20; - //tag::hql-collection-valued-associations[] + //tag::ejb-collection-valued-associations[] // alternate syntax List phones = session.createQuery( @@ -436,11 +462,12 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "in (pr.phones) ph, " + "in (ph.calls) c " + "where pr.address = :address " + - " and c.duration > :duration" ) + " and c.duration > :duration", + Phone.class ) .setParameter( "address", address ) .setParameter( "duration", duration ) - .list(); - //end::hql-collection-valued-associations[] + .getResultList(); + //end::ejb-collection-valued-associations[] assertEquals(1, phones.size()); assertEquals( "123-456-7890", phones.get( 0 ).getNumber() ); }); @@ -458,7 +485,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "select ch " + "from Phone ph " + "join ph.callHistory ch " + - "where ph.id = :id ", Call.class ) + "where ph.id = :id ", + Call.class ) .setParameter( "id", id ) .getResultList(); //end::hql-collection-qualification-example[] @@ -478,7 +506,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "select value(ch) " + "from Phone ph " + "join ph.callHistory ch " + - "where ph.id = :id ", Call.class ) + "where ph.id = :id ", + Call.class ) .setParameter( "id", id ) .getResultList(); //end::hql-collection-qualification-example[] @@ -493,11 +522,12 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-collection-qualification-example[] // select all the Call timestamps (the map key) for a given Phone - List timestamps = entityManager.createQuery( + List timestamps = entityManager.createQuery( "select key(ch) " + "from Phone ph " + "join ph.callHistory ch " + - "where ph.id = :id ", Date.class ) + "where ph.id = :id ", + LocalDateTime.class ) .setParameter( "id", id ) .getResultList(); //end::hql-collection-qualification-example[] @@ -544,7 +574,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "join pr.phones ph " + "join ph.callHistory ch " + "where ph.id = :id " + - " and index(ph) = :phoneIndex", Long.class ) + " and index(ph) = :phoneIndex", + Long.class ) .setParameter( "id", id ) .setParameter( "phoneIndex", phoneIndex ) .getSingleResult(); @@ -567,7 +598,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { TypedQuery typedQuery = entityManager.createQuery( "select p " + "from Person p " + - "where p.name like :name", Person.class + "where p.name like :name", + Person.class ); //end::jpql-api-example[] }); @@ -579,9 +611,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::jpql-api-named-query-example[] Query query = entityManager.createNamedQuery( "get_person_by_name" ); - TypedQuery typedQuery = entityManager.createNamedQuery( - "get_person_by_name", Person.class - ); + TypedQuery typedQuery = entityManager.createNamedQuery( "get_person_by_name", Person.class ); //end::jpql-api-named-query-example[] }); } @@ -601,20 +631,50 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_jpql_api_basic_usage_example() { + int page = 1; doInJPA( this::entityManagerFactory, entityManager -> { //tag::jpql-api-basic-usage-example[] - Query query = entityManager.createQuery( - "select p " + - "from Person p " + - "where p.name like :name" ) - // timeout - in milliseconds - .setHint( "jakarta.persistence.query.timeout", 2000 ) - // flush only at commit time - .setFlushMode( FlushModeType.COMMIT ); + Person person = entityManager.createQuery( + "select p " + + "from Person p " + + "where p.name = :name", + Person.class ) + .setParameter( "name", "John Doe" ) + .setMaxResults(1) + .getSingleResult(); + + List people = entityManager.createQuery( + "select p " + + "from Person p " + + "where p.name like :name", + Person.class ) + .setParameter( "name", "J%" ) + .setFirstResult( page*10 ) + .setMaxResults( 10 ) + .getResultList(); //end::jpql-api-basic-usage-example[] }); } + @Test + public void test_jpql_api_hint_usage_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::jpql-api-hint-usage-example[] + Person query = entityManager.createQuery( + "select p " + + "from Person p " + + "where p.name like :name", + Person.class ) + // timeout - in milliseconds + .setHint( "jakarta.persistence.query.timeout", 2000 ) + // flush only at commit time + .setFlushMode( FlushModeType.COMMIT ) + .setParameter( "name", "J%" ) + .getSingleResult(); + //end::jpql-api-hint-usage-example[] + }); + } + @Test public void test_jpql_api_parameter_example_1() { doInJPA( this::entityManagerFactory, entityManager -> { @@ -631,16 +691,14 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_jpql_api_parameter_example_2() { doInJPA( this::entityManagerFactory, entityManager -> { - Date timestamp = new Date( ); + LocalDateTime timestamp = LocalDateTime.now(); //tag::jpql-api-parameter-example[] - // For generic temporal field types (e.g. `java.util.Date`, `java.util.Calendar`) - // we also need to provide the associated `TemporalType` Query query = entityManager.createQuery( "select p " + "from Person p " + "where p.createdOn > :timestamp" ) - .setParameter( "timestamp", timestamp, TemporalType.DATE ); + .setParameter( "timestamp", timestamp ); //end::jpql-api-parameter-example[] }); } @@ -649,10 +707,11 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { public void test_jpql_api_positional_parameter_example() { doInJPA( this::entityManagerFactory, entityManager -> { //tag::jpql-api-positional-parameter-example[] - Query query = entityManager.createQuery( + TypedQuery query = entityManager.createQuery( "select p " + "from Person p " + - "where p.name like ?1" ) + "where p.name like ?1", + Person.class ) .setParameter( 1, "J%" ); //end::jpql-api-positional-parameter-example[] }); @@ -665,7 +724,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.name like :name" ) + "where p.name like :name", + Person.class ) .setParameter( "name", "J%" ) .getResultList(); //end::jpql-api-list-example[] @@ -679,7 +739,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { try(Stream personStream = entityManager.createQuery( "select p " + "from Person p " + - "where p.name like :name", Person.class ) + "where p.name like :name", + Person.class ) .setParameter( "name", "J%" ) .getResultStream()) { List persons = personStream @@ -693,7 +754,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.name like :name", Person.class ) + "where p.name like :name", + Person.class ) .setParameter( "name", "J%" ) .getResultStream() .skip( 5 ) @@ -721,13 +783,13 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_example() { doInJPA( this::entityManagerFactory, entityManager -> { - Session session = entityManager.unwrap( Session.class ); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-example[] - org.hibernate.query.Query query = session.createQuery( + org.hibernate.query.Query query = session.createQuery( "select p " + "from Person p " + - "where p.name like :name" - ); + "where p.name like :name", + Person.class ); //end::hql-api-example[] }); } @@ -735,9 +797,11 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_named_query_example() { doInJPA( this::entityManagerFactory, entityManager -> { - Session session = entityManager.unwrap( Session.class ); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-named-query-example[] - org.hibernate.query.Query query = session.getNamedQuery( "get_person_by_name" ); + org.hibernate.query.Query query = session.createNamedQuery( + "get_person_by_name", + Person.class ); //end::hql-api-named-query-example[] }); } @@ -745,12 +809,13 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_basic_usage_example() { doInJPA( this::entityManagerFactory, entityManager -> { - Session session = entityManager.unwrap( Session.class ); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-basic-usage-example[] - org.hibernate.query.Query query = session.createQuery( + org.hibernate.query.Query query = session.createQuery( "select p " + "from Person p " + - "where p.name like :name" ) + "where p.name like :name", + Person.class ) // timeout - in seconds .setTimeout( 2 ) // write to L2 caches, but do not read from them @@ -766,12 +831,13 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_parameter_example() { doInJPA( this::entityManagerFactory, entityManager -> { - Session session = entityManager.unwrap( Session.class ); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-parameter-example[] - org.hibernate.query.Query query = session.createQuery( + org.hibernate.query.Query query = session.createQuery( "select p " + "from Person p " + - "where p.name like :name" ) + "where p.name like :name", + Person.class ) .setParameter( "name", "J%", StandardBasicTypes.STRING ); //end::hql-api-parameter-example[] }); @@ -780,12 +846,13 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_parameter_inferred_type_example() { doInJPA( this::entityManagerFactory, entityManager -> { - Session session = entityManager.unwrap( Session.class ); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-parameter-inferred-type-example[] - org.hibernate.query.Query query = session.createQuery( + org.hibernate.query.Query query = session.createQuery( "select p " + "from Person p " + - "where p.name like :name" ) + "where p.name like :name", + Person.class ) .setParameter( "name", "J%" ); //end::hql-api-parameter-inferred-type-example[] }); @@ -794,16 +861,17 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_parameter_short_form_example() { doInJPA( this::entityManagerFactory, entityManager -> { - Date timestamp = new Date( ); - Session session = entityManager.unwrap( Session.class ); + LocalDateTime timestamp = LocalDateTime.now(); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-parameter-short-form-example[] - org.hibernate.query.Query query = session.createQuery( + org.hibernate.query.Query query = session.createQuery( "select p " + "from Person p " + "where p.name like :name " + - " and p.createdOn > :timestamp" ) + " and p.createdOn > :timestamp", + Person.class ) .setParameter( "name", "J%" ) - .setParameter( "timestamp", timestamp, TemporalType.TIMESTAMP); + .setParameter( "timestamp", timestamp ); //end::hql-api-parameter-short-form-example[] }); } @@ -812,12 +880,13 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { public void test_hql_api_positional_parameter_example() { doInJPA( this::entityManagerFactory, entityManager -> { Date timestamp = new Date( ); - Session session = entityManager.unwrap( Session.class ); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-positional-parameter-example[] - org.hibernate.query.Query query = session.createQuery( + org.hibernate.query.Query query = session.createQuery( "select p " + "from Person p " + - "where p.name like ?" ) + "where p.name like ?", + Person.class ) .setParameter( 1, "J%" ); //end::hql-api-positional-parameter-example[] }); @@ -826,14 +895,15 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_list_example() { doInJPA( this::entityManagerFactory, entityManager -> { - Session session = entityManager.unwrap( Session.class ); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-list-example[] List persons = session.createQuery( "select p " + "from Person p " + - "where p.name like :name" ) + "where p.name like :name", + Person.class ) .setParameter( "name", "J%" ) - .list(); + .getResultList(); //end::hql-api-list-example[] }); } @@ -841,14 +911,15 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_stream_example() { doInJPA( this::entityManagerFactory, entityManager -> { - Session session = entityManager.unwrap( Session.class ); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-stream-example[] try( Stream persons = session.createQuery( "select p " + "from Person p " + - "where p.name like :name" ) + "where p.name like :name", + Person.class ) .setParameter( "name", "J%" ) - .stream() ) { + .getResultStream() ) { Map> callRegistry = persons .flatMap( person -> person.getPhones().stream() ) @@ -867,20 +938,17 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_stream_projection_example() { doInJPA( this::entityManagerFactory, entityManager -> { - Session session = entityManager.unwrap( Session.class ); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-stream-projection-example[] try ( Stream persons = session.createQuery( "select p.name, p.nickName " + "from Person p " + - "where p.name like :name" ) + "where p.name like :name", + Object[].class ) .setParameter( "name", "J%" ) - .stream() ) { - - persons - .map( row -> new PersonNames( - (String) row[0], - (String) row[1] ) ) - .forEach( this::process ); + .getResultStream() ) { + persons.map( row -> new PersonNames( (String) row[0], (String) row[1] ) ) + .forEach( this::process ); } //end::hql-api-stream-projection-example[] }); @@ -889,17 +957,18 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_scroll_projection_example() { doInJPA( this::entityManagerFactory, entityManager -> { - Session session = entityManager.unwrap( Session.class ); + QueryProducer session = entityManager.unwrap( Session.class ); //tag::hql-api-scroll-example[] - try ( ScrollableResults scrollableResults = session.createQuery( + try ( ScrollableResults scrollableResults = session.createQuery( "select p " + "from Person p " + - "where p.name like :name" ) + "where p.name like :name", + Person.class ) .setParameter( "name", "J%" ) .scroll() ) { - while(scrollableResults.next()) { - Person person = (Person) scrollableResults.get(); + while ( scrollableResults.next() ) { + Person person = scrollableResults.get(); process(person); } } @@ -909,12 +978,13 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test public void test_hql_api_scroll_open_example() { - ScrollableResults scrollableResults = doInJPA( this::entityManagerFactory, entityManager -> { - Session session = entityManager.unwrap( Session.class ); + ScrollableResults scrollableResults = doInJPA( this::entityManagerFactory, entityManager -> { + QueryProducer session = entityManager.unwrap( Session.class ); return session.createQuery( "select p " + "from Person p " + - "where p.name like :name" ) + "where p.name like :name", + Person.class ) .setParameter( "name", "J%" ) .scroll(); }); @@ -937,12 +1007,13 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { doInJPA( this::entityManagerFactory, entityManager -> { Session session = entityManager.unwrap( Session.class ); //tag::hql-api-unique-result-example[] - Person person = (Person) session.createQuery( + Person person = session.createQuery( "select p " + "from Person p " + - "where p.name like :name" ) + "where p.name like :name", + Person.class ) .setParameter( "name", "J%" ) - .uniqueResult(); + .getSingleResult(); //end::hql-api-unique-result-example[] }); } @@ -954,7 +1025,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.name like 'Joe'", Person.class) + "where p.name like 'Joe'", + Person.class ) .getResultList(); //end::hql-string-literals-example[] }); @@ -969,7 +1041,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.name like 'Joe''s'", Person.class) + "where p.name like 'Joe''s'", + Person.class ) .getResultList(); //end::hql-string-literals-example[] }); @@ -983,7 +1056,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { Person person = entityManager.createQuery( "select p " + "from Person p " + - "where p.id = 1", Person.class) + "where p.id = 1", + Person.class ) .getSingleResult(); //end::hql-numeric-literals-example[] }); @@ -998,7 +1072,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { Person person = entityManager.createQuery( "select p " + "from Person p " + - "where p.id = 1L", Person.class) + "where p.id = 1L", + Person.class ) .getSingleResult(); //end::hql-numeric-literals-example[] }); @@ -1013,7 +1088,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List calls = entityManager.createQuery( "select c " + "from Call c " + - "where c.duration > 100.5", Call.class ) + "where c.duration > 100.5", + Call.class ) .getResultList(); //end::hql-numeric-literals-example[] }); @@ -1028,7 +1104,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List calls = entityManager.createQuery( "select c " + "from Call c " + - "where c.duration > 100.5F", Call.class ) + "where c.duration > 100.5F", + Call.class ) .getResultList(); //end::hql-numeric-literals-example[] }); @@ -1043,7 +1120,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List calls = entityManager.createQuery( "select c " + "from Call c " + - "where c.duration > 1e+2", Call.class ) + "where c.duration > 1e+2", + Call.class ) .getResultList(); //end::hql-numeric-literals-example[] }); @@ -1058,7 +1136,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List calls = entityManager.createQuery( "select c " + "from Call c " + - "where c.duration > 1e+2F", Call.class ) + "where c.duration > 1e+2F", + Call.class ) .getResultList(); //end::hql-numeric-literals-example[] }); @@ -1075,7 +1154,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "from Person pr " + "join pr.phones ph " + "join ph.callHistory ch " + - "where ph.id = 1L ", Long.class ) + "where ph.id = 1L ", + Long.class ) .setParameter( "multiplier", 1000L ) .getSingleResult(); //end::hql-numeric-arithmetic-example[] @@ -1092,7 +1172,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { Integer years = entityManager.createQuery( "select year( current_date() ) - year( p.createdOn ) " + "from Person p " + - "where p.id = 1L", Integer.class ) + "where p.id = 1L", + Integer.class ) .getSingleResult(); //end::hql-numeric-arithmetic-example[] assertTrue(years > 0); @@ -1108,7 +1189,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where year( current_date() ) - year( p.createdOn ) > 1", Person.class ) + "where year( current_date() ) - year( p.createdOn ) > 1", + Person.class ) .getResultList(); //end::hql-numeric-arithmetic-example[] assertTrue(persons.size() > 0); @@ -1122,7 +1204,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { String name = entityManager.createQuery( "select 'Customer ' || p.name " + "from Person p " + - "where p.id = 1", String.class ) + "where p.id = 1", + String.class ) .getSingleResult(); //end::hql-concatenation-example[] assertNotNull(name); @@ -1140,7 +1223,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { " min(c.duration), " + " max(c.duration), " + " avg(c.duration) " + - "from Call c ", Object[].class ) + "from Call c ", + Object[].class ) .getSingleResult(); //end::hql-aggregate-functions-example[] assertNotNull(callStatistics); @@ -1154,7 +1238,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { Long phoneCount = entityManager.createQuery( "select count( distinct c.phone ) " + - "from Call c ", Long.class ) + "from Call c ", + Long.class ) .getSingleResult(); //end::hql-aggregate-functions-example[] assertNotNull(phoneCount); @@ -1170,13 +1255,29 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "select p.number, count(c) " + "from Call c " + "join c.phone p " + - "group by p.number", Object[].class ) + "group by p.number", + Object[].class ) .getResultList(); //end::hql-aggregate-functions-example[] assertNotNull(callCount.get( 0 )); }); } + @Test + public void test_hql_aggregate_functions_simple_filter_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::hql-aggregate-functions-simple-filter-example[] + + List callCount = entityManager.createQuery( + "select count(c) filter(where c.duration < 30) " + + "from Call c ", + Long.class ) + .getResultList(); + //end::hql-aggregate-functions-simple-filter-example[] + assertNotNull(callCount.get( 0 )); + }); + } + @Test public void test_hql_aggregate_functions_filter_example() { doInJPA( this::entityManagerFactory, entityManager -> { @@ -1186,7 +1287,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "select p.number, count(c) filter(where c.duration < 30) " + "from Call c " + "join c.phone p " + - "group by p.number", Object[].class ) + "group by p.number", + Object[].class ) .getResultList(); //end::hql-aggregate-functions-filter-example[] assertNotNull(callCount.get( 0 )); @@ -1201,7 +1303,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List callHistory = entityManager.createQuery( "select concat( p.number, ' : ' , cast(c.duration as string) ) " + "from Call c " + - "join c.phone p", String.class ) + "join c.phone p", + String.class ) .getResultList(); //end::hql-concat-function-example[] assertEquals(2, callHistory.size()); @@ -1215,7 +1318,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List prefixes = entityManager.createQuery( "select substring( p.number, 1, 2 ) " + "from Call c " + - "join c.phone p", String.class ) + "join c.phone p", + String.class ) .getResultList(); //end::hql-substring-function-example[] assertEquals(2, prefixes.size()); @@ -1228,7 +1332,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-upper-function-example[] List names = entityManager.createQuery( "select upper( p.name ) " + - "from Person p ", String.class ) + "from Person p ", + String.class ) .getResultList(); //end::hql-upper-function-example[] assertEquals(3, names.size()); @@ -1241,7 +1346,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-lower-function-example[] List names = entityManager.createQuery( "select lower( p.name ) " + - "from Person p ", String.class ) + "from Person p ", + String.class ) .getResultList(); //end::hql-lower-function-example[] assertEquals(3, names.size()); @@ -1252,12 +1358,20 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { public void test_hql_trim_function_example() { doInJPA( this::entityManagerFactory, entityManager -> { //tag::hql-trim-function-example[] - List names = entityManager.createQuery( + List names1 = entityManager.createQuery( "select trim( p.name ) " + - "from Person p ", String.class ) + "from Person p ", + String.class ) + .getResultList(); + + List names2 = entityManager.createQuery( + "select trim( leading ' ' from p.name ) " + + "from Person p ", + String.class ) .getResultList(); //end::hql-trim-function-example[] - assertEquals(3, names.size()); + assertEquals(3, names1.size()); + assertEquals(3, names2.size()); }); } @@ -1267,7 +1381,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-length-function-example[] List lengths = entityManager.createQuery( "select length( p.name ) " + - "from Person p ", Integer.class ) + "from Person p ", + Integer.class ) .getResultList(); //end::hql-length-function-example[] assertEquals(3, lengths.size()); @@ -1280,7 +1395,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-locate-function-example[] List sizes = entityManager.createQuery( "select locate( 'John', p.name ) " + - "from Person p ", Integer.class ) + "from Person p ", + Integer.class ) .getResultList(); //end::hql-locate-function-example[] assertEquals(3, sizes.size()); @@ -1293,7 +1409,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-abs-function-example[] List abs = entityManager.createQuery( "select abs( c.duration ) " + - "from Call c ", Integer.class ) + "from Call c ", + Integer.class ) .getResultList(); //end::hql-abs-function-example[] assertEquals(2, abs.size()); @@ -1306,7 +1423,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-mod-function-example[] List mods = entityManager.createQuery( "select mod( c.duration, 10 ) " + - "from Call c ", Integer.class ) + "from Call c ", + Integer.class ) .getResultList(); //end::hql-mod-function-example[] assertEquals(2, mods.size()); @@ -1320,7 +1438,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-sqrt-function-example[] List sqrts = entityManager.createQuery( "select sqrt( c.duration ) " + - "from Call c ", Double.class ) + "from Call c ", + Double.class ) .getResultList(); //end::hql-sqrt-function-example[] assertEquals(2, sqrts.size()); @@ -1336,7 +1455,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List calls = entityManager.createQuery( "select c " + "from Call c " + - "where c.timestamp = current_date", Call.class ) + "where c.timestamp = current_date", + Call.class ) .getResultList(); //end::hql-current-date-function-example[] assertEquals(0, calls.size()); @@ -1350,7 +1470,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List calls = entityManager.createQuery( "select c " + "from Call c " + - "where c.timestamp = current_date()", Call.class ) + "where c.timestamp = current_date()", + Call.class ) .getResultList(); assertEquals(0, calls.size()); }); @@ -1364,7 +1485,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List calls = entityManager.createQuery( "select c " + "from Call c " + - "where c.timestamp = current_time", Call.class ) + "where c.timestamp = current_time", + Call.class ) .getResultList(); //end::hql-current-time-function-example[] assertEquals(0, calls.size()); @@ -1378,7 +1500,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List calls = entityManager.createQuery( "select c " + "from Call c " + - "where c.timestamp = current_timestamp", Call.class ) + "where c.timestamp = current_timestamp", + Call.class ) .getResultList(); //end::hql-current-timestamp-function-example[] assertEquals(0, calls.size()); @@ -1394,7 +1517,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-bit-length-function-example[] List bits = entityManager.createQuery( "select bit_length( c.duration ) " + - "from Call c ", Number.class ) + "from Call c ", + Number.class ) .getResultList(); //end::hql-bit-length-function-example[] assertEquals(2, bits.size()); @@ -1407,8 +1531,9 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { doInJPA( this::entityManagerFactory, entityManager -> { //tag::hql-cast-function-example[] List durations = entityManager.createQuery( - "select cast( c.duration as string ) " + - "from Call c ", String.class ) + "select cast( c.duration as String ) " + + "from Call c ", + String.class ) .getResultList(); //end::hql-cast-function-example[] assertEquals(2, durations.size()); @@ -1422,7 +1547,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-extract-function-example[] List years = entityManager.createQuery( "select extract( YEAR from c.timestamp ) " + - "from Call c ", Integer.class ) + "from Call c ", + Integer.class ) .getResultList(); //end::hql-extract-function-example[] assertEquals(2, years.size()); @@ -1435,7 +1561,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-year-function-example[] List years = entityManager.createQuery( "select year( c.timestamp ) " + - "from Call c ", Integer.class ) + "from Call c ", + Integer.class ) .getResultList(); //end::hql-year-function-example[] assertEquals(2, years.size()); @@ -1449,7 +1576,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-str-function-example[] List timestamps = entityManager.createQuery( "select str( c.timestamp ) " + - "from Call c ", String.class ) + "from Call c ", + String.class ) .getResultList(); //end::hql-str-function-example[] assertEquals(2, timestamps.size()); @@ -1464,7 +1592,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { // Special SQL Server function "str" that converts floats List timestamps = entityManager.createQuery( "select str( cast(duration as float) / 60, 4, 2 ) " + - "from Call c ", String.class ) + "from Call c ", + String.class ) .getResultList(); //end::hql-str-function-example[] assertEquals(2, timestamps.size()); @@ -1479,7 +1608,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List phones = entityManager.createQuery( "select p " + "from Phone p " + - "where maxelement( p.calls ) = :call", Phone.class ) + "where maxelement( p.calls ) = :call", + Phone.class ) .setParameter( "call", call ) .getResultList(); //end::hql-collection-expressions-example[] @@ -1496,7 +1626,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List phones = entityManager.createQuery( "select p " + "from Phone p " + - "where minelement( p.calls ) = :call", Phone.class ) + "where minelement( p.calls ) = :call", + Phone.class ) .setParameter( "call", call ) .getResultList(); //end::hql-collection-expressions-example[] @@ -1512,7 +1643,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where maxindex( p.phones ) = 0", Person.class ) + "where maxindex( p.phones ) = 0", + Person.class ) .getResultList(); //end::hql-collection-expressions-example[] assertEquals(1, persons.size()); @@ -1530,7 +1662,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where :phone member of p.phones", Person.class ) + "where :phone member of p.phones", + Person.class ) .setParameter( "phone", phone ) .getResultList(); //end::hql-collection-expressions-example[] @@ -1548,7 +1681,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where :phone = some elements ( p.phones )", Person.class ) + "where :phone = some elements ( p.phones )", + Person.class ) .setParameter( "phone", phone ) .getResultList(); //end::hql-collection-expressions-example[] @@ -1564,7 +1698,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where exists elements ( p.phones )", Person.class ) + "where exists elements ( p.phones )", + Person.class ) .getResultList(); //end::hql-collection-expressions-example[] assertEquals(2, persons.size()); @@ -1580,7 +1715,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List phones = entityManager.createQuery( "select p " + "from Phone p " + - "where current_date() > key( p.callHistory )", Phone.class ) + "where current_date() > key( p.callHistory )", + Phone.class ) .getResultList(); //end::hql-collection-expressions-example[] assertEquals( 1, phones.size() ); @@ -1596,7 +1732,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List phones = entityManager.createQuery( "select p " + "from Phone p " + - "where current_date() > all elements( p.repairTimestamps )", Phone.class ) + "where current_date() > all elements( p.repairTimestamps )", + Phone.class ) .getResultList(); //end::hql-collection-expressions-example[] assertEquals(3, phones.size()); @@ -1611,7 +1748,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where 1 in indices( p.phones )", Person.class ) + "where 1 in indices( p.phones )", + Person.class ) .getResultList(); //end::hql-collection-expressions-example[] assertEquals(1, persons.size()); @@ -1626,7 +1764,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where size( p.phones ) = 2", Person.class ) + "where size( p.phones ) = 2", + Person.class ) .getResultList(); //end::hql-collection-expressions-example[] assertEquals(1, persons.size()); @@ -1641,7 +1780,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.phones[ 0 ].type = 'LAND_LINE'", Person.class ) + "where p.phones[ 0 ].type = 'LAND_LINE'", + Person.class ) .getResultList(); //end::hql-collection-index-operator-example[] assertEquals(1, persons.size()); @@ -1658,7 +1798,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.addresses[ 'HOME' ] = :address", Person.class ) + "where p.addresses[ 'HOME' ] = :address", + Person.class ) .setParameter( "address", address) .getResultList(); //end::hql-collection-index-operator-example[] @@ -1676,7 +1817,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select pr " + "from Person pr " + - "where pr.phones[ maxindex(pr.phones) ].type = 'LAND_LINE'", Person.class ) + "where pr.phones[ maxindex(pr.phones) ].type = 'LAND_LINE'", + Person.class ) .getResultList(); //end::hql-collection-index-operator-example[] assertEquals(1, persons.size()); @@ -1689,7 +1831,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-polymorphism-example[] List payments = entityManager.createQuery( "select p " + - "from Payment p ", Payment.class ) + "from Payment p ", + Payment.class ) .getResultList(); //end::hql-polymorphism-example[] assertEquals(2, payments.size()); @@ -1703,7 +1846,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List payments = entityManager.createQuery( "select p " + "from Payment p " + - "where type(p) = CreditCardPayment", Payment.class ) + "where type(p) = CreditCardPayment", + Payment.class ) .getResultList(); //end::hql-entity-type-exp-example[] assertEquals(1, payments.size()); @@ -1717,7 +1861,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List payments = entityManager.createQuery( "select p " + "from Payment p " + - "where type(p) = :type", Payment.class ) + "where type(p) = :type", + Payment.class ) .setParameter( "type", WireTransferPayment.class) .getResultList(); //end::hql-entity-type-exp-example[] @@ -1736,7 +1881,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { " then '' " + " else p.nickName " + " end " + - "from Person p", String.class ) + "from Person p", + String.class ) .getResultList(); //end::hql-simple-case-expressions-example[] assertEquals(3, nickNames.size()); @@ -1751,7 +1897,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { // same as above List nickNames = entityManager.createQuery( "select coalesce(p.nickName, '') " + - "from Person p", String.class ) + "from Person p", + String.class ) .getResultList(); //end::hql-simple-case-expressions-example[] assertEquals(3, nickNames.size()); @@ -1774,7 +1921,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { " end" + " else p.nickName " + " end " + - "from Person p", String.class ) + "from Person p", + String.class ) .getResultList(); //end::hql-searched-case-expressions-example[] assertEquals(3, nickNames.size()); @@ -1789,7 +1937,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { // coalesce can handle this more succinctly List nickNames = entityManager.createQuery( "select coalesce( p.nickName, p.name, '' ) " + - "from Person p", String.class ) + "from Person p", + String.class ) .getResultList(); //end::hql-searched-case-expressions-example[] assertEquals(3, nickNames.size()); @@ -1807,7 +1956,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { " else p.id " + " end " + "from Person p " + - "order by p.id", Long.class) + "order by p.id", + Long.class) .getResultList(); assertEquals(3, values.size()); @@ -1824,7 +1974,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-nullif-example[] List nickNames = entityManager.createQuery( "select nullif( p.nickName, p.name ) " + - "from Person p", String.class ) + "from Person p", + String.class ) .getResultList(); //end::hql-nullif-example[] assertEquals(3, nickNames.size()); @@ -1844,7 +1995,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { " then null" + " else p.nickName" + " end " + - "from Person p", String.class ) + "from Person p", + String.class ) .getResultList(); //end::hql-nullif-example[] assertEquals(3, nickNames.size()); @@ -1863,7 +2015,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { " max(c.duration), " + " avg(c.duration)" + ") " + - "from Call c ", CallStatistics.class ) + "from Call c ", + CallStatistics.class ) .getSingleResult(); //end::hql-select-clause-dynamic-instantiation-example[] assertNotNull(callStatistics); @@ -1880,7 +2033,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { " c.duration " + ") " + "from Call c " + - "join c.phone p ", List.class ) + "join c.phone p ", + List.class ) .getResultList(); //end::hql-select-clause-dynamic-list-instantiation-example[] assertNotNull(phoneCallDurations); @@ -1900,7 +2054,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { ") " + "from Call c " + "join c.phone p " + - "group by p.number ", Map.class ) + "group by p.number ", + Map.class ) .getResultList(); //end::hql-select-clause-dynamic-map-instantiation-example[] assertNotNull(phoneCallTotalDurations); @@ -1916,7 +2071,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List calls = entityManager.createQuery( "select c " + "from Call c " + - "where c.duration < 30 ", Call.class ) + "where c.duration < 30 ", + Call.class ) .getResultList(); //end::hql-relational-comparisons-example[] assertEquals(1, calls.size()); @@ -1933,7 +2089,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.name like 'John%' ", Person.class ) + "where p.name like 'John%' ", + Person.class ) .getResultList(); //end::hql-relational-comparisons-example[] assertEquals(1, persons.size()); @@ -1953,7 +2110,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.createdOn > '1950-01-01' ", Person.class ) + "where p.createdOn > '1950-01-01' ", + Person.class ) .getResultList(); //end::hql-relational-comparisons-example[] assertEquals(2, persons.size()); @@ -1970,7 +2128,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List phones = entityManager.createQuery( "select p " + "from Phone p " + - "where p.type = 'MOBILE' ", Phone.class ) + "where p.type = 'MOBILE' ", + Phone.class ) .getResultList(); //end::hql-relational-comparisons-example[] assertEquals(1, phones.size()); @@ -1987,7 +2146,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List payments = entityManager.createQuery( "select p " + "from Payment p " + - "where p.completed = true ", Payment.class ) + "where p.completed = true ", + Payment.class ) .getResultList(); //end::hql-relational-comparisons-example[] assertEquals(2, payments.size()); @@ -2004,7 +2164,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List payments = entityManager.createQuery( "select p " + "from Payment p " + - "where type(p) = WireTransferPayment ", Payment.class ) + "where type(p) = WireTransferPayment ", + Payment.class ) .getResultList(); //end::hql-relational-comparisons-example[] assertEquals(1, payments.size()); @@ -2021,7 +2182,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List phonePayments = entityManager.createQuery( "select p " + "from Payment p, Phone ph " + - "where p.person = ph.person ", Object[].class ) + "where p.person = ph.person ", + Object[].class ) .getResultList(); //end::hql-relational-comparisons-example[] assertEquals(2, phonePayments.size()); @@ -2058,7 +2220,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.nickName is not null", Person.class ) + "where p.nickName is not null", + Person.class ) .getResultList(); //end::hql-null-predicate-example[] assertEquals(1, persons.size()); @@ -2075,7 +2238,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.nickName is null", Person.class ) + "where p.nickName is null", + Person.class ) .getResultList(); //end::hql-null-predicate-example[] assertEquals(2, persons.size()); @@ -2090,7 +2254,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.name like 'Jo%'", Person.class ) + "where p.name like 'Jo%'", + Person.class ) .getResultList(); //end::hql-like-predicate-example[] assertEquals(1, persons.size()); @@ -2106,7 +2271,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.name not like 'Jo%'", Person.class ) + "where p.name not like 'Jo%'", + Person.class ) .getResultList(); //end::hql-like-predicate-example[] assertEquals(2, persons.size()); @@ -2122,7 +2288,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.name like 'Dr|_%' escape '|'", Person.class ) + "where p.name like 'Dr|_%' escape '|'", + Person.class ) .getResultList(); //end::hql-like-predicate-escape-example[] assertEquals(1, persons.size()); @@ -2138,7 +2305,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "select p " + "from Person p " + "join p.phones ph " + - "where p.id = 1L and index(ph) between 0 and 3", Person.class ) + "where p.id = 1L and index(ph) between 0 and 3", + Person.class ) .getResultList(); //end::hql-between-predicate-example[] assertEquals(1, persons.size()); @@ -2157,7 +2325,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.createdOn between '1999-01-01' and '2001-01-02'", Person.class ) + "where p.createdOn between '1999-01-01' and '2001-01-02'", + Person.class ) .getResultList(); //end::hql-between-predicate-example[] assertEquals(2, persons.size()); @@ -2173,7 +2342,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List calls = entityManager.createQuery( "select c " + "from Call c " + - "where c.duration between 5 and 20", Call.class ) + "where c.duration between 5 and 20", + Call.class ) .getResultList(); //end::hql-between-predicate-example[] assertEquals(1, calls.size()); @@ -2189,7 +2359,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.name between 'H' and 'M'", Person.class ) + "where p.name between 'H' and 'M'", + Person.class ) .getResultList(); //end::hql-between-predicate-example[] assertEquals(1, persons.size()); @@ -2204,7 +2375,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List payments = entityManager.createQuery( "select p " + "from Payment p " + - "where type(p) in ( CreditCardPayment, WireTransferPayment )", Payment.class ) + "where type(p) in ( CreditCardPayment, WireTransferPayment )", + Payment.class ) .getResultList(); //end::hql-in-predicate-example[] assertEquals(2, payments.size()); @@ -2220,7 +2392,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List phones = entityManager.createQuery( "select p " + "from Phone p " + - "where type in ( 'MOBILE', 'LAND_LINE' )", Phone.class ) + "where type in ( 'MOBILE', 'LAND_LINE' )", + Phone.class ) .getResultList(); //end::hql-in-predicate-example[] assertEquals(3, phones.size()); @@ -2236,7 +2409,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List phones = entityManager.createQuery( "select p " + "from Phone p " + - "where type in :types", Phone.class ) + "where type in :types", + Phone.class ) .setParameter( "types", Arrays.asList( PhoneType.MOBILE, PhoneType.LAND_LINE ) ) .getResultList(); //end::hql-in-predicate-example[] @@ -2257,7 +2431,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { " select py.person.id " + " from Payment py" + " where py.completed = true and py.amount > 50 " + - ")", Phone.class ) + ")", + Phone.class ) .getResultList(); //end::hql-in-predicate-example[] assertEquals(2, phones.size()); @@ -2278,7 +2453,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { " select py.person " + " from Payment py" + " where py.completed = true and py.amount > 50 " + - ")", Phone.class ) + ")", + Phone.class ) .getResultList(); //end::hql-in-predicate-example[] assertEquals(2, phones.size()); @@ -2315,7 +2491,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.phones is empty", Person.class ) + "where p.phones is empty", + Person.class ) .getResultList(); //end::hql-empty-collection-predicate-example[] assertEquals(1, persons.size()); @@ -2331,7 +2508,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where p.phones is not empty", Person.class ) + "where p.phones is not empty", + Person.class ) .getResultList(); //end::hql-empty-collection-predicate-example[] assertEquals(2, persons.size()); @@ -2346,7 +2524,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where 'Home address' member of p.addresses", Person.class ) + "where 'Home address' member of p.addresses", + Person.class ) .getResultList(); //end::hql-member-of-collection-predicate-example[] assertEquals(1, persons.size()); @@ -2362,7 +2541,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "where 'Home address' not member of p.addresses", Person.class ) + "where 'Home address' not member of p.addresses", + Person.class ) .getResultList(); //end::hql-member-of-collection-predicate-example[] assertEquals(2, persons.size()); @@ -2376,7 +2556,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { //tag::hql-group-by-example[] Long totalDuration = entityManager.createQuery( "select sum( c.duration ) " + - "from Call c ", Long.class ) + "from Call c ", + Long.class ) .getSingleResult(); //end::hql-group-by-example[] assertEquals(Long.valueOf( 45 ), totalDuration); @@ -2394,7 +2575,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "from Call c " + "join c.phone ph " + "join ph.person p " + - "group by p.name", Object[].class ) + "group by p.name", + Object[].class ) .getResultList(); //end::hql-group-by-example[] assertEquals(1, personTotalCallDurations.size()); @@ -2413,7 +2595,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "from Call c " + "join c.phone ph " + "join ph.person p " + - "group by p", Object[].class ) + "group by p", + Object[].class ) .getResultList(); //end::hql-group-by-example[] assertEquals(1, personTotalCallDurations.size()); @@ -2427,7 +2610,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { Call call11 = new Call(); call11.setDuration( 10 ); - call11.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + call11.setTimestamp( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ); Phone phone = entityManager.createQuery( "select p from Phone p where p.calls is empty ", Phone.class).getResultList().get( 0 ); @@ -2457,7 +2640,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "join c.phone ph " + "join ph.person p " + "group by p.name " + - "having sum( c.duration ) > 1000", Object[].class ) + "having sum( c.duration ) > 1000", + Object[].class ) .getResultList(); //end::hql-group-by-having-example[] assertEquals(0, personTotalCallDurations.size()); @@ -2472,7 +2656,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { List persons = entityManager.createQuery( "select p " + "from Person p " + - "order by p.name", Person.class ) + "order by p.name", + Person.class ) .getResultList(); //end::hql-order-by-example[] assertEquals(3, persons.size()); @@ -2491,7 +2676,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "join c.phone ph " + "join ph.person p " + "group by p.name " + - "order by total", Object[].class ) + "order by total", + Object[].class ) .getResultList(); //end::hql-order-by-example[] assertEquals(1, personTotalCallDurations.size()); @@ -2507,7 +2693,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "select c " + "from Call c " + "join c.phone p " + - "where p.number = :phoneNumber ", Call.class ) + "where p.number = :phoneNumber ", + Call.class ) .setParameter( "phoneNumber", "123-456-7890" ) .setHint( "org.hibernate.readOnly", true ) .getResultList(); @@ -2526,7 +2713,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { "select c " + "from Call c " + "join c.phone p " + - "where p.number = :phoneNumber ", Call.class ) + "where p.number = :phoneNumber ", + Call.class ) .setParameter( "phoneNumber", "123-456-7890" ) .unwrap( org.hibernate.query.Query.class ) .setReadOnly( true ) diff --git a/documentation/src/test/java/org/hibernate/userguide/hql/SelectDistinctTest.java b/documentation/src/test/java/org/hibernate/userguide/hql/SelectDistinctTest.java index 881ff53e3b..6d119ecc1a 100644 --- a/documentation/src/test/java/org/hibernate/userguide/hql/SelectDistinctTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/hql/SelectDistinctTest.java @@ -22,11 +22,12 @@ import jakarta.persistence.Table; import org.hibernate.jpa.QueryHints; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; +import org.hibernate.transform.DistinctResultTransformer; import org.junit.Before; import org.junit.Test; +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** @@ -65,9 +66,9 @@ public class SelectDistinctTest extends BaseEntityManagerFunctionalTestCase { @Test public void testDistinctProjection() { - doInJPA( this::entityManagerFactory, entityManager -> { + doInJPA( this::entityManagerFactory, entityManager -> { //tag::hql-distinct-projection-query-example[] - List lastNames = entityManager.createQuery( + List lastNames = entityManager.createQuery( "select distinct p.lastName " + "from Person p", String.class) .getResultList(); @@ -143,6 +144,28 @@ public class SelectDistinctTest extends BaseEntityManagerFunctionalTestCase { }); } + @Test + public void testDistinctAuthorsWithResultTransformer() { + + doInHibernate( this::entityManagerFactory, session -> { + //tag::hql-distinct-entity-resulttransformer-example[] + List authors = session.createQuery( + "select p " + + "from Person p " + + "left join fetch p.books", Person.class) + .setResultListTransformer( DistinctResultTransformer.INSTANCE ) + .getResultList(); + //end::hql-distinct-entity-resulttransformer-example[] + + authors.forEach( author -> { + log.infof( "Author %s wrote %d books", + author.getFirstName() + " " + author.getLastName(), + author.getBooks().size() + ); + } ); + }); + } + @Entity(name = "Person") @Table( name = "person") public static class Person { diff --git a/documentation/src/test/java/org/hibernate/userguide/sql/MySQLStoredProcedureTest.java b/documentation/src/test/java/org/hibernate/userguide/sql/MySQLStoredProcedureTest.java index c1b935cd9a..b101c97887 100644 --- a/documentation/src/test/java/org/hibernate/userguide/sql/MySQLStoredProcedureTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/sql/MySQLStoredProcedureTest.java @@ -3,10 +3,8 @@ package org.hibernate.userguide.sql; import java.sql.CallableStatement; import java.sql.SQLException; import java.sql.Statement; -import java.sql.Timestamp; import java.sql.Types; import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; @@ -104,7 +102,7 @@ public class MySQLStoredProcedureTest extends BaseEntityManagerFunctionalTestCas Person person1 = new Person("John Doe" ); person1.setNickName( "JD" ); person1.setAddress( "Earth" ); - person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ; + person1.setCreatedOn( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ) ; person1.getAddresses().put( AddressType.HOME, "Home address" ); person1.getAddresses().put( AddressType.OFFICE, "Office address" ); diff --git a/documentation/src/test/java/org/hibernate/userguide/sql/OracleStoredProcedureTest.java b/documentation/src/test/java/org/hibernate/userguide/sql/OracleStoredProcedureTest.java index 3ec540ecb8..ca20cf6e45 100644 --- a/documentation/src/test/java/org/hibernate/userguide/sql/OracleStoredProcedureTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/sql/OracleStoredProcedureTest.java @@ -100,7 +100,7 @@ public class OracleStoredProcedureTest extends BaseEntityManagerFunctionalTestCa Person person1 = new Person("John Doe" ); person1.setNickName( "JD" ); person1.setAddress( "Earth" ); - person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ; + person1.setCreatedOn( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ) ; person1.getAddresses().put( AddressType.HOME, "Home address" ); person1.getAddresses().put( AddressType.OFFICE, "Office address" ); diff --git a/documentation/src/test/java/org/hibernate/userguide/sql/SQLTest.java b/documentation/src/test/java/org/hibernate/userguide/sql/SQLTest.java index da3279dfb2..c324662059 100644 --- a/documentation/src/test/java/org/hibernate/userguide/sql/SQLTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/sql/SQLTest.java @@ -7,9 +7,7 @@ package org.hibernate.userguide.sql; import java.math.BigDecimal; -import java.sql.Timestamp; import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.List; import jakarta.persistence.PersistenceException; @@ -70,14 +68,14 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase { Person person1 = new Person("John Doe" ); person1.setNickName( "JD" ); person1.setAddress( "Earth" ); - person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ; + person1.setCreatedOn( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ) ; person1.getAddresses().put( AddressType.HOME, "Home address" ); person1.getAddresses().put( AddressType.OFFICE, "Office address" ); entityManager.persist(person1); Person person2 = new Person("Mrs. John Doe" ); person2.setAddress( "Earth" ); - person2.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 2, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ; + person2.setCreatedOn( LocalDateTime.of( 2000, 1, 2, 12, 0, 0 ) ) ; entityManager.persist(person2); Person person3 = new Person("Dr_ John Doe" ); @@ -87,16 +85,16 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase { phone1.setId( 1L ); phone1.setType( PhoneType.MOBILE ); person1.addPhone( phone1 ); - phone1.getRepairTimestamps().add( Timestamp.from( LocalDateTime.of( 2005, 1, 1, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); - phone1.getRepairTimestamps().add( Timestamp.from( LocalDateTime.of( 2006, 1, 1, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + phone1.getRepairTimestamps().add( LocalDateTime.of( 2005, 1, 1, 12, 0, 0 ) ); + phone1.getRepairTimestamps().add( LocalDateTime.of( 2006, 1, 1, 12, 0, 0 ) ); Call call11 = new Call(); call11.setDuration( 12 ); - call11.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + call11.setTimestamp( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ); Call call12 = new Call(); call12.setDuration( 33 ); - call12.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + call12.setTimestamp( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ) ); phone1.addCall(call11); phone1.addCall(call12);