Skip to content

Commit 42cd05f

Browse files
committed
[#1504] Added forced loading of proxy entity references
1 parent dca2bcf commit 42cd05f

File tree

1 file changed

+109
-8
lines changed
  • hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl

1 file changed

+109
-8
lines changed

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

Lines changed: 109 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,38 @@
66
package org.hibernate.reactive.engine.impl;
77

88
import java.util.Map;
9+
import java.util.concurrent.CompletableFuture;
910
import java.util.concurrent.CompletionStage;
1011

1112
import org.hibernate.AssertionFailure;
1213
import org.hibernate.HibernateException;
14+
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
1315
import org.hibernate.engine.spi.EntityKey;
1416
import org.hibernate.engine.spi.EntityUniqueKey;
1517
import org.hibernate.engine.spi.PersistenceContext;
18+
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
1619
import org.hibernate.engine.spi.SessionFactoryImplementor;
1720
import org.hibernate.engine.spi.SessionImplementor;
1821
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1922
import org.hibernate.persister.entity.EntityPersister;
23+
import org.hibernate.proxy.HibernateProxy;
24+
import org.hibernate.proxy.LazyInitializer;
2025
import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister;
2126
import org.hibernate.reactive.session.impl.ReactiveQueryExecutorLookup;
2227
import org.hibernate.reactive.session.impl.ReactiveSessionImpl;
28+
import org.hibernate.reactive.util.impl.CompletionStages;
2329
import org.hibernate.type.EntityType;
2430
import org.hibernate.type.ForeignKeyDirection;
2531
import org.hibernate.type.OneToOneType;
2632
import org.hibernate.type.Type;
2733

2834
import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY;
35+
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
36+
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable;
2937
import static org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl.UNKNOWN;
38+
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
3039
import static org.hibernate.reactive.engine.impl.ForeignKeys.getEntityIdentifierIfNotUnsaved;
40+
import static org.hibernate.reactive.session.impl.SessionUtil.checkEntityFound;
3141
import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture;
3242
import static org.hibernate.reactive.util.impl.CompletionStages.loop;
3343
import static org.hibernate.reactive.util.impl.CompletionStages.nullFuture;
@@ -124,13 +134,29 @@ static CompletionStage<Object> loadByUniqueKey(
124134
else {
125135
return persister
126136
.reactiveLoadByUniqueKey( uniqueKeyPropertyName, key, session )
127-
.thenApply( loaded -> {
137+
.thenApply( ukResult -> {
138+
Object loadedUniqueKey = ukResult;
139+
// The unique key may be a HibernateProxy object and needs to be loaded before
140+
// adding to persistenceContext
141+
if ( loadedUniqueKey instanceof HibernateProxy ) {
142+
try {
143+
CompletionStage<Object> resultObject = loadHibernateProxyEntity(
144+
(HibernateProxy) loadedUniqueKey,
145+
session
146+
);
147+
loadedUniqueKey = ((CompletableFuture)resultObject).get();
148+
}
149+
catch (Exception e) {
150+
throw new RuntimeException( e );
151+
}
152+
}
153+
128154
// If the entity was not in the Persistence Context, but was found now,
129155
// add it to the Persistence Context
130-
if ( loaded != null ) {
131-
persistenceContext.addEntity( euk, loaded );
156+
if ( loadedUniqueKey != null ) {
157+
persistenceContext.addEntity( euk, loadedUniqueKey );
132158
}
133-
return loaded;
159+
return loadedUniqueKey;
134160
} );
135161

136162
}
@@ -311,15 +337,28 @@ private static CompletionStage<Object> resolveIdOrUniqueKey(
311337
.thenCompose( fetched -> {
312338
Object idOrUniqueKey = entityType.getIdentifierOrUniqueKeyType( session.getFactory() )
313339
.replace( fetched, null, session, owner, copyCache );
314-
return resolve( entityType, idOrUniqueKey, owner, session );
340+
Object resultObject = idOrUniqueKey;
341+
if( resultObject instanceof CompletableFuture ) {
342+
try {
343+
resultObject = ((CompletableFuture)idOrUniqueKey).get();
344+
}
345+
catch (Exception e) {
346+
throw new RuntimeException( e );
347+
}
348+
}
349+
350+
return resolve( entityType, resultObject, owner, session );
315351
} );
316352
} );
317353
}
318354

319355
/**
320356
* see EntityType#getIdentifier(Object, SharedSessionContractImplementor)
321357
*/
322-
private static CompletionStage<Object> getIdentifier(EntityType entityType, Object value, SessionImplementor session) {
358+
private static CompletionStage<Object> getIdentifier(
359+
EntityType entityType,
360+
Object value,
361+
SessionImplementor session) {
323362
if ( entityType.isReferenceToIdentifierProperty() ) {
324363
// tolerates nulls
325364
return getEntityIdentifierIfNotUnsaved( entityType.getAssociatedEntityName(), value, session );
@@ -328,17 +367,79 @@ private static CompletionStage<Object> getIdentifier(EntityType entityType, Obje
328367
return nullFuture();
329368
}
330369

331-
EntityPersister entityPersister = entityType.getAssociatedEntityPersister( session.getFactory() );
370+
if( value instanceof HibernateProxy ) {
371+
return getIdentifierFromHibernateProxy( entityType, (HibernateProxy)value, session );
372+
}
373+
374+
final LazyInitializer lazyInitializer = extractLazyInitializer( value );
375+
if ( lazyInitializer != null ) {
376+
/*
377+
If the value is a Proxy and the property access is field, the value returned by
378+
`attributeMapping.getAttributeMetadata().getPropertyAccess().getGetter().get( object )`
379+
is always null except for the id, we need the to use the proxy implementation to
380+
extract the property value.
381+
*/
382+
value = lazyInitializer.getImplementation();
383+
}
384+
else if ( isPersistentAttributeInterceptable( value ) ) {
385+
/*
386+
If the value is an instance of PersistentAttributeInterceptable, and it is not initialized
387+
we need to force initialization the get the property value
388+
*/
389+
final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable( value ).$$_hibernate_getInterceptor();
390+
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
391+
( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( value, null );
392+
}
393+
}
394+
final EntityPersister entityPersister = entityType.getAssociatedEntityPersister( session.getFactory() );
332395
String uniqueKeyPropertyName = entityType.getRHSUniqueKeyPropertyName();
333396
Object propertyValue = entityPersister.getPropertyValue( value, uniqueKeyPropertyName );
334397
// We now have the value of the property-ref we reference. However,
335398
// we need to dig a little deeper, as that property might also be
336399
// an entity type, in which case we need to resolve its identifier
337-
Type type = entityPersister.getPropertyType( uniqueKeyPropertyName );
400+
final Type type = entityPersister.getPropertyType( uniqueKeyPropertyName );
338401
if ( type.isEntityType() ) {
339402
propertyValue = getIdentifier( (EntityType) type, propertyValue, session );
340403
}
341404
return completedFuture( propertyValue );
405+
406+
}
407+
408+
private static CompletionStage<Object> getIdentifierFromHibernateProxy(EntityType entityType, HibernateProxy proxy, SharedSessionContractImplementor session) {
409+
LazyInitializer initializer = proxy.getHibernateLazyInitializer();
410+
final String entityName = initializer.getEntityName();
411+
final Object identifier = initializer.getIdentifier();
412+
return ( (ReactiveSessionImpl) session ).reactiveImmediateLoad( entityName, identifier )
413+
.thenApply( entity -> {
414+
checkEntityFound( session, entityName, identifier, entity );
415+
initializer.setSession( session );
416+
initializer.setImplementation( entity );
417+
if ( entity != null ) {
418+
final EntityPersister entityPersister = entityType.getAssociatedEntityPersister( session.getFactory() );
419+
String uniqueKeyPropertyName = entityType.getRHSUniqueKeyPropertyName();
420+
Object propertyValue = entityPersister.getPropertyValue( entity, uniqueKeyPropertyName );
421+
// We now have the value of the property-ref we reference. However,
422+
// we need to dig a little deeper, as that property might also be
423+
// an entity type, in which case we need to resolve its identifier
424+
final Type type = entityPersister.getPropertyType( uniqueKeyPropertyName );
425+
if ( type.isEntityType() ) {
426+
propertyValue = getIdentifier( (EntityType) type, propertyValue, (SessionImplementor) session );
427+
}
428+
return completedFuture( propertyValue );
429+
}
430+
return CompletionStages.nullFuture();
431+
} );
432+
}
433+
434+
private static CompletionStage<Object> loadHibernateProxyEntity( HibernateProxy proxy, SharedSessionContractImplementor session) {
435+
LazyInitializer initializer = proxy.getHibernateLazyInitializer();
436+
final String entityName = initializer.getEntityName();
437+
final Object identifier = initializer.getIdentifier();
438+
return ( (ReactiveSessionImpl) session ).reactiveImmediateLoad( entityName, identifier )
439+
.thenApply( entity -> {
440+
checkEntityFound( session, entityName, identifier, entity );
441+
return entity;
442+
} );
342443
}
343444

344445
}

0 commit comments

Comments
 (0)