1010
1111import org .hibernate .AssertionFailure ;
1212import org .hibernate .HibernateException ;
13+ import org .hibernate .bytecode .enhance .spi .interceptor .EnhancementAsProxyLazinessInterceptor ;
1314import org .hibernate .engine .spi .EntityKey ;
1415import org .hibernate .engine .spi .EntityUniqueKey ;
1516import org .hibernate .engine .spi .PersistenceContext ;
17+ import org .hibernate .engine .spi .PersistentAttributeInterceptor ;
1618import org .hibernate .engine .spi .SessionFactoryImplementor ;
1719import org .hibernate .engine .spi .SessionImplementor ;
1820import org .hibernate .engine .spi .SharedSessionContractImplementor ;
1921import org .hibernate .persister .entity .EntityPersister ;
22+ import org .hibernate .proxy .HibernateProxy ;
23+ import org .hibernate .proxy .LazyInitializer ;
2024import org .hibernate .reactive .persister .entity .impl .ReactiveEntityPersister ;
2125import org .hibernate .reactive .session .impl .ReactiveQueryExecutorLookup ;
2226import org .hibernate .reactive .session .impl .ReactiveSessionImpl ;
27+ import org .hibernate .reactive .util .impl .CompletionStages ;
2328import org .hibernate .type .EntityType ;
2429import org .hibernate .type .ForeignKeyDirection ;
2530import org .hibernate .type .OneToOneType ;
2631import org .hibernate .type .Type ;
2732
2833import 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 ;
2936import static org .hibernate .property .access .internal .PropertyAccessStrategyBackRefImpl .UNKNOWN ;
37+ import static org .hibernate .proxy .HibernateProxy .extractLazyInitializer ;
3038import static org .hibernate .reactive .engine .impl .ForeignKeys .getEntityIdentifierIfNotUnsaved ;
39+ import static org .hibernate .reactive .session .impl .SessionUtil .checkEntityFound ;
3140import static org .hibernate .reactive .util .impl .CompletionStages .completedFuture ;
3241import static org .hibernate .reactive .util .impl .CompletionStages .loop ;
3342import 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