15
15
import org .hibernate .PersistentObjectException ;
16
16
import org .hibernate .TypeMismatchException ;
17
17
import org .hibernate .action .internal .DelayedPostInsertIdentifier ;
18
+ import org .hibernate .bytecode .enhance .spi .interceptor .EnhancementAsProxyLazinessInterceptor ;
18
19
import org .hibernate .cache .spi .access .EntityDataAccess ;
19
20
import org .hibernate .cache .spi .access .SoftLock ;
20
- import org .hibernate .engine .spi .EntityEntry ;
21
21
import org .hibernate .engine .spi .EntityKey ;
22
22
import org .hibernate .engine .spi .PersistenceContext ;
23
23
import org .hibernate .engine .spi .PersistentAttributeInterceptable ;
24
+ import org .hibernate .engine .spi .PersistentAttributeInterceptor ;
24
25
import org .hibernate .engine .spi .SessionImplementor ;
25
- import org .hibernate .engine .spi .Status ;
26
+ import org .hibernate .engine .spi .SharedSessionContractImplementor ;
26
27
import org .hibernate .event .spi .EventSource ;
27
28
import org .hibernate .event .spi .LoadEvent ;
28
- import org .hibernate .event .spi .LoadEventListener ;import org . hibernate . event . spi . LoadEventListener . LoadType ;
29
+ import org .hibernate .event .spi .LoadEventListener ;
29
30
import org .hibernate .loader .ast .internal .CacheEntityLoaderHelper ;
31
+ import org .hibernate .loader .ast .internal .CacheEntityLoaderHelper .PersistenceContextEntry ;
30
32
import org .hibernate .metamodel .mapping .AttributeMapping ;
31
33
import org .hibernate .metamodel .mapping .AttributeMappingsList ;
32
34
import org .hibernate .metamodel .mapping .CompositeIdentifierMapping ;
42
44
import org .hibernate .reactive .logging .impl .Log ;
43
45
import org .hibernate .reactive .logging .impl .LoggerFactory ;
44
46
import org .hibernate .reactive .persister .entity .impl .ReactiveEntityPersister ;
47
+ import org .hibernate .reactive .session .impl .ReactiveQueryExecutorLookup ;
45
48
import org .hibernate .stat .spi .StatisticsImplementor ;
46
49
50
+ import static org .hibernate .engine .internal .ManagedTypeHelper .asPersistentAttributeInterceptable ;
51
+ import static org .hibernate .engine .internal .ManagedTypeHelper .isPersistentAttributeInterceptable ;
47
52
import static org .hibernate .pretty .MessageHelper .infoString ;
48
53
import static org .hibernate .reactive .session .impl .SessionUtil .checkEntityFound ;
49
54
import static org .hibernate .reactive .session .impl .SessionUtil .throwEntityNotFound ;
@@ -91,7 +96,7 @@ public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException
91
96
// Since this method is not reactive, we're not expecting to hit the
92
97
// database here (if we do, it's a bug) and so we can assume the
93
98
// returned CompletionStage is already completed
94
- CompletionStage <Void > checkId = checkId ( event , loadType , persister );
99
+ final CompletionStage <Void > checkId = checkId ( event , loadType , persister );
95
100
if ( !checkId .toCompletableFuture ().isDone () ) {
96
101
// This only happens if the object is loaded from the db
97
102
throw new UnexpectedAccessToTheDatabase ();
@@ -101,7 +106,7 @@ public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException
101
106
// Since this method is not reactive, we're not expecting to hit the
102
107
// database here (if we do, it's a bug) and so we can assume the
103
108
// returned CompletionStage is already completed (a proxy, perhaps)
104
- CompletionStage <Object > loaded = doOnLoad ( persister , event , loadType );
109
+ final CompletionStage <Object > loaded = doOnLoad ( persister , event , loadType );
105
110
if ( !loaded .toCompletableFuture ().isDone () ) {
106
111
// This only happens if the object is loaded from the db
107
112
throw new UnexpectedAccessToTheDatabase ();
@@ -244,7 +249,8 @@ else if ( idMapping instanceof NonAggregatedIdentifierMapping ) {
244
249
}
245
250
}
246
251
}
247
- throw new TypeMismatchException ( "Provided id of the wrong type for class " + persister .getEntityName () + ". Expected: " + idClass + ", got " + event .getEntityId ().getClass () );
252
+ throw new TypeMismatchException ( "Provided id of the wrong type for class " + persister .getEntityName ()
253
+ + ". Expected: " + idClass + ", got " + event .getEntityId ().getClass () );
248
254
}
249
255
250
256
/*
@@ -338,8 +344,7 @@ else if ( persister.hasProxy() ) {
338
344
}
339
345
340
346
private static boolean wasDeleted (PersistenceContext persistenceContext , Object existing ) {
341
- final Status status = persistenceContext .getEntry ( existing ).getStatus ();
342
- return status == Status .DELETED || status == Status .GONE ;
347
+ return persistenceContext .getEntry ( existing ).getStatus ().isDeletedOrGone ();
343
348
}
344
349
345
350
private CompletionStage <Object > loadWithBytecodeProxy (LoadEvent event , EntityPersister persister , EntityKey keyToLoad , LoadType options ) {
@@ -372,12 +377,12 @@ private CompletionStage<Object> loadWithRegularProxy(LoadEvent event, EntityPers
372
377
final Object proxy = event .getSession ().getPersistenceContextInternal ().getProxy ( keyToLoad );
373
378
if ( proxy != null ) {
374
379
// narrow the existing proxy to the type we're looking for
375
- return returnNarrowedProxy ( event , persister , keyToLoad , options , proxy );
380
+ return narrowedProxy ( event , persister , keyToLoad , options , proxy );
376
381
}
377
382
else if ( options .isAllowProxyCreation () ) {
378
383
// return a new proxy
379
384
// ORM calls DefaultLoadEventListener#proxyOrCache
380
- return completedFuture ( createProxyIfNecessary ( event , persister , keyToLoad , options ) );
385
+ return completedFuture ( proxyOrCached ( event , persister , keyToLoad , options ) );
381
386
}
382
387
else {
383
388
return load ( event , persister , keyToLoad , options );
@@ -404,9 +409,7 @@ private static CompletionStage<Object> loadWithProxyFactory(LoadEvent event, Ent
404
409
}
405
410
else if ( persister .hasSubclasses () ) {
406
411
// specialized handling for entities with subclasses with a HibernateProxy factory
407
- // entities with subclasses that define a ProxyFactory can create a HibernateProxy
408
- // Maybe we can get it from the cache: Check DefaultLoadEventListener#proxyOrCache
409
- return completedFuture ( createProxy ( event , persister , keyToLoad ) );
412
+ return completedFuture ( proxyOrCached ( event , persister , keyToLoad ) );
410
413
}
411
414
else {
412
415
// no existing proxy, and no subclasses
@@ -427,22 +430,40 @@ private static PersistentAttributeInterceptable createBatchLoadableEnhancedProxy
427
430
return persister .getBytecodeEnhancementMetadata ().createEnhancedProxy ( keyToLoad , true , session );
428
431
}
429
432
430
- private CompletionStage <Object > proxyImplementation (LoadEvent event , EntityPersister persister , EntityKey keyToLoad , LoadType options ) {
431
- return load ( event , persister , keyToLoad , options )
432
- .thenApply ( optional -> {
433
- if (optional != null ) {
434
- return optional ;
435
- }
436
- if ( options != LoadEventListener .INTERNAL_LOAD_NULLABLE ) {
437
- // throw an appropriate exception
438
- event .getSession ().getFactory ().getEntityNotFoundDelegate ()
439
- .handleEntityNotFound ( persister .getEntityName (), keyToLoad .getIdentifier () );
440
- }
441
- // Otherwise, if it's INTERNAL_LOAD_NULLABLE, the proxy is
442
- // for a non-existing association mapped as @NotFound.
443
- // Don't throw an exception; just return null.
444
- return null ;
445
- } );
433
+ private static Object proxyOrCached (LoadEvent event , EntityPersister persister , EntityKey keyToLoad ) {
434
+ final Object cachedEntity = CacheEntityLoaderHelper .INSTANCE .loadFromSecondLevelCache (
435
+ event .getSession (),
436
+ null ,
437
+ LockMode .NONE ,
438
+ persister ,
439
+ keyToLoad
440
+ );
441
+ if ( cachedEntity != null ) {
442
+ return cachedEntity ;
443
+ }
444
+ // entities with subclasses that define a ProxyFactory can create a HibernateProxy
445
+ return createProxy ( event , persister , keyToLoad );
446
+ }
447
+
448
+ private static Object proxyOrCached (LoadEvent event , EntityPersister persister , EntityKey keyToLoad , LoadType options ) {
449
+ final PersistenceContext persistenceContext = event .getSession ().getPersistenceContext ();
450
+ final Object existing = persistenceContext .getEntity ( keyToLoad );
451
+ if ( existing != null ) {
452
+ return options .isCheckDeleted () && wasDeleted ( persistenceContext , existing ) ? null : existing ;
453
+ }
454
+ if ( persister .hasSubclasses () ) {
455
+ final Object cachedEntity = CacheEntityLoaderHelper .INSTANCE .loadFromSecondLevelCache (
456
+ event .getSession (),
457
+ null ,
458
+ LockMode .NONE ,
459
+ persister ,
460
+ keyToLoad
461
+ );
462
+ if ( cachedEntity != null ) {
463
+ return cachedEntity ;
464
+ }
465
+ }
466
+ return createProxyIfNecessary ( event , persister , keyToLoad , options );
446
467
}
447
468
448
469
/**
@@ -457,7 +478,7 @@ private CompletionStage<Object> proxyImplementation(LoadEvent event, EntityPersi
457
478
*
458
479
* @return The created/existing proxy
459
480
*/
460
- private CompletionStage <Object > returnNarrowedProxy (
481
+ private CompletionStage <Object > narrowedProxy (
461
482
LoadEvent event ,
462
483
EntityPersister persister ,
463
484
EntityKey keyToLoad ,
@@ -468,23 +489,43 @@ private CompletionStage<Object> returnNarrowedProxy(
468
489
}
469
490
470
491
LazyInitializer li = ( (HibernateProxy ) proxy ).getHibernateLazyInitializer ();
471
-
472
492
if ( li .isUnwrap () ) {
473
493
return completedFuture ( li .getImplementation () );
474
494
}
475
-
476
- final PersistenceContext persistenceContext = event .getSession ().getPersistenceContextInternal ();
477
- if ( options .isAllowProxyCreation () ) {
478
- return completedFuture ( persistenceContext .narrowProxy ( proxy , persister , keyToLoad , null ) );
479
- }
480
495
else {
481
- return proxyImplementation ( event , persister , keyToLoad , options )
482
- .thenApply ( impl -> impl == null
483
- ? null
484
- : persistenceContext .narrowProxy ( proxy , persister , keyToLoad , impl ) );
496
+ final PersistenceContext persistenceContext = event .getSession ().getPersistenceContextInternal ();
497
+ if ( options .isAllowProxyCreation () ) {
498
+ return completedFuture ( persistenceContext .narrowProxy ( proxy , persister , keyToLoad , null ) );
499
+ }
500
+ else {
501
+ return proxyImplementation ( event , persister , keyToLoad , options )
502
+ .thenApply ( impl -> impl == null
503
+ ? null
504
+ : persistenceContext .narrowProxy ( proxy , persister , keyToLoad , impl ) );
505
+ }
485
506
}
486
507
}
487
508
509
+ private CompletionStage <Object > proxyImplementation (LoadEvent event , EntityPersister persister , EntityKey keyToLoad , LoadType options ) {
510
+ return load ( event , persister , keyToLoad , options )
511
+ .thenApply ( optional -> {
512
+ if ( optional != null ) {
513
+ return optional ;
514
+ }
515
+ else {
516
+ if ( options != LoadEventListener .INTERNAL_LOAD_NULLABLE ) {
517
+ // throw an appropriate exception
518
+ event .getSession ().getFactory ().getEntityNotFoundDelegate ()
519
+ .handleEntityNotFound ( persister .getEntityName (), keyToLoad .getIdentifier () );
520
+ }
521
+ // Otherwise, if it's INTERNAL_LOAD_NULLABLE, the proxy is
522
+ // for a non-existing association mapped as @NotFound.
523
+ // Don't throw an exception; just return null.
524
+ return null ;
525
+ }
526
+ } );
527
+ }
528
+
488
529
/**
489
530
* If there is already a corresponding proxy associated with the
490
531
* persistence context, return it; otherwise create a proxy, associate it
@@ -497,38 +538,29 @@ private CompletionStage<Object> returnNarrowedProxy(
497
538
*
498
539
* @return The created/existing proxy
499
540
*/
500
- private Object createProxyIfNecessary (
541
+ private static Object createProxyIfNecessary (
501
542
LoadEvent event ,
502
543
EntityPersister persister ,
503
544
EntityKey keyToLoad ,
504
545
LoadType options ) {
505
546
final PersistenceContext persistenceContext = event .getSession ().getPersistenceContextInternal ();
506
547
final Object existing = persistenceContext .getEntity ( keyToLoad );
507
- final boolean traceEnabled = LOG .isTraceEnabled ();
508
548
if ( existing != null ) {
509
549
// return existing object or initialized proxy (unless deleted)
510
- if ( traceEnabled ) {
550
+ if ( LOG . isTraceEnabled () ) {
511
551
LOG .trace ( "Entity found in session cache" );
512
552
}
513
- if ( options .isCheckDeleted () ) {
514
- EntityEntry entry = persistenceContext .getEntry ( existing );
515
- Status status = entry .getStatus ();
516
- if ( status == Status .DELETED || status == Status .GONE ) {
517
- return null ;
518
- }
519
- }
520
- return existing ;
553
+ return options .isCheckDeleted () && wasDeleted ( persistenceContext , existing ) ? null : existing ;
521
554
}
522
- if ( traceEnabled ) {
523
- LOG .trace ( "Creating new proxy for entity" );
555
+ else {
556
+ if ( LOG .isTraceEnabled () ) {
557
+ LOG .trace ( "Creating new proxy for entity" );
558
+ }
559
+ return createProxy ( event , persister , keyToLoad );
524
560
}
525
- return createProxy ( event , persister , keyToLoad );
526
561
}
527
562
528
- private static Object createProxy (
529
- LoadEvent event ,
530
- EntityPersister persister ,
531
- EntityKey keyToLoad ) {
563
+ private static Object createProxy (LoadEvent event , EntityPersister persister , EntityKey keyToLoad ) {
532
564
// return new uninitialized proxy
533
565
Object proxy = persister .createProxy ( event .getEntityId (), event .getSession () );
534
566
PersistenceContext persistenceContext = event .getSession ().getPersistenceContextInternal ();
@@ -611,28 +643,59 @@ private CompletionStage<Object> doLoad(
611
643
EntityPersister persister ,
612
644
EntityKey keyToLoad ,
613
645
LoadType options ) {
614
-
615
- final EventSource session = event .getSession ();
616
- final boolean traceEnabled = LOG .isTraceEnabled ();
617
- if ( traceEnabled ) {
646
+ if ( LOG .isTraceEnabled () ) {
618
647
LOG .tracev (
619
648
"Attempting to resolve: {0}" ,
620
- infoString ( persister , event .getEntityId (), session .getFactory () )
649
+ infoString ( persister , event .getEntityId (), event . getSession () .getFactory () )
621
650
);
622
651
}
623
652
624
- final CacheEntityLoaderHelper .PersistenceContextEntry persistenceContextEntry =
625
- ReactiveCacheEntityLoaderHelper .INSTANCE .loadFromSessionCache ( event , keyToLoad , options );
626
- Object entity = persistenceContextEntry .getEntity ();
627
- if ( entity != null ) {
628
- return persistenceContextEntry .isManaged ()
629
- ? completedFuture ( entity )
630
- : nullFuture ();
653
+ if ( event .getSession ().getPersistenceContextInternal ().containsDeletedUnloadedEntityKey ( keyToLoad ) ) {
654
+ return nullFuture ();
655
+ }
656
+ else {
657
+ final PersistenceContextEntry persistenceContextEntry =
658
+ ReactiveCacheEntityLoaderHelper .INSTANCE .loadFromSessionCache ( event , keyToLoad , options );
659
+ final Object entity = persistenceContextEntry .getEntity ();
660
+ if ( entity != null ) {
661
+ return persistenceContextEntry .isManaged () ? initializeIfNecessary ( entity ) : nullFuture ();
662
+ }
663
+ else {
664
+ return loadFromCacheOrDatasource ( event , persister , keyToLoad );
665
+ }
631
666
}
667
+ }
632
668
633
- entity = CacheEntityLoaderHelper .INSTANCE .loadFromSecondLevelCache ( event , persister , keyToLoad );
669
+ private static CompletionStage <Object > initializeIfNecessary (Object entity ) {
670
+ if ( isPersistentAttributeInterceptable ( entity ) ) {
671
+ final PersistentAttributeInterceptable interceptable = asPersistentAttributeInterceptable ( entity );
672
+ final PersistentAttributeInterceptor interceptor = interceptable .$$_hibernate_getInterceptor ();
673
+ if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
674
+ final EnhancementAsProxyLazinessInterceptor lazinessInterceptor =
675
+ (EnhancementAsProxyLazinessInterceptor ) interceptor ;
676
+ final SharedSessionContractImplementor session = lazinessInterceptor .getLinkedSession ();
677
+ if ( session == null ) {
678
+ throw LOG .sessionClosedLazyInitializationException ();
679
+ }
680
+ return ReactiveQueryExecutorLookup .extract ( session ).reactiveFetch ( entity , false );
681
+ }
682
+ else {
683
+ return completedFuture ( entity );
684
+ }
685
+ }
686
+ else {
687
+ return completedFuture ( entity );
688
+ }
689
+ }
690
+
691
+ private CompletionStage <Object > loadFromCacheOrDatasource (
692
+ LoadEvent event ,
693
+ EntityPersister persister ,
694
+ EntityKey keyToLoad ) {
695
+ final EventSource session = event .getSession ();
696
+ final Object entity = CacheEntityLoaderHelper .INSTANCE .loadFromSecondLevelCache (event , persister , keyToLoad );
634
697
if ( entity != null ) {
635
- if ( traceEnabled ) {
698
+ if ( LOG . isTraceEnabled () ) {
636
699
LOG .tracev (
637
700
"Resolved object in second-level cache: {0}" ,
638
701
infoString ( persister , event .getEntityId (), session .getFactory () )
@@ -642,15 +705,15 @@ private CompletionStage<Object> doLoad(
642
705
return completedFuture ( entity );
643
706
}
644
707
else {
645
- if ( traceEnabled ) {
708
+ if ( LOG . isTraceEnabled () ) {
646
709
LOG .tracev (
647
710
"Object not resolved in any cache: {0}" ,
648
711
infoString ( persister , event .getEntityId (), session .getFactory () )
649
712
);
650
713
}
651
714
return loadFromDatasource ( event , persister )
652
715
.thenApply ( optional -> {
653
- if ( optional != null ) {
716
+ if ( optional != null ) {
654
717
cacheNaturalId ( event , persister , session , optional );
655
718
}
656
719
return optional ;
@@ -661,9 +724,10 @@ private CompletionStage<Object> doLoad(
661
724
private void cacheNaturalId (LoadEvent event , EntityPersister persister , EventSource session , Object entity ) {
662
725
if ( entity != null && persister .hasNaturalIdentifier () ) {
663
726
session .getPersistenceContextInternal ().getNaturalIdResolutions ()
664
- .cacheResolutionFromLoad ( event .getEntityId (),
665
- persister .getNaturalIdMapping ().extractNaturalIdFromEntity ( entity ),
666
- persister
727
+ .cacheResolutionFromLoad (
728
+ event .getEntityId (),
729
+ persister .getNaturalIdMapping ().extractNaturalIdFromEntity ( entity ),
730
+ persister
667
731
);
668
732
}
669
733
}
@@ -692,8 +756,9 @@ protected CompletionStage<Object> loadFromDatasource(LoadEvent event, EntityPers
692
756
// persister/loader/initializer sensitive to this fact - possibly
693
757
// passing LoadType along
694
758
695
- if ( entity instanceof HibernateProxy ) {
696
- entity = ( (HibernateProxy ) entity ).getHibernateLazyInitializer ().getImplementation ();
759
+ final LazyInitializer lazyInitializer = HibernateProxy .extractLazyInitializer ( entity );
760
+ if ( lazyInitializer != null ) {
761
+ entity = lazyInitializer .getImplementation ();
697
762
}
698
763
699
764
final StatisticsImplementor statistics = event .getSession ().getFactory ().getStatistics ();
0 commit comments