Skip to content

Commit 4b58a94

Browse files
authored
Merge branch 'main' into build/remove-jreleaser-config
2 parents 51c8b20 + 5684680 commit 4b58a94

File tree

422 files changed

+5855
-3594
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

422 files changed

+5855
-3594
lines changed

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,39 @@ class OrderEvents {
799799
----
800800
A single entity listener class may even be a generic listener that receives lifecycle callbacks for multiple different entity classes.
801801

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()`].
834+
802835

803836
[[jdbc]]
804837
=== Interacting directly with JDBC

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:

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,29 @@
44
*/
55
package org.hibernate.community.dialect;
66

7-
import java.sql.DatabaseMetaData;
8-
import java.sql.ResultSet;
9-
import java.sql.SQLException;
10-
import java.sql.Types;
11-
import java.time.temporal.ChronoField;
12-
import java.time.temporal.TemporalAccessor;
13-
import java.util.Calendar;
14-
import java.util.Date;
15-
import java.util.Map;
16-
import java.util.TimeZone;
17-
import java.util.regex.Matcher;
18-
import java.util.regex.Pattern;
19-
7+
import jakarta.persistence.GenerationType;
8+
import jakarta.persistence.TemporalType;
9+
import jakarta.persistence.Timeout;
2010
import org.checkerframework.checker.nullness.qual.Nullable;
2111
import org.hibernate.LockMode;
2212
import org.hibernate.LockOptions;
2313
import org.hibernate.PessimisticLockException;
2414
import org.hibernate.QueryTimeoutException;
15+
import org.hibernate.Timeouts;
2516
import org.hibernate.boot.model.FunctionContributions;
2617
import org.hibernate.boot.model.TypeContributions;
27-
import org.hibernate.dialect.*;
18+
import org.hibernate.dialect.DatabaseVersion;
19+
import org.hibernate.dialect.Dialect;
20+
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
21+
import org.hibernate.dialect.FunctionalDependencyAnalysisSupport;
22+
import org.hibernate.dialect.FunctionalDependencyAnalysisSupportImpl;
23+
import org.hibernate.dialect.NationalizationSupport;
24+
import org.hibernate.dialect.NullOrdering;
25+
import org.hibernate.dialect.PostgreSQLDriverKind;
26+
import org.hibernate.dialect.RowLockStrategy;
27+
import org.hibernate.dialect.SimpleDatabaseVersion;
28+
import org.hibernate.dialect.SpannerDialect;
29+
import org.hibernate.dialect.TimeZoneSupport;
2830
import org.hibernate.dialect.aggregate.AggregateSupport;
2931
import org.hibernate.dialect.aggregate.CockroachDBAggregateSupport;
3032
import org.hibernate.dialect.function.CommonFunctionFactory;
@@ -58,9 +60,8 @@
5860
import org.hibernate.internal.util.JdbcExceptionHelper;
5961
import org.hibernate.internal.util.StringHelper;
6062
import org.hibernate.query.SemanticException;
61-
import org.hibernate.query.sqm.IntervalType;
62-
import org.hibernate.dialect.NullOrdering;
6363
import org.hibernate.query.common.TemporalUnit;
64+
import org.hibernate.query.sqm.IntervalType;
6465
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
6566
import org.hibernate.service.ServiceRegistry;
6667
import org.hibernate.sql.ast.SqlAstTranslator;
@@ -83,9 +84,18 @@
8384
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
8485
import org.hibernate.type.spi.TypeConfiguration;
8586

86-
87-
import jakarta.persistence.GenerationType;
88-
import jakarta.persistence.TemporalType;
87+
import java.sql.DatabaseMetaData;
88+
import java.sql.ResultSet;
89+
import java.sql.SQLException;
90+
import java.sql.Types;
91+
import java.time.temporal.ChronoField;
92+
import java.time.temporal.TemporalAccessor;
93+
import java.util.Calendar;
94+
import java.util.Date;
95+
import java.util.Map;
96+
import java.util.TimeZone;
97+
import java.util.regex.Matcher;
98+
import java.util.regex.Pattern;
8999

90100
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
91101
import static org.hibernate.query.common.TemporalUnit.DAY;
@@ -1009,14 +1019,38 @@ public String getForUpdateString(String aliases, LockOptions lockOptions) {
10091019
};
10101020
}
10111021

1022+
private String withTimeout(String lockString, Timeout timeout) {
1023+
return withTimeout( lockString, timeout.milliseconds() );
1024+
}
1025+
10121026
private String withTimeout(String lockString, int timeout) {
10131027
return switch ( timeout ) {
1014-
case LockOptions.NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString;
1015-
case LockOptions.SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString;
1028+
case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString;
1029+
case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString;
10161030
default -> lockString;
10171031
};
10181032
}
10191033

1034+
@Override
1035+
public String getWriteLockString(Timeout timeout) {
1036+
return withTimeout( getForUpdateString(), timeout );
1037+
}
1038+
1039+
@Override
1040+
public String getWriteLockString(String aliases, Timeout timeout) {
1041+
return withTimeout( getForUpdateString( aliases ), timeout );
1042+
}
1043+
1044+
@Override
1045+
public String getReadLockString(Timeout timeout) {
1046+
return withTimeout(" for share", timeout );
1047+
}
1048+
1049+
@Override
1050+
public String getReadLockString(String aliases, Timeout timeout) {
1051+
return withTimeout(" for share of " + aliases, timeout );
1052+
}
1053+
10201054
@Override
10211055
public String getWriteLockString(int timeout) {
10221056
return withTimeout( getForUpdateString(), timeout );

0 commit comments

Comments
 (0)