10
10
11
11
import org .hibernate .AssertionFailure ;
12
12
import org .hibernate .HibernateException ;
13
+ import org .hibernate .bytecode .enhance .spi .interceptor .EnhancementAsProxyLazinessInterceptor ;
13
14
import org .hibernate .engine .spi .EntityKey ;
14
15
import org .hibernate .engine .spi .EntityUniqueKey ;
15
16
import org .hibernate .engine .spi .PersistenceContext ;
17
+ import org .hibernate .engine .spi .PersistentAttributeInterceptor ;
16
18
import org .hibernate .engine .spi .SessionFactoryImplementor ;
17
19
import org .hibernate .engine .spi .SessionImplementor ;
18
20
import org .hibernate .engine .spi .SharedSessionContractImplementor ;
19
21
import org .hibernate .persister .entity .EntityPersister ;
22
+ import org .hibernate .proxy .HibernateProxy ;
23
+ import org .hibernate .proxy .LazyInitializer ;
20
24
import org .hibernate .reactive .persister .entity .impl .ReactiveEntityPersister ;
21
25
import org .hibernate .reactive .session .impl .ReactiveQueryExecutorLookup ;
22
26
import org .hibernate .reactive .session .impl .ReactiveSessionImpl ;
27
+ import org .hibernate .reactive .util .impl .CompletionStages ;
23
28
import org .hibernate .type .EntityType ;
24
29
import org .hibernate .type .ForeignKeyDirection ;
25
30
import org .hibernate .type .OneToOneType ;
26
31
import org .hibernate .type .Type ;
27
32
28
33
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 ;
29
36
import static org .hibernate .property .access .internal .PropertyAccessStrategyBackRefImpl .UNKNOWN ;
37
+ import static org .hibernate .proxy .HibernateProxy .extractLazyInitializer ;
30
38
import static org .hibernate .reactive .engine .impl .ForeignKeys .getEntityIdentifierIfNotUnsaved ;
39
+ import static org .hibernate .reactive .session .impl .SessionUtil .checkEntityFound ;
31
40
import static org .hibernate .reactive .util .impl .CompletionStages .completedFuture ;
32
41
import static org .hibernate .reactive .util .impl .CompletionStages .loop ;
33
42
import static org .hibernate .reactive .util .impl .CompletionStages .nullFuture ;
43
+ import static org .hibernate .reactive .util .impl .CompletionStages .voidFuture ;
34
44
35
45
/**
36
46
* Reactive operations that really belong to {@link EntityType}
@@ -124,14 +134,12 @@ static CompletionStage<Object> loadByUniqueKey(
124
134
else {
125
135
return persister
126
136
.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
+ );
135
143
136
144
}
137
145
}
@@ -173,7 +181,15 @@ public static CompletionStage<Object[]> replace(
173
181
session ,
174
182
owner ,
175
183
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
+ } )
177
193
).thenApply ( v -> copied );
178
194
}
179
195
@@ -207,17 +223,25 @@ public static CompletionStage<Object[]> replace(
207
223
}
208
224
}
209
225
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
+ } )
221
245
).thenApply ( v -> copied );
222
246
}
223
247
@@ -311,6 +335,12 @@ private static CompletionStage<Object> resolveIdOrUniqueKey(
311
335
.thenCompose ( fetched -> {
312
336
Object idOrUniqueKey = entityType .getIdentifierOrUniqueKeyType ( session .getFactory () )
313
337
.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
+
314
344
return resolve ( entityType , idOrUniqueKey , owner , session );
315
345
} );
316
346
} );
@@ -319,7 +349,10 @@ private static CompletionStage<Object> resolveIdOrUniqueKey(
319
349
/**
320
350
* see EntityType#getIdentifier(Object, SharedSessionContractImplementor)
321
351
*/
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 ) {
323
356
if ( entityType .isReferenceToIdentifierProperty () ) {
324
357
// tolerates nulls
325
358
return getEntityIdentifierIfNotUnsaved ( entityType .getAssociatedEntityName (), value , session );
@@ -328,17 +361,86 @@ private static CompletionStage<Object> getIdentifier(EntityType entityType, Obje
328
361
return nullFuture ();
329
362
}
330
363
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 () );
332
389
String uniqueKeyPropertyName = entityType .getRHSUniqueKeyPropertyName ();
333
390
Object propertyValue = entityPersister .getPropertyValue ( value , uniqueKeyPropertyName );
334
391
// We now have the value of the property-ref we reference. However,
335
392
// we need to dig a little deeper, as that property might also be
336
393
// 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 );
338
395
if ( type .isEntityType () ) {
339
396
propertyValue = getIdentifier ( (EntityType ) type , propertyValue , session );
340
397
}
341
398
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
+ }
342
444
}
343
445
344
446
}
0 commit comments