Skip to content

Commit 83113ed

Browse files
committed
improve new query execution chapter
1 parent 978c94a commit 83113ed

File tree

1 file changed

+74
-28
lines changed

1 file changed

+74
-28
lines changed

documentation/src/main/asciidoc/introduction/Querying.adoc

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ Selection queries usually start with the keyword `select` or `from`, whereas mut
3434
|===
3535
| Kind | `Session` method | `EntityManager` method | `Query` execution method
3636

37-
| Selection | `createSelectionQuery(String,Class)` | `createQuery(String,Class)` | `getResultList()`, `getSingleResult()`, or `getSingleResultOrNull()`
38-
| Mutation | `createMutationQuery(String)` | `createQuery(String)` | `executeUpdate()`
37+
| Selection | link:{doc-javadoc-url}org/hibernate/query/QueryProducer.html#createSelectionQuery(java.lang.String,java.lang.Class)[`createSelectionQuery(String,Class)`] | `createQuery(String,Class)` | `getResultList()`, `getSingleResult()`, or `getSingleResultOrNull()`
38+
| Mutation | link:{doc-javadoc-url}org/hibernate/query/QueryProducer.html#createMutationQuery(java.lang.String)[`createMutationQuery(String)`] | `createQuery(String)` | `executeUpdate()`
3939
|===
4040

4141
So for the `Session` API we would write:
@@ -58,11 +58,41 @@ List<Book> matchingBooks =
5858
.getResultList();
5959
----
6060

61-
The only difference between `createSelectionQuery()` and `createQuery()` is that `createSelectionQuery()` throws an exception if passed an `insert`, `delete`, or `update`.
61+
The main difference between `createSelectionQuery()` and `createQuery()` is that `createSelectionQuery()` throws an exception if passed a query string that begins with `insert`, `delete`, or `update`.
6262

63-
In the query above, `:titleSearchPattern` is called a _named parameter_.
64-
We may also identify parameters by a number.
65-
These are called _ordinal parameters_.
63+
We've been using link:{doc-javadoc-url}org/hibernate/query/SelectionQuery.html#getResultList()[`getResultList()`] because we've been expecting our queries to return multiple results.
64+
If we're expecting a query to return a single result, we can use link:{doc-javadoc-url}org/hibernate/query/SelectionQuery.html#getSingleResult()[`getSingleResult()`].
65+
66+
[source,java]
67+
----
68+
Book book =
69+
session.createSelectionQuery("from Book where isbn = ?1", Book.class)
70+
.setParameter(1, isbn)
71+
.getSingleResult();
72+
----
73+
74+
Or, if we're expecting it to return at most one result, we can use link:{doc-javadoc-url}org/hibernate/query/SelectionQuery.html#getSingleResultOrNull()[`getSingleResultOrNull()`].
75+
76+
[source,java]
77+
----
78+
Book bookOrNull =
79+
session.createSelectionQuery("from Book where isbn = ?1", Book.class)
80+
.setParameter(1, isbn)
81+
.getSingleResultOrNull();
82+
----
83+
84+
The difference, of course, is that `getSingleResult()` throws an exception if there's no matching row in the database, whereas `getSingleResultOrNull()` just returns `null`.
85+
86+
To execute a `MutationQuery`, we use link:{doc-javadoc-url}org/hibernate/query/MutationQuery.html#executeUpdate()[`executeUpdate()`], which returns the number of entities affected by the `insert`, `update`, or `delete`.
87+
88+
=== Query parameters
89+
90+
Queries are often parameterized.
91+
92+
- In the query above, `:titleSearchPattern` is called a _named parameter_.
93+
- Alternatively, we may label a parameter by a number. Such a parameter is called an _ordinal parameter_.
94+
95+
We may easily rewrite our query to use an ordinal parameter:
6696

6797
[source,java]
6898
----
@@ -81,45 +111,60 @@ _Never_ concatenate user input with HQL and pass the concatenated string to `cre
81111
This would open up the possibility for an attacker to execute arbitrary code on your database server.
82112
====
83113

84-
If we're expecting a query to return a single result, we can use `getSingleResult()`.
114+
The link:{doc-javadoc-url}org/hibernate/query/CommonQueryContract.html#setParameter(java.lang.String,java.lang.Object)[`setParameter()`] methods specify arguments to query parameters.
115+
116+
[TIP]
117+
====
118+
The two-argument forms of `setParameter()` are perfect for most purposes, but _very occasionally_ it's necessary to resolve an ambiguity in the interpretation of the argument value by explicitly specifying the <<compositional-basic-types,type>> of the argument.
119+
The best way to identify the type is via a reference to a JPA metamodel `Type`.
120+
There are two ways to do this:
121+
122+
// - by passing a Java `Class` as a third argument to link:{doc-javadoc-url}org/hibernate/query/CommonQueryContract.html#setParameter(java.lang.String,java.lang.Object,java.lang.Class)[`setParameter()`] (this doesn't usually help very much),
123+
- by passing the `Type` as a third argument to link:{doc-javadoc-url}org/hibernate/query/CommonQueryContract.html#setParameter(java.lang.String,java.lang.Object,jakarta.persistence.metamodel.Type)[`setParameter()`], or
124+
- by packaging the argument and its `Type` in a link:{doc-javadoc-url}org/hibernate/query/TypedParameterValue.html[`TypedParameterValue`].
125+
126+
For example, we may pass a <<static-metamodel,static metamodel>> reference to `setParameter()`.
85127
86128
[source,java]
87129
----
88-
Book book =
89-
session.createSelectionQuery("from Book where isbn = ?1", Book.class)
90-
.setParameter(1, isbn)
91-
.getSingleResult();
130+
session.createSelectionQuery("from Person where address = :address")
131+
.setParameter("address" address, Person_.address.getType())
132+
.getResultList();
92133
----
134+
====
135+
136+
=== Auto-flush
137+
138+
By default, Hibernate dirty checks entities in the persistence context before executing a query, in order to determine if there are changes which have not yet been flushed to the database, but which might affect the results of the query.
139+
If there are unflushed changes, then Hibernate goes ahead and executes an automatic <<flush,flush>> before executing the query.
140+
That way, the query won't return stale results which fail to reflect changes made to data within the current unit of work.
141+
But if there are many entities association with the persistence context, then this can be an expensive operation.
93142

94-
Or, if we're expecting it to return at most one result, we can use `getSingleResultOrNull()`.
143+
To disable this behavior, set the link:{doc-javadoc-url}org/hibernate/query/QueryFlushMode.html[query flush mode] to `NO_FLUSH`:
95144

96145
[source,java]
97146
----
98147
Book bookOrNull =
99148
session.createSelectionQuery("from Book where isbn = ?1", Book.class)
100149
.setParameter(1, isbn)
101-
.getSingleResultOrNull();
150+
.setQueryFlushMode(QueryFlushMode.NO_FLUSH)
151+
.getSingleResult();
102152
----
103153

104-
The difference, of course, is that `getSingleResult()` throws an exception if there's no matching row in the database, whereas `getSingleResultOrNull()` just returns `null`.
105-
106-
By default, Hibernate dirty checks entities in the persistence context before executing a query, in order to determine if the session should be flushed.
107-
If there are many entities association with the persistence context, then this can be an expensive operation.
108-
109-
To disable this behavior, set the flush mode to `COMMIT` or `MANUAL`:
154+
Or, especially if you're using JPA-standard APIs, use `FlushModeType.COMMIT`:
110155

111156
[source,java]
112157
----
113158
Book bookOrNull =
114159
session.createSelectionQuery("from Book where isbn = ?1", Book.class)
115160
.setParameter(1, isbn)
116-
.setHibernateFlushMode(MANUAL)
161+
.setFlushMode(FlushModeType.COMMIT)
117162
.getSingleResult();
118163
----
119164

120165
[CAUTION]
121166
====
122-
Setting the flush mode to `COMMIT` or `MANUAL` might cause the query to return stale results.
167+
Setting the flush mode to `NO_FLUSH`, `COMMIT`, or `MANUAL` might cause the query to return stale results.
123168
====
124169

125170
Occasionally we need to build a query at runtime, from a set of optional conditions.
@@ -178,6 +223,7 @@ HibernateCriteriaBuilder builder =
178223

179224
We're ready to create a criteria query.
180225

226+
[[criteria-query-example]]
181227
[source,java]
182228
----
183229
CriteriaQuery<Book> query = builder.createQuery(Book.class);
@@ -210,8 +256,8 @@ Execution of a criteria query works almost exactly like execution of HQL.
210256
|===
211257
| Kind | `Session` method | `EntityManager` method | `Query` execution method
212258

213-
| Selection | `createSelectionQuery(CriteriaQuery)` | `createQuery(CriteriaQuery)` | `getResultList()`, `getSingleResult()`, or `getSingleResultOrNull()`
214-
| Mutation | `createMutationQuery(CriteriaUpdate)` or `createMutationQuery(CriteriaDelete)` | `createQuery(CriteriaUpdate)` or `createQuery(CriteriaDelte)` | `executeUpdate()`
259+
| Selection | link:{doc-javadoc-url}org/hibernate/query/QueryProducer.html#createSelectionQuery(jakarta.persistence.criteria.CriteriaQuery)[`createSelectionQuery(CriteriaQuery)`] | `createQuery(CriteriaQuery)` | `getResultList()`, `getSingleResult()`, or `getSingleResultOrNull()`
260+
| Mutation | link:{doc-javadoc-url}org/hibernate/query/QueryProducer.html#createMutationQuery(jakarta.persistence.criteria.CriteriaUpdate)[`createMutationQuery(CriteriaUpdate)`] or link:{doc-javadoc-url}org/hibernate/query/QueryProducer.html#createMutationQuery(jakarta.persistence.criteria.CriteriaDelete)[`createMutationQuery(CriteriaDelete)`] |`createQuery(CriteriaUpdate)` or `createQuery(CriteriaDelete)` | `executeUpdate()`
215261
|===
216262

217263
For example:
@@ -261,8 +307,8 @@ The reason it works this way is that each JPA provider has its own implementatio
261307
// [%unbreakable]
262308
// [TIP]
263309
// ====
264-
Hibernate 6.3 introduces the helper class link:{doc-javadoc-url}org/hibernate/query/criteria/CriteriaDefinition.html[`CriteriaDefinition`] to reduce the verbosity of criteria queries.
265-
Our example looks like this:
310+
The helper class link:{doc-javadoc-url}org/hibernate/query/criteria/CriteriaDefinition.html[`CriteriaDefinition`] can reduce the verbosity of criteria queries by eliminating the need to explicitly qualify calls to the methods of `CriteriaBuilder`.
311+
Our <<criteria-query-example,previous example>> would look like this:
266312

267313
[source,java]
268314
----
@@ -295,9 +341,9 @@ As we said <<introduction,right up front>>, Hibernate's generated SQL is meant t
295341
|===
296342
| Kind | `Session` method | `EntityManager` method | `Query` execution method
297343

298-
| Selection | `createNativeQuery(String,Class)` | `createNativeQuery(String,Class)` | `getResultList()`, `getSingleResult()`, or `getSingleResultOrNull()`
299-
| Mutation | `createNativeMutationQuery(String)` | `createNativeQuery(String)` | `executeUpdate()`
300-
| Stored procedure | `createStoredProcedureCall(String)` | `createStoredProcedureQuery(String)` | `execute()`
344+
| Selection | link:{doc-javadoc-url}org/hibernate/query/QueryProducer.html#createNativeQuery(java.lang.String,java.lang.Class)[`createNativeQuery(String,Class)`] | `createNativeQuery(String,Class)` | `getResultList()`, `getSingleResult()`, or `getSingleResultOrNull()`
345+
| Mutation | link:{doc-javadoc-url}org/hibernate/query/QueryProducer.html#createNativeMutationQuery(java.lang.String)[`createNativeMutationQuery(String)`] | `createNativeQuery(String)` | `executeUpdate()`
346+
| Stored procedure | link:{doc-javadoc-url}org/hibernate/SharedSessionContract.html#createStoredProcedureCall(java.lang.String)[`createStoredProcedureCall(String)`] | `createStoredProcedureQuery(String)` | `execute()`
301347
|===
302348

303349
For the most simple cases, Hibernate can infer the shape of the result set:

0 commit comments

Comments
 (0)