Skip to content

Commit f65cdec

Browse files
blafondDavideD
authored andcommitted
[#1504] Fix EntityTypes to handle proxy entity references
1 parent 52b5fd0 commit f65cdec

File tree

1 file changed

+125
-23
lines changed
  • hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl

1 file changed

+125
-23
lines changed

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

Lines changed: 125 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,37 @@
1010

1111
import org.hibernate.AssertionFailure;
1212
import org.hibernate.HibernateException;
13+
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
1314
import org.hibernate.engine.spi.EntityKey;
1415
import org.hibernate.engine.spi.EntityUniqueKey;
1516
import org.hibernate.engine.spi.PersistenceContext;
17+
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
1618
import org.hibernate.engine.spi.SessionFactoryImplementor;
1719
import org.hibernate.engine.spi.SessionImplementor;
1820
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1921
import org.hibernate.persister.entity.EntityPersister;
22+
import org.hibernate.proxy.HibernateProxy;
23+
import org.hibernate.proxy.LazyInitializer;
2024
import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister;
2125
import org.hibernate.reactive.session.impl.ReactiveQueryExecutorLookup;
2226
import org.hibernate.reactive.session.impl.ReactiveSessionImpl;
27+
import org.hibernate.reactive.util.impl.CompletionStages;
2328
import org.hibernate.type.EntityType;
2429
import org.hibernate.type.ForeignKeyDirection;
2530
import org.hibernate.type.OneToOneType;
2631
import org.hibernate.type.Type;
2732

2833
import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY;
34+
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
35+
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable;
2936
import static org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl.UNKNOWN;
37+
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
3038
import static org.hibernate.reactive.engine.impl.ForeignKeys.getEntityIdentifierIfNotUnsaved;
39+
import static org.hibernate.reactive.session.impl.SessionUtil.checkEntityFound;
3140
import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture;
3241
import static org.hibernate.reactive.util.impl.CompletionStages.loop;
3342
import static org.hibernate.reactive.util.impl.CompletionStages.nullFuture;
43+
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture;
3444

3545
/**
3646
* Reactive operations that really belong to {@link EntityType}
@@ -124,14 +134,12 @@ static CompletionStage<Object> loadByUniqueKey(
124134
else {
125135
return persister
126136
.reactiveLoadByUniqueKey( uniqueKeyPropertyName, key, session )
127-
.thenApply( loaded -> {
128-
// If the entity was not in the Persistence Context, but was found now,
129-
// add it to the Persistence Context
130-
if ( loaded != null ) {
131-
persistenceContext.addEntity( euk, loaded );
132-
}
133-
return loaded;
134-
} );
137+
.thenApply( ukResult -> loadHibernateProxyEntity( ukResult, session )
138+
.thenApply( targetUK -> {
139+
persistenceContext.addEntity( euk, targetUK );
140+
return targetUK;
141+
} )
142+
);
135143

136144
}
137145
}
@@ -173,7 +181,15 @@ public static CompletionStage<Object[]> replace(
173181
session,
174182
owner,
175183
copyCache
176-
).thenAccept( copy -> copied[i] = copy )
184+
).thenCompose( copy -> {
185+
if ( copy instanceof CompletionStage ) {
186+
return ( (CompletionStage) copy ).thenAccept( nonStageCopy -> copied[i] = nonStageCopy );
187+
}
188+
else {
189+
copied[i] = copy;
190+
return voidFuture();
191+
}
192+
} )
177193
).thenApply( v -> copied );
178194
}
179195

@@ -207,17 +223,25 @@ public static CompletionStage<Object[]> replace(
207223
}
208224
}
209225
return loop( 0, types.length,
210-
i -> original[i] != UNFETCHED_PROPERTY && original[i] != UNKNOWN
211-
&& types[i] instanceof EntityType,
212-
i -> replace(
213-
(EntityType) types[i],
214-
original[i],
215-
target[i] == UNFETCHED_PROPERTY ? null : target[i],
216-
session,
217-
owner,
218-
copyCache,
219-
foreignKeyDirection
220-
).thenAccept( copy -> copied[i] = copy )
226+
i -> original[i] != UNFETCHED_PROPERTY && original[i] != UNKNOWN
227+
&& types[i] instanceof EntityType,
228+
i -> replace(
229+
(EntityType) types[i],
230+
original[i],
231+
target[i] == UNFETCHED_PROPERTY ? null : target[i],
232+
session,
233+
owner,
234+
copyCache,
235+
foreignKeyDirection
236+
).thenCompose( copy -> {
237+
if ( copy instanceof CompletionStage ) {
238+
return ( (CompletionStage) copy ).thenAccept( nonStageCopy -> copied[i] = nonStageCopy );
239+
}
240+
else {
241+
copied[i] = copy;
242+
return voidFuture();
243+
}
244+
} )
221245
).thenApply( v -> copied );
222246
}
223247

@@ -311,6 +335,12 @@ private static CompletionStage<Object> resolveIdOrUniqueKey(
311335
.thenCompose( fetched -> {
312336
Object idOrUniqueKey = entityType.getIdentifierOrUniqueKeyType( session.getFactory() )
313337
.replace( fetched, null, session, owner, copyCache );
338+
if ( idOrUniqueKey instanceof CompletionStage ) {
339+
return ( (CompletionStage) idOrUniqueKey ).thenCompose(
340+
key -> resolve( entityType, key, owner, session )
341+
);
342+
}
343+
314344
return resolve( entityType, idOrUniqueKey, owner, session );
315345
} );
316346
} );
@@ -319,7 +349,10 @@ private static CompletionStage<Object> resolveIdOrUniqueKey(
319349
/**
320350
* see EntityType#getIdentifier(Object, SharedSessionContractImplementor)
321351
*/
322-
private static CompletionStage<Object> getIdentifier(EntityType entityType, Object value, SessionImplementor session) {
352+
private static CompletionStage<Object> getIdentifier(
353+
EntityType entityType,
354+
Object value,
355+
SessionImplementor session) {
323356
if ( entityType.isReferenceToIdentifierProperty() ) {
324357
// tolerates nulls
325358
return getEntityIdentifierIfNotUnsaved( entityType.getAssociatedEntityName(), value, session );
@@ -328,17 +361,86 @@ private static CompletionStage<Object> getIdentifier(EntityType entityType, Obje
328361
return nullFuture();
329362
}
330363

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

344446
}

0 commit comments

Comments
 (0)