Skip to content

Commit 3c1b984

Browse files
committed
[#1782] Upgrade Hibernate ORM to 6.4.0.Final
1 parent f517fc0 commit 3c1b984

29 files changed

+409
-332
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Hibernate Reactive has been tested with:
3737
- CockroachDB 22.1
3838
- MS SQL Server 2019
3939
- Oracle 21.3
40-
- [Hibernate ORM][] 6.3.2.Final
40+
- [Hibernate ORM][] 6.4.0.Final
4141
- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.0
4242
- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.0
4343
- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.0

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ version = projectVersion
5353
// ./gradlew clean build -PhibernateOrmVersion=5.6.15-SNAPSHOT
5454
ext {
5555
if ( !project.hasProperty('hibernateOrmVersion') ) {
56-
hibernateOrmVersion = '6.3.2.Final'
56+
hibernateOrmVersion = '6.4.0.Final'
5757
}
5858
if ( !project.hasProperty( 'hibernateOrmGradlePluginVersion' ) ) {
5959
// Same as ORM as default

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ org.gradle.java.installations.auto-download=false
3535
#enableMavenLocalRepo = true
3636

3737
# Override default Hibernate ORM version
38-
#hibernateOrmVersion = 6.2.3.Final
38+
#hibernateOrmVersion = 6.4.0.Final
3939

4040
# Override default Hibernate ORM Gradle plugin version
4141
# Using the stable version because I don't know how to configure the build to download the snapshot version from
4242
# a remote repository
43-
#hibernateOrmGradlePluginVersion = 6.2.3.Final
43+
#hibernateOrmGradlePluginVersion = 6.4.0.Final
4444

4545
# If set to true, skip Hibernate ORM version parsing (default is true, if set to null)
4646
# this is required when using intervals or weird versions or the build will fail

hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java

Lines changed: 84 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.hibernate.metamodel.mapping.AttributeMapping;
3030
import org.hibernate.persister.collection.CollectionPersister;
3131
import org.hibernate.persister.entity.EntityPersister;
32-
import org.hibernate.proxy.HibernateProxy;
3332
import org.hibernate.reactive.logging.impl.Log;
3433
import org.hibernate.reactive.logging.impl.LoggerFactory;
3534
import org.hibernate.reactive.session.ReactiveSession;
@@ -40,6 +39,7 @@
4039
import org.hibernate.type.EntityType;
4140
import org.hibernate.type.Type;
4241

42+
import static org.hibernate.engine.internal.ManagedTypeHelper.isHibernateProxy;
4343
import static org.hibernate.pretty.MessageHelper.infoString;
4444
import static org.hibernate.reactive.util.impl.CompletionStages.loop;
4545
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture;
@@ -111,9 +111,9 @@ public static CompletionStage<?> fetchLazyAssociationsBeforeCascade(
111111
* Cascade an action from the parent entity instance to all its children.
112112
*/
113113
public CompletionStage<Void> cascade() throws HibernateException {
114-
return voidFuture().thenCompose(v -> {
114+
return voidFuture().thenCompose( v -> {
115115
CacheMode cacheMode = eventSource.getCacheMode();
116-
if ( action==CascadingActions.DELETE ) {
116+
if ( action == CascadingActions.DELETE ) {
117117
eventSource.setCacheMode( CacheMode.GET );
118118
}
119119
eventSource.getPersistenceContextInternal().incrementCascadeLevel();
@@ -125,18 +125,23 @@ public CompletionStage<Void> cascade() throws HibernateException {
125125
}
126126

127127
private CompletionStage<Void> cascadeInternal() throws HibernateException {
128-
129128
if ( persister.hasCascades() || action.requiresNoCascadeChecking() ) { // performance opt
130129
final boolean traceEnabled = LOG.isTraceEnabled();
131130
if ( traceEnabled ) {
132131
LOG.tracev( "Processing cascade {0} for: {1}", action, persister.getEntityName() );
133132
}
134133
final PersistenceContext persistenceContext = eventSource.getPersistenceContextInternal();
134+
final EntityEntry entry = persistenceContext.getEntry( parent );
135+
if ( entry != null && entry.getLoadedState() == null && entry.getStatus() == Status.MANAGED && persister.getBytecodeEnhancementMetadata()
136+
.isEnhancedForLazyLoading() ) {
137+
return voidFuture();
138+
}
135139

136140
final Type[] types = persister.getPropertyTypes();
137141
final String[] propertyNames = persister.getPropertyNames();
138142
final CascadeStyle[] cascadeStyles = persister.getPropertyCascadeStyles();
139143
final boolean hasUninitializedLazyProperties = persister.hasUninitializedLazyProperties( parent );
144+
140145
for ( int i = 0; i < types.length; i++) {
141146
final CascadeStyle style = cascadeStyles[ i ];
142147
final String propertyName = propertyNames[ i ];
@@ -154,7 +159,7 @@ private CompletionStage<Void> cascadeInternal() throws HibernateException {
154159
// If parent is a detached entity being merged,
155160
// then parent will not be in the PersistenceContext
156161
// (so lazy attributes must not be initialized).
157-
if ( persistenceContext.getEntry( parent ) == null ) {
162+
if ( entry == null ) {
158163
// parent was not in the PersistenceContext
159164
continue;
160165
}
@@ -295,91 +300,88 @@ private void cascadeLogicalOneToOneOrphanRemoval(
295300
final String propertyName,
296301
final boolean isCascadeDeleteEnabled) throws HibernateException {
297302

298-
// potentially we need to handle orphan deletes for one-to-ones here...
299-
if ( isLogicalOneToOne( type ) ) {
300-
// We have a physical or logical one-to-one. See if the attribute cascade settings and action-type require
301-
// orphan checking
302-
if ( style.hasOrphanDelete() && action.deleteOrphans() ) {
303-
// value is orphaned if loaded state for this property shows not null
304-
// because it is currently null.
305-
final PersistenceContext persistenceContext = eventSource.getPersistenceContextInternal();
306-
final EntityEntry entry = persistenceContext.getEntry( parent );
307-
if ( entry != null && entry.getStatus() != Status.SAVING ) {
308-
Object loadedValue;
309-
if ( componentPath == null ) {
310-
// association defined on entity
311-
loadedValue = entry.getLoadedValue( propertyName );
303+
// We have a physical or logical one-to-one. See if the attribute cascade settings and action-type require
304+
// orphan checking
305+
if ( style.hasOrphanDelete() && action.deleteOrphans() ) {
306+
// value is orphaned if loaded state for this property shows not null
307+
// because it is currently null.
308+
final PersistenceContext persistenceContext = eventSource.getPersistenceContextInternal();
309+
final EntityEntry entry = persistenceContext.getEntry( parent );
310+
if ( entry != null && entry.getStatus() != Status.SAVING ) {
311+
Object loadedValue;
312+
if ( componentPath == null ) {
313+
// association defined on entity
314+
loadedValue = entry.getLoadedValue( propertyName );
315+
}
316+
else {
317+
// association defined on component
318+
// Since the loadedState in the EntityEntry is a flat domain type array
319+
// We first have to extract the component object and then ask the component type
320+
// recursively to give us the value of the sub-property of that object
321+
final AttributeMapping propertyType = entry.getPersister().findAttributeMapping( componentPath.get( 0) );
322+
if ( propertyType instanceof ComponentType) {
323+
loadedValue = entry.getLoadedValue( componentPath.get( 0 ) );
324+
ComponentType componentType = (ComponentType) propertyType;
325+
if ( componentPath.size() != 1 ) {
326+
for ( int i = 1; i < componentPath.size(); i++ ) {
327+
final int subPropertyIndex = componentType.getPropertyIndex( componentPath.get( i ) );
328+
loadedValue = componentType.getPropertyValue( loadedValue, subPropertyIndex );
329+
componentType = (ComponentType) componentType.getSubtypes()[subPropertyIndex];
330+
}
331+
}
332+
333+
loadedValue = componentType.getPropertyValue( loadedValue, componentType.getPropertyIndex( propertyName ) );
312334
}
313335
else {
314-
// association defined on component
315-
// Since the loadedState in the EntityEntry is a flat domain type array
316-
// We first have to extract the component object and then ask the component type
317-
// recursively to give us the value of the sub-property of that object
318-
final AttributeMapping propertyType = entry.getPersister().findAttributeMapping( componentPath.get( 0) );
319-
if ( propertyType instanceof ComponentType) {
320-
loadedValue = entry.getLoadedValue( componentPath.get( 0 ) );
321-
ComponentType componentType = (ComponentType) propertyType;
322-
if ( componentPath.size() != 1 ) {
323-
for ( int i = 1; i < componentPath.size(); i++ ) {
324-
final int subPropertyIndex = componentType.getPropertyIndex( componentPath.get( i ) );
325-
loadedValue = componentType.getPropertyValue( loadedValue, subPropertyIndex );
326-
componentType = (ComponentType) componentType.getSubtypes()[subPropertyIndex];
327-
}
328-
}
336+
// Association is probably defined in an element collection, so we can't do orphan removals
337+
loadedValue = null;
338+
}
339+
}
329340

330-
loadedValue = componentType.getPropertyValue( loadedValue, componentType.getPropertyIndex( propertyName ) );
331-
}
332-
else {
333-
// Association is probably defined in an element collection, so we can't do orphan removals
334-
loadedValue = null;
341+
// orphaned if the association was nulled (child == null) or receives a new value while the
342+
// entity is managed (without first nulling and manually flushing).
343+
if ( child == null || loadedValue != null && child != loadedValue ) {
344+
EntityEntry valueEntry = persistenceContext.getEntry( loadedValue );
345+
346+
if ( valueEntry == null && isHibernateProxy( loadedValue ) ) {
347+
// un-proxy and re-associate for cascade operation
348+
// useful for @OneToOne defined as FetchType.LAZY
349+
loadedValue = persistenceContext.unproxyAndReassociate( loadedValue );
350+
valueEntry = persistenceContext.getEntry( loadedValue );
351+
352+
// HHH-11965
353+
// Should the unwrapped proxy value be equal via reference to the entity's property value
354+
// provided by the 'child' variable, we should not trigger the orphan removal of the
355+
// associated one-to-one.
356+
if ( child == loadedValue ) {
357+
// do nothing
358+
return;
335359
}
336360
}
337361

338-
// orphaned if the association was nulled (child == null) or receives a new value while the
339-
// entity is managed (without first nulling and manually flushing).
340-
if ( child == null || loadedValue != null && child != loadedValue ) {
341-
EntityEntry valueEntry = persistenceContext.getEntry( loadedValue );
342-
343-
if ( valueEntry == null && loadedValue instanceof HibernateProxy ) {
344-
// un-proxy and re-associate for cascade operation
345-
// useful for @OneToOne defined as FetchType.LAZY
346-
loadedValue = persistenceContext.unproxyAndReassociate( loadedValue );
347-
valueEntry = persistenceContext.getEntry( loadedValue );
348-
349-
// HHH-11965
350-
// Should the unwrapped proxy value be equal via reference to the entity's property value
351-
// provided by the 'child' variable, we should not trigger the orphan removal of the
352-
// associated one-to-one.
353-
if ( child == loadedValue ) {
354-
// do nothing
355-
return;
356-
}
362+
if ( valueEntry != null ) {
363+
final EntityPersister persister = valueEntry.getPersister();
364+
final String entityName = persister.getEntityName();
365+
if ( LOG.isTraceEnabled() ) {
366+
LOG.tracev(
367+
"Deleting orphaned entity instance: {0}",
368+
infoString( entityName, persister.getIdentifier( loadedValue, eventSource ) )
369+
);
357370
}
358371

359-
if ( valueEntry != null ) {
360-
final EntityPersister persister = valueEntry.getPersister();
361-
final String entityName = persister.getEntityName();
362-
if ( LOG.isTraceEnabled() ) {
363-
LOG.tracev(
364-
"Deleting orphaned entity instance: {0}",
365-
infoString( entityName, persister.getIdentifier( loadedValue, eventSource ) )
366-
);
367-
}
368-
369-
final Object loaded = loadedValue;
370-
if ( type.isAssociationType()
371-
&& ( (AssociationType) type ).getForeignKeyDirection().equals(TO_PARENT) ) {
372-
// If FK direction is to-parent, we must remove the orphan *before* the queued update(s)
373-
// occur. Otherwise, replacing the association on a managed entity, without manually
374-
// nulling and flushing, causes FK constraint violations.
375-
stage = stage.thenCompose( v -> ( (ReactiveSession) eventSource )
376-
.reactiveRemoveOrphanBeforeUpdates( entityName, loaded ) );
377-
}
378-
else {
379-
// Else, we must delete after the updates.
380-
stage = stage.thenCompose( v -> ( (ReactiveSession) eventSource )
381-
.reactiveRemove( entityName, loaded, isCascadeDeleteEnabled, DeleteContext.create() ) );
382-
}
372+
final Object loaded = loadedValue;
373+
if ( type.isAssociationType()
374+
&& ( (AssociationType) type ).getForeignKeyDirection().equals(TO_PARENT) ) {
375+
// If FK direction is to-parent, we must remove the orphan *before* the queued update(s)
376+
// occur. Otherwise, replacing the association on a managed entity, without manually
377+
// nulling and flushing, causes FK constraint violations.
378+
stage = stage.thenCompose( v -> ( (ReactiveSession) eventSource )
379+
.reactiveRemoveOrphanBeforeUpdates( entityName, loaded ) );
380+
}
381+
else {
382+
// Else, we must delete after the updates.
383+
stage = stage.thenCompose( v -> ( (ReactiveSession) eventSource )
384+
.reactiveRemove( entityName, loaded, isCascadeDeleteEnabled, DeleteContext.create() ) );
383385
}
384386
}
385387
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveFlushingEventListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ protected void logFlushResults(FlushEvent event) {
135135
persistenceContext.getCollectionEntriesSize()
136136
);
137137
new EntityPrinter( session.getFactory() ).toString(
138-
persistenceContext.getEntitiesByKey().entrySet()
138+
persistenceContext.getEntityHoldersByKey().entrySet()
139139
);
140140
}
141141

hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveInitializeCollectionEventListener.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
*/
66
package org.hibernate.reactive.event.impl;
77

8-
import static org.hibernate.pretty.MessageHelper.collectionInfoString;
9-
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture;
10-
118
import java.lang.invoke.MethodHandles;
129
import java.util.concurrent.CompletionStage;
1310

@@ -29,6 +26,9 @@
2926
import org.hibernate.sql.results.internal.ResultsHelper;
3027
import org.hibernate.stat.spi.StatisticsImplementor;
3128

29+
import static org.hibernate.pretty.MessageHelper.collectionInfoString;
30+
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture;
31+
3232
public class DefaultReactiveInitializeCollectionEventListener implements InitializeCollectionEventListener {
3333

3434
private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );
@@ -144,7 +144,7 @@ private boolean initializeCollectionFromCache(
144144
final SessionFactoryImplementor factory = source.getFactory();
145145
final CollectionDataAccess cacheAccessStrategy = persister.getCacheAccessStrategy();
146146
final Object ck = cacheAccessStrategy.generateCacheKey( id, persister, factory, source.getTenantIdentifier() );
147-
final Object ce = CacheHelper.fromSharedCache( source, ck, cacheAccessStrategy );
147+
final Object ce = CacheHelper.fromSharedCache( source, ck, persister, cacheAccessStrategy );
148148

149149
final StatisticsImplementor statistics = factory.getStatistics();
150150
if ( statistics.isStatisticsEnabled() ) {

hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveRefreshEventListener.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ public CompletionStage<Void> reactiveOnRefresh(RefreshEvent event) throws Hibern
6060

6161
@Override
6262
public void onRefresh(RefreshEvent event) throws HibernateException {
63-
throw new UnsupportedOperationException();
63+
throw LOG.nonReactiveMethodCall( "reactiveOnRefresh" );
6464
}
6565

6666
@Override
6767
public void onRefresh(RefreshEvent event, RefreshContext refreshedAlready) throws HibernateException {
68-
throw new UnsupportedOperationException();
68+
throw LOG.nonReactiveMethodCall( "reactiveOnRefresh" );
6969
}
7070

7171
/**
@@ -83,16 +83,25 @@ public CompletionStage<Void> reactiveOnRefresh(RefreshEvent event, RefreshContex
8383

8484
if ( detached ) {
8585
// Hibernate Reactive doesn't support detached instances in refresh()
86-
throw new IllegalArgumentException("unmanaged instance passed to refresh()");
86+
throw new IllegalArgumentException( "Unmanaged instance passed to refresh()" );
8787
}
88-
return ( (ReactiveSession) source ).reactiveFetch( event.getObject(), true )
88+
return ( (ReactiveSession) source )
89+
.reactiveFetch( event.getObject(), true )
8990
.thenCompose( entity -> reactiveOnRefresh( event, refreshedAlready, entity ) );
9091
}
9192

92-
private CompletionStage<Void> reactiveOnRefresh(RefreshEvent event, RefreshContext refreshedAlready, Object entity) {
93+
private CompletionStage<Void> reactiveOnRefresh(RefreshEvent event, RefreshContext refreshedAlready, Object object) {
9394
final EventSource source = event.getSession();
9495
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
9596

97+
if ( persistenceContext.reassociateIfUninitializedProxy( object ) ) {
98+
if ( isTransient( event, source, object ) ) {
99+
source.setReadOnly( object, source.isDefaultReadOnly() );
100+
}
101+
return voidFuture();
102+
}
103+
104+
Object entity = persistenceContext.unproxyAndReassociate( object );
96105
if ( !refreshedAlready.add( entity) ) {
97106
LOG.trace( "Already refreshed" );
98107
return voidFuture();
@@ -169,6 +178,11 @@ private CompletionStage<Void> reactiveOnRefresh(RefreshEvent event, RefreshConte
169178
} );
170179
}
171180

181+
private static boolean isTransient(RefreshEvent event, EventSource source, Object object) {
182+
final String entityName = event.getEntityName();
183+
return entityName != null ? !source.contains( entityName, object) : !source.contains(object);
184+
}
185+
172186
private static void evictEntity(Object entity, EntityPersister persister, Object id, EventSource source) {
173187
if ( persister.canWriteToCache() ) {
174188
Object previousVersion = null;

hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ExecutionContextWithSubselectFetchHandler.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
*/
66
package org.hibernate.reactive.loader.ast.internal;
77

8-
import org.hibernate.engine.spi.EntityKey;
8+
import org.hibernate.engine.spi.EntityHolder;
99
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1010
import org.hibernate.engine.spi.SubselectFetch;
1111
import org.hibernate.sql.exec.internal.BaseExecutionContext;
12-
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
1312

1413
/**
1514
* Copy and paste of {@link org.hibernate.loader.ast.internal.ExecutionContextWithSubselectFetchHandler}
@@ -25,9 +24,9 @@ public ExecutionContextWithSubselectFetchHandler(SharedSessionContractImplemento
2524
}
2625

2726
@Override
28-
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
27+
public void registerLoadingEntityHolder(EntityHolder holder) {
2928
if ( subSelectFetchableKeysHandler != null ) {
30-
subSelectFetchableKeysHandler.addKey( entityKey, entry );
29+
subSelectFetchableKeysHandler.addKey( holder );
3130
}
3231
}
3332

0 commit comments

Comments
 (0)