Skip to content

Commit 9870646

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

File tree

1 file changed

+76
-28
lines changed

1 file changed

+76
-28
lines changed

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

Lines changed: 76 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,42 @@ 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+
=== Query parameters
90+
91+
Queries are often parameterized.
92+
93+
- In the query above, `:titleSearchPattern` is called a _named parameter_.
94+
- Alternatively, we may label a parameter by a number. Such a parameter is called an _ordinal parameter_.
95+
96+
We may easily rewrite our query to use an ordinal parameter:
6697

6798
[source,java]
6899
----
@@ -81,45 +112,61 @@ _Never_ concatenate user input with HQL and pass the concatenated string to `cre
81112
This would open up the possibility for an attacker to execute arbitrary code on your database server.
82113
====
83114

84-
If we're expecting a query to return a single result, we can use `getSingleResult()`.
115+
The link:{doc-javadoc-url}org/hibernate/query/CommonQueryContract.html#setParameter(java.lang.String,java.lang.Object)[`setParameter()`] methods specify arguments to query parameters.
116+
117+
[TIP]
118+
====
119+
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.
120+
The best way to identify the type is via a reference to a JPA metamodel `Type`.
121+
There are two ways to do this:
122+
123+
// - 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),
124+
- 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
125+
- by packaging the argument and its `Type` in a link:{doc-javadoc-url}org/hibernate/query/TypedParameterValue.html[`TypedParameterValue`].
126+
127+
For example, we may pass a <<static-metamodel,static metamodel>> reference to `setParameter()`.
85128
86129
[source,java]
87130
----
88-
Book book =
89-
session.createSelectionQuery("from Book where isbn = ?1", Book.class)
90-
.setParameter(1, isbn)
91-
.getSingleResult();
131+
session.createSelectionQuery("from Person where address = :address")
132+
.setParameter("address" address, Person_.address.getType())
133+
.getResultList();
92134
----
135+
====
136+
137+
[[auto-flush]]
138+
=== Auto-flush
139+
140+
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.
141+
If there are unflushed changes, then Hibernate goes ahead and executes an automatic <<flush,flush>> before executing the query.
142+
That way, the query won't return stale results which fail to reflect changes made to data within the current unit of work.
143+
But if there are many entities association with the persistence context, then this can be an expensive operation.
93144

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

96147
[source,java]
97148
----
98149
Book bookOrNull =
99150
session.createSelectionQuery("from Book where isbn = ?1", Book.class)
100151
.setParameter(1, isbn)
101-
.getSingleResultOrNull();
152+
.setQueryFlushMode(QueryFlushMode.NO_FLUSH)
153+
.getSingleResult();
102154
----
103155

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`:
156+
Or, especially if you're using JPA-standard APIs, use `FlushModeType.COMMIT`:
110157

111158
[source,java]
112159
----
113160
Book bookOrNull =
114161
session.createSelectionQuery("from Book where isbn = ?1", Book.class)
115162
.setParameter(1, isbn)
116-
.setHibernateFlushMode(MANUAL)
163+
.setFlushMode(FlushModeType.COMMIT)
117164
.getSingleResult();
118165
----
119166

120167
[CAUTION]
121168
====
122-
Setting the flush mode to `COMMIT` or `MANUAL` might cause the query to return stale results.
169+
Setting the flush mode to `NO_FLUSH`, `COMMIT`, or `MANUAL` might cause the query to return stale results.
123170
====
124171

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

179226
We're ready to create a criteria query.
180227

228+
[[criteria-query-example]]
181229
[source,java]
182230
----
183231
CriteriaQuery<Book> query = builder.createQuery(Book.class);
@@ -210,8 +258,8 @@ Execution of a criteria query works almost exactly like execution of HQL.
210258
|===
211259
| Kind | `Session` method | `EntityManager` method | `Query` execution method
212260

213-
| Selection | `createSelectionQuery(CriteriaQuery)` | `createQuery(CriteriaQuery)` | `getResultList()`, `getSingleResult()`, or `getSingleResultOrNull()`
214-
| Mutation | `createMutationQuery(CriteriaUpdate)` or `createMutationQuery(CriteriaDelete)` | `createQuery(CriteriaUpdate)` or `createQuery(CriteriaDelte)` | `executeUpdate()`
261+
| Selection | link:{doc-javadoc-url}org/hibernate/query/QueryProducer.html#createSelectionQuery(jakarta.persistence.criteria.CriteriaQuery)[`createSelectionQuery(CriteriaQuery)`] | `createQuery(CriteriaQuery)` | `getResultList()`, `getSingleResult()`, or `getSingleResultOrNull()`
262+
| 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()`
215263
|===
216264

217265
For example:
@@ -261,8 +309,8 @@ The reason it works this way is that each JPA provider has its own implementatio
261309
// [%unbreakable]
262310
// [TIP]
263311
// ====
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:
312+
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`.
313+
Our <<criteria-query-example,previous example>> would look like this:
266314

267315
[source,java]
268316
----
@@ -295,9 +343,9 @@ As we said <<introduction,right up front>>, Hibernate's generated SQL is meant t
295343
|===
296344
| Kind | `Session` method | `EntityManager` method | `Query` execution method
297345

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()`
346+
| 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()`
347+
| Mutation | link:{doc-javadoc-url}org/hibernate/query/QueryProducer.html#createNativeMutationQuery(java.lang.String)[`createNativeMutationQuery(String)`] | `createNativeQuery(String)` | `executeUpdate()`
348+
| Stored procedure | link:{doc-javadoc-url}org/hibernate/SharedSessionContract.html#createStoredProcedureCall(java.lang.String)[`createStoredProcedureCall(String)`] | `createStoredProcedureQuery(String)` | `execute()`
301349
|===
302350

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

0 commit comments

Comments
 (0)