You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: documentation/src/main/asciidoc/introduction/Interacting.adoc
+33Lines changed: 33 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -799,6 +799,39 @@ class OrderEvents {
799
799
----
800
800
A single entity listener class may even be a generic listener that receives lifecycle callbacks for multiple different entity classes.
801
801
802
+
[TIP]
803
+
The venerable link:{doc-javadoc-url}org/hibernate/Interceptor.html[`Interceptor`] interface is a more powerful alternative to entity listeners.
804
+
`Interceptor` and its friend link:{doc-javadoc-url}org/hibernate/CustomEntityDirtinessStrategy.html[`CustomEntityDirtinessStrategy`] allow advanced users to augment the built-in handling of managed entities with custom behavior.
805
+
These interfaces are very useful if you're building your own persistence framework with Hibernate as the foundation.
806
+
807
+
We're about to see one way `Interceptor` can be used.
808
+
809
+
[[transient-vs-detached]]
810
+
=== Transient vs detached
811
+
812
+
Sometimes, Hibernate needs to be able to distinguish whether an entity instance is:
813
+
814
+
- a brand-new transient object the client just instantiated using `new`, or
815
+
- a detached object, which previously belonged to a persistence context.
816
+
817
+
This is a bit of a problem, since there's no good and efficient way for Hibernate to just tag an entity with a Post-it saying "I've seen you before".
818
+
819
+
Therefore, Hibernate uses heuristics.
820
+
The two most useful heuristics are:
821
+
822
+
1. If the entity has a <<generated-identifiers,generated identifier>>, the value of the id field is inspected: if the value currently assigned to the id field is the default value for the type of the field, then the object is transient; otherwise, the object is detached.
823
+
2. If the entity has a <<version-attributes,version>>, the value of the version field is inspected: if the value currently assigned to the version field is the default value, or a negative number, then the object is transient; otherwise, the object is detached.
824
+
825
+
If the entity has neither a generated id, nor a version, Hibernate usually falls back to just doing something reasonable.
826
+
In extreme cases a `SELECT` query will be issued to determine whether a matching row exists in the database.
827
+
828
+
[WARNING]
829
+
These heuristics aren't perfect.
830
+
It's quite easy to confuse Hibernate by assigning a value to the id field or version field, making a new transient instance look like it's detached.
831
+
We therefore strongly discourage assigning values to fields annotated `@GeneratedValue` or `@Version` before passing an entity to Hibernate.
832
+
833
+
If the heuristics ever happen cause a real problem, you may implement your own Post-it tagging via link:{doc-javadoc-url}org/hibernate/Interceptor.html#isTransient(java.lang.Object)[`Interceptor.isTransient()`].
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`.
62
62
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:
66
97
67
98
[source,java]
68
99
----
@@ -81,45 +112,61 @@ _Never_ concatenate user input with HQL and pass the concatenated string to `cre
81
112
This would open up the possibility for an attacker to execute arbitrary code on your database server.
82
113
====
83
114
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()`.
85
128
86
129
[source,java]
87
130
----
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")
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.
93
144
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`:
95
146
96
147
[source,java]
97
148
----
98
149
Book bookOrNull =
99
150
session.createSelectionQuery("from Book where isbn = ?1", Book.class)
100
151
.setParameter(1, isbn)
101
-
.getSingleResultOrNull();
152
+
.setQueryFlushMode(QueryFlushMode.NO_FLUSH)
153
+
.getSingleResult();
102
154
----
103
155
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`:
110
157
111
158
[source,java]
112
159
----
113
160
Book bookOrNull =
114
161
session.createSelectionQuery("from Book where isbn = ?1", Book.class)
115
162
.setParameter(1, isbn)
116
-
.setHibernateFlushMode(MANUAL)
163
+
.setFlushMode(FlushModeType.COMMIT)
117
164
.getSingleResult();
118
165
----
119
166
120
167
[CAUTION]
121
168
====
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.
123
170
====
124
171
125
172
Occasionally we need to build a query at runtime, from a set of optional conditions.
| 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()`
215
263
|===
216
264
217
265
For example:
@@ -261,8 +309,8 @@ The reason it works this way is that each JPA provider has its own implementatio
261
309
// [%unbreakable]
262
310
// [TIP]
263
311
// ====
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:
266
314
267
315
[source,java]
268
316
----
@@ -295,9 +343,9 @@ As we said <<introduction,right up front>>, Hibernate's generated SQL is meant t
0 commit comments