Skip to content

Commit dd1c68e

Browse files
Merge branch 'hibernate:main' into HHH-18891
2 parents 365f18b + 10b2c81 commit dd1c68e

File tree

107 files changed

+1907
-1375
lines changed

Some content is hidden

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

107 files changed

+1907
-1375
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,15 @@ session.enableFetchProfile(Book_.PROFILE_EAGER_BOOK);
11481148
Book eagerBook = session.find(Book.class, bookId);
11491149
----
11501150

1151-
Alternatively, an instance of link:{doc-javadoc-url}org/hibernate/EnabledFetchProfile.html[`EnabledFetchProfile`] may be obtained in a type safe way from the static metamodel, and used as a `FindOption`:
1151+
Alternatively, an instance of link:{doc-javadoc-url}org/hibernate/EnabledFetchProfile.html[`EnabledFetchProfile`] may be obtained in a type safe way from the static metamodel, and applied to the session:
1152+
1153+
[source,java]
1154+
----
1155+
Book_._EagerBook.enable(session);
1156+
Book eagerBook = session.find(Book.class, bookId);
1157+
----
1158+
1159+
Even better, the `EnabledFetchProfile` may be passed as a `FindOption`:
11521160

11531161
[source,java]
11541162
----

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ _Orphan removal_ indicates that an `Item` should be automatically deleted if it
395395
Our data model is a set of interconnected entities, and in Java our whole dataset would be represented as an enormous interconnected graph of objects.
396396
It's possible that this graph is disconnected, but more likely it's connected, or composed of a relatively small number of connected subgraphs.
397397

398-
Therefore, when we retrieve on object belonging to this graph from the database and instantiate it in memory, we simply can't recursively retrieve and instantiate all its associated entities.
398+
Therefore, when we retrieve an object belonging to this graph from the database and instantiate it in memory, we simply can't recursively retrieve and instantiate all its associated entities.
399399
Quite aside from the waste of memory on the VM side, this process would involve a huge number of round trips to the database server, or a massive multidimensional cartesian product of tables, or both.
400400
Instead, we're forced to cut the graph somewhere.
401401

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ You don't have to use stateful sessions, and you're not doing anything wrong if
531531
As of Hibernate 7, a key decision for any new project is which of these programming models to take as a baseline.
532532
Fortunately, the two models aren't mutually exclusive.
533533
This is a friendly competition, where the two APIs are designed to complement each other.
534-
Even if we decide to use `StatefulSession` most of the time, we can still use a `StatelessSession` wherever it's more convenient.
534+
Even if we decide to use stateful ``Session``s most of the time, we can still use a `StatelessSession` wherever it's more convenient.
535535

536536
NOTE: On the other hand, if you decide to adopt Jakarta Data, the decision is made for you: repositories in Jakarta Data 1.0 are always stateless, and in https://hibernate.org/repositories/[Hibernate Data Repositories] a repository is backed by a `StatelessSession`.
537537

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,7 @@ It's much more typesafe to use one of the first two options.
11751175
=== Reactive programming with Hibernate
11761176

11771177
:hr: https://hibernate.org/reactive/
1178-
:hr-guide: https://hibernate.org/reactive/documentation/2.0/reference/html_single/
1178+
:hr-guide: https://hibernate.org/reactive/documentation/3.0/reference/html_single/
11791179

11801180
Finally, many systems which require high scalability now make use of reactive programming and reactive streams.
11811181
{hr}[Hibernate Reactive] brings O/R mapping to the world of reactive programming.

hibernate-core/src/main/java/org/hibernate/EnabledFetchProfile.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@
55
package org.hibernate;
66

77
import jakarta.persistence.FindOption;
8+
import org.hibernate.query.SelectionQuery;
89

910
/**
1011
* A {@link jakarta.persistence.FindOption} which requests a named
1112
* {@linkplain org.hibernate.annotations.FetchProfile fetch profile}.
1213
* <p>
1314
* An instance of this class may be obtained in a type safe way
1415
* from the static metamodel for the class annotated
15-
* {@link org.hibernate.annotations.FetchProfile @FetchProfile}
16-
* and passed as an option to
17-
* {@link Session#find(Class, Object, FindOption...) find()}.
16+
* {@link org.hibernate.annotations.FetchProfile @FetchProfile}.
1817
* <p>
1918
* For example, this class defines a fetch profile:
2019
* <pre>
@@ -28,11 +27,20 @@
2827
* Set&lt;Author&gt; authors;
2928
* }
3029
* </pre>
31-
* The fetch profile may be requested like this:
30+
* <p>
31+
* An {@code EnabledFetchProfile} may be obtained from the static
32+
* metamodel for the entity {@code Book} and passed as an option to
33+
* {@link Session#find(Class, Object, FindOption...) find()}.
3234
* <pre>
3335
* Book bookWithAuthors =
3436
* session.find(Book.class, isbn, Book_._WithAuthors)
3537
* </pre>
38+
* Alternatively, it may be {@linkplain #enable(Session) applied}
39+
* to a {@code Session} or {@code Query}.
40+
* <pre>
41+
* Book_._WithAuthors.enable(session);
42+
* Book bookWithAuthors = session.find(Book.class, isbn);
43+
* </pre>
3644
* <p>
3745
* When the static metamodel is not used, an {@code EnabledFetchProfile}
3846
* may be instantiated directly, passing the name of the fetch profile
@@ -54,4 +62,20 @@
5462
*/
5563
public record EnabledFetchProfile(String profileName)
5664
implements FindOption {
65+
66+
/**
67+
* Enable the fetch profile represented by this
68+
* object in the given session.
69+
*/
70+
public void enable(Session session) {
71+
session.enableFetchProfile(profileName);
72+
}
73+
74+
/**
75+
* Enable the fetch profile represented by this
76+
* object during execution of the given query.
77+
*/
78+
public void enable(SelectionQuery<?> query) {
79+
query.enableFetchProfile(profileName);
80+
}
5781
}

hibernate-core/src/main/java/org/hibernate/LockMode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public enum LockMode implements FindOption, RefreshOption {
5252
* rather than pull it from a cache.
5353
* <p>
5454
* This is the "default" lock mode, the mode requested by calling
55-
* {@link Session#get(Class, Object)} without passing an explicit
55+
* {@link Session#find(Class, Object)} without passing an explicit
5656
* mode. It permits the state of an object to be retrieved from
5757
* the cache without the cost of database access.
5858
*

hibernate-core/src/main/java/org/hibernate/Session.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -825,19 +825,32 @@ public interface Session extends SharedSessionContract, EntityManager {
825825
void remove(Object object);
826826

827827
/**
828-
* Determine the current {@link LockMode} of the given managed instance associated
829-
* with this session.
828+
* Determine the current {@linkplain LockMode lock mode} held on the given
829+
* managed instance associated with this session.
830+
* <p>
831+
* Unlike the JPA-standard {@link #getLockMode}, this operation may be
832+
* called when no transaction is active, in which case it should return
833+
* {@link LockMode#NONE}, indicating that no pessimistic lock is held on
834+
* the given entity.
830835
*
831836
* @param object a persistent instance associated with this session
832837
*
833-
* @return the current lock mode
838+
* @return the lock mode currently held on the given entity
839+
*
840+
* @throws IllegalStateException if the given instance is not associated
841+
* with this persistence context
842+
* @throws ObjectDeletedException if the given instance was already
843+
* {@linkplain #remove removed}
834844
*/
835845
LockMode getCurrentLockMode(Object object);
836846

837847
/**
838-
* Completely clear the session. Evict all loaded instances and cancel all pending
839-
* saves, updates and deletions. Do not close open iterators or instances of
840-
* {@link ScrollableResults}.
848+
* Completely clear the persistence context. Evict all loaded instances,
849+
* causing every managed entity currently associated with this session to
850+
* transition to the detached state, and cancel all pending insertions,
851+
* updates, and deletions.
852+
* <p>
853+
* Does not close open iterators or instances of {@link ScrollableResults}.
841854
*/
842855
@Override
843856
void clear();

hibernate-core/src/main/java/org/hibernate/SessionFactory.java

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,19 @@ public interface SessionFactory extends EntityManagerFactory, Referenceable, Ser
227227
StatelessSession openStatelessSession(Connection connection);
228228

229229
/**
230-
* Open a {@link Session} and use it to perform an action.
230+
* Open a {@link Session} and use it to perform the given action.
231+
*
232+
* @apiNote This method does not begin a transaction, and so
233+
* the session is not automatically flushed before the method
234+
* returns unless either:
235+
* <ul>
236+
* <li>the given action calls {@link Session#flush() flush()}
237+
* explicitly, or
238+
* <li>a transaction is initiated by the given action, using
239+
* {@link Session#inTransaction}, for example.
240+
* </ul>
241+
*
242+
* @see #inTransaction(Consumer)
231243
*/
232244
default void inSession(Consumer<? super Session> action) {
233245
try ( Session session = openSession() ) {
@@ -236,7 +248,20 @@ default void inSession(Consumer<? super Session> action) {
236248
}
237249

238250
/**
239-
* Open a {@link StatelessSession} and use it to perform an action.
251+
* Open a {@link StatelessSession} and use it to perform the
252+
* given action.
253+
*
254+
* @apiNote This method does not begin a transaction, and so
255+
* the session is not automatically flushed before the method
256+
* returns unless either:
257+
* <ul>
258+
* <li>the given action calls {@link Session#flush() flush()}
259+
* explicitly, or
260+
* <li>a transaction is initiated by the given action, using
261+
* {@link Session#inTransaction}, for example.
262+
* </ul>
263+
*
264+
* @see #inStatelessTransaction(Consumer)
240265
*
241266
* @since 6.3
242267
*/
@@ -247,7 +272,7 @@ default void inStatelessSession(Consumer<? super StatelessSession> action) {
247272
}
248273

249274
/**
250-
* Open a {@link Session} and use it to perform an action
275+
* Open a {@link Session} and use it to perform the given action
251276
* within the bounds of a transaction.
252277
*
253278
* @apiNote This method competes with the JPA-defined method
@@ -269,6 +294,18 @@ default void inStatelessTransaction(Consumer<? super StatelessSession> action) {
269294

270295
/**
271296
* Open a {@link Session} and use it to obtain a value.
297+
*
298+
* @apiNote This method does not begin a transaction, and so
299+
* the session is not automatically flushed before the method
300+
* returns unless either:
301+
* <ul>
302+
* <li>the given action calls {@link Session#flush() flush()}
303+
* explicitly, or
304+
* <li>a transaction is initiated by the given action, using
305+
* {@link Session#inTransaction}, for example.
306+
* </ul>
307+
*
308+
* @see #fromTransaction(Function)
272309
*/
273310
default <R> R fromSession(Function<? super Session,R> action) {
274311
try ( Session session = openSession() ) {
@@ -279,6 +316,18 @@ default <R> R fromSession(Function<? super Session,R> action) {
279316
/**
280317
* Open a {@link StatelessSession} and use it to obtain a value.
281318
*
319+
* @apiNote This method does not begin a transaction, and so
320+
* the session is not automatically flushed before the method
321+
* returns unless either:
322+
* <ul>
323+
* <li>the given action calls {@link Session#flush() flush()}
324+
* explicitly, or
325+
* <li>a transaction is initiated by the given action, using
326+
* {@link Session#inTransaction}, for example.
327+
* </ul>
328+
*
329+
* @see #fromStatelessTransaction(Function)
330+
*
282331
* @since 6.3
283332
*/
284333
default <R> R fromStatelessSession(Function<? super StatelessSession,R> action) {

hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1941,7 +1941,7 @@ protected void secondPassCompileForeignKeys(Table table, Set<ForeignKey> done, M
19411941
table.createForeignKeys( buildingContext );
19421942

19431943
final Dialect dialect = getDatabase().getJdbcEnvironment().getDialect();
1944-
for ( ForeignKey foreignKey : table.getForeignKeys().values() ) {
1944+
for ( ForeignKey foreignKey : table.getForeignKeyCollection() ) {
19451945
if ( !done.contains( foreignKey ) ) {
19461946
done.add( foreignKey );
19471947
final PersistentClass referencedClass = foreignKey.resolveReferencedClass(this);

hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ private void handleUDT(UserDefinedType userDefinedType, ColumnOrderingStrategy c
408408
}
409409

410410
private void handleForeignKeys(Table table, ColumnOrderingStrategy columnOrderingStrategy) {
411-
for ( ForeignKey foreignKey : table.getForeignKeys().values() ) {
411+
for ( ForeignKey foreignKey : table.getForeignKeyCollection() ) {
412412
final List<Column> columns = foreignKey.getColumns();
413413
if ( columns.size() > 1 ) {
414414
if ( foreignKey.getReferencedColumns().isEmpty() ) {

hibernate-core/src/main/java/org/hibernate/boot/model/FunctionContributor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import org.hibernate.service.JavaServiceLoadable;
88

99
/**
10-
* On object that contributes custom HQL functions, eventually to a
10+
* An object that contributes custom HQL functions, eventually to a
1111
* {@link org.hibernate.query.sqm.function.SqmFunctionRegistry}, via an
1212
* instance of {@link FunctionContributions}.
1313
* <ul>

hibernate-core/src/main/java/org/hibernate/boot/model/TypeContributor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import org.hibernate.service.ServiceRegistry;
99

1010
/**
11-
* On object that contributes custom types and type descriptors, eventually to
11+
* An object that contributes custom types and type descriptors, eventually to
1212
* a {@link org.hibernate.type.spi.TypeConfiguration}, via an instance of
1313
* {@link TypeContributions}.
1414
* <ul>

hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,12 @@ private static void checkColumnInSameTable(
224224
* considered the target of the association. This method adds
225225
* the property holding the synthetic component to the target
226226
* entity {@link PersistentClass} by side effect.
227+
* <p>
228+
* This method automatically marks the reference column unique,
229+
* or creates a unique key on the referenced columns. It's not
230+
* really clear that we should do this. Perhaps we should just
231+
* validate that they are unique and error if not, like in
232+
* {@code TableBinder.checkReferenceToUniqueKey()}.
227233
*/
228234
private static Property referencedProperty(
229235
PersistentClass ownerEntity,
@@ -238,7 +244,10 @@ private static Property referencedProperty(
238244
&& ownerEntity == columnOwner
239245
&& !( properties.get(0).getValue() instanceof ToOne ) ) {
240246
// no need to make a synthetic property
241-
return properties.get(0);
247+
final Property property = properties.get( 0 );
248+
// mark it unique
249+
property.getValue().createUniqueKey( context );
250+
return property;
242251
}
243252
else {
244253
// Create a synthetic Property whose Value is a synthetic

hibernate-core/src/main/java/org/hibernate/boot/model/internal/IndexBinder.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.StringTokenizer;
1111

1212
import org.hibernate.AnnotationException;
13+
import org.hibernate.AssertionFailure;
1314
import org.hibernate.boot.model.naming.Identifier;
1415
import org.hibernate.boot.model.naming.ImplicitIndexNameSource;
1516
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
@@ -158,10 +159,11 @@ private void createIndexOrUniqueKey(
158159
String[] columnNames,
159160
String[] orderings,
160161
boolean unique,
162+
boolean declaredAsIndex,
161163
String options,
162164
Selectable[] columns) {
163165
final IndexOrUniqueKeyNameSource source =
164-
new IndexOrUniqueKeyNameSource( context, table, columnNames, originalKeyName );
166+
new IndexOrUniqueKeyNameSource( context, table, columnNames, originalKeyName, declaredAsIndex );
165167
boolean hasFormula = false;
166168
for ( Selectable selectable : columns ) {
167169
if ( selectable.isFormula() ) {
@@ -175,7 +177,12 @@ private void createIndexOrUniqueKey(
175177
uniqueKey.setNameExplicit( nameExplicit );
176178
uniqueKey.setOptions( options );
177179
for ( int i = 0; i < columns.length; i++ ) {
178-
uniqueKey.addColumn( (Column) columns[i], orderings != null ? orderings[i] : null );
180+
if ( columns[i] instanceof Column column) {
181+
uniqueKey.addColumn( column, orderings != null ? orderings[i] : null );
182+
}
183+
else {
184+
throw new AssertionFailure( "Not a column" );
185+
}
179186
}
180187
}
181188
else {
@@ -212,6 +219,7 @@ void bindIndexes(Table table, jakarta.persistence.Index[] indexes) {
212219
columnExpressions,
213220
ordering,
214221
unique,
222+
true,
215223
options,
216224
selectables( table, name, columnExpressions )
217225
);
@@ -230,6 +238,7 @@ void bindUniqueConstraints(Table table, UniqueConstraint[] constraints) {
230238
columnNames,
231239
null,
232240
true,
241+
false,
233242
options,
234243
columns( table, name, columnNames )
235244
);
@@ -255,17 +264,32 @@ else if ( tmp.endsWith( " asc" ) ) {
255264
}
256265
}
257266

258-
private class IndexOrUniqueKeyNameSource implements ImplicitIndexNameSource, ImplicitUniqueKeyNameSource {
267+
private class IndexOrUniqueKeyNameSource
268+
implements ImplicitIndexNameSource, ImplicitUniqueKeyNameSource {
259269
private final MetadataBuildingContext buildingContext;
260270
private final Table table;
261271
private final String[] columnNames;
262272
private final String originalKeyName;
273+
private final boolean declaredAsIndex;
263274

264-
public IndexOrUniqueKeyNameSource(MetadataBuildingContext buildingContext, Table table, String[] columnNames, String originalKeyName) {
275+
private IndexOrUniqueKeyNameSource(
276+
MetadataBuildingContext buildingContext,
277+
Table table,
278+
String[] columnNames,
279+
String originalKeyName,
280+
boolean declaredAsIndex) {
265281
this.buildingContext = buildingContext;
266282
this.table = table;
267283
this.columnNames = columnNames;
268284
this.originalKeyName = originalKeyName;
285+
this.declaredAsIndex = declaredAsIndex;
286+
}
287+
288+
@Override
289+
public Kind kind() {
290+
return declaredAsIndex
291+
? ImplicitIndexNameSource.super.kind()
292+
: ImplicitUniqueKeyNameSource.super.kind();
269293
}
270294

271295
@Override

0 commit comments

Comments
 (0)