6
6
package org .hibernate .reactive .engine .impl ;
7
7
8
8
import java .util .Map ;
9
+ import java .util .concurrent .CompletableFuture ;
9
10
import java .util .concurrent .CompletionStage ;
10
11
11
12
import org .hibernate .AssertionFailure ;
12
13
import org .hibernate .HibernateException ;
14
+ import org .hibernate .bytecode .enhance .spi .interceptor .EnhancementAsProxyLazinessInterceptor ;
13
15
import org .hibernate .engine .spi .EntityKey ;
14
16
import org .hibernate .engine .spi .EntityUniqueKey ;
15
17
import org .hibernate .engine .spi .PersistenceContext ;
18
+ import org .hibernate .engine .spi .PersistentAttributeInterceptor ;
16
19
import org .hibernate .engine .spi .SessionFactoryImplementor ;
17
20
import org .hibernate .engine .spi .SessionImplementor ;
18
21
import org .hibernate .engine .spi .SharedSessionContractImplementor ;
19
22
import org .hibernate .persister .entity .EntityPersister ;
23
+ import org .hibernate .proxy .HibernateProxy ;
24
+ import org .hibernate .proxy .LazyInitializer ;
20
25
import org .hibernate .reactive .persister .entity .impl .ReactiveEntityPersister ;
21
26
import org .hibernate .reactive .session .impl .ReactiveQueryExecutorLookup ;
22
27
import org .hibernate .reactive .session .impl .ReactiveSessionImpl ;
28
+ import org .hibernate .reactive .util .impl .CompletionStages ;
23
29
import org .hibernate .type .EntityType ;
24
30
import org .hibernate .type .ForeignKeyDirection ;
25
31
import org .hibernate .type .OneToOneType ;
26
32
import org .hibernate .type .Type ;
27
33
28
34
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 ;
29
37
import static org .hibernate .property .access .internal .PropertyAccessStrategyBackRefImpl .UNKNOWN ;
38
+ import static org .hibernate .proxy .HibernateProxy .extractLazyInitializer ;
30
39
import static org .hibernate .reactive .engine .impl .ForeignKeys .getEntityIdentifierIfNotUnsaved ;
40
+ import static org .hibernate .reactive .session .impl .SessionUtil .checkEntityFound ;
31
41
import static org .hibernate .reactive .util .impl .CompletionStages .completedFuture ;
32
42
import static org .hibernate .reactive .util .impl .CompletionStages .loop ;
33
43
import static org .hibernate .reactive .util .impl .CompletionStages .nullFuture ;
@@ -124,13 +134,29 @@ static CompletionStage<Object> loadByUniqueKey(
124
134
else {
125
135
return persister
126
136
.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
+
128
154
// If the entity was not in the Persistence Context, but was found now,
129
155
// add it to the Persistence Context
130
- if ( loaded != null ) {
131
- persistenceContext .addEntity ( euk , loaded );
156
+ if ( loadedUniqueKey != null ) {
157
+ persistenceContext .addEntity ( euk , loadedUniqueKey );
132
158
}
133
- return loaded ;
159
+ return loadedUniqueKey ;
134
160
} );
135
161
136
162
}
@@ -311,15 +337,28 @@ private static CompletionStage<Object> resolveIdOrUniqueKey(
311
337
.thenCompose ( fetched -> {
312
338
Object idOrUniqueKey = entityType .getIdentifierOrUniqueKeyType ( session .getFactory () )
313
339
.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 );
315
351
} );
316
352
} );
317
353
}
318
354
319
355
/**
320
356
* see EntityType#getIdentifier(Object, SharedSessionContractImplementor)
321
357
*/
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 ) {
323
362
if ( entityType .isReferenceToIdentifierProperty () ) {
324
363
// tolerates nulls
325
364
return getEntityIdentifierIfNotUnsaved ( entityType .getAssociatedEntityName (), value , session );
@@ -328,17 +367,79 @@ private static CompletionStage<Object> getIdentifier(EntityType entityType, Obje
328
367
return nullFuture ();
329
368
}
330
369
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 () );
332
395
String uniqueKeyPropertyName = entityType .getRHSUniqueKeyPropertyName ();
333
396
Object propertyValue = entityPersister .getPropertyValue ( value , uniqueKeyPropertyName );
334
397
// We now have the value of the property-ref we reference. However,
335
398
// we need to dig a little deeper, as that property might also be
336
399
// 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 );
338
401
if ( type .isEntityType () ) {
339
402
propertyValue = getIdentifier ( (EntityType ) type , propertyValue , session );
340
403
}
341
404
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
+ } );
342
443
}
343
444
344
445
}
0 commit comments