1515import org .hibernate .PersistentObjectException ;
1616import org .hibernate .TypeMismatchException ;
1717import org .hibernate .action .internal .DelayedPostInsertIdentifier ;
18+ import org .hibernate .bytecode .enhance .spi .interceptor .EnhancementAsProxyLazinessInterceptor ;
1819import org .hibernate .cache .spi .access .EntityDataAccess ;
1920import org .hibernate .cache .spi .access .SoftLock ;
20- import org .hibernate .engine .spi .EntityEntry ;
2121import org .hibernate .engine .spi .EntityKey ;
2222import org .hibernate .engine .spi .PersistenceContext ;
2323import org .hibernate .engine .spi .PersistentAttributeInterceptable ;
24+ import org .hibernate .engine .spi .PersistentAttributeInterceptor ;
2425import org .hibernate .engine .spi .SessionImplementor ;
25- import org .hibernate .engine .spi .Status ;
26+ import org .hibernate .engine .spi .SharedSessionContractImplementor ;
2627import org .hibernate .event .spi .EventSource ;
2728import 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 ;
2930import org .hibernate .loader .ast .internal .CacheEntityLoaderHelper ;
31+ import org .hibernate .loader .ast .internal .CacheEntityLoaderHelper .PersistenceContextEntry ;
3032import org .hibernate .metamodel .mapping .AttributeMapping ;
3133import org .hibernate .metamodel .mapping .AttributeMappingsList ;
3234import org .hibernate .metamodel .mapping .CompositeIdentifierMapping ;
4244import org .hibernate .reactive .logging .impl .Log ;
4345import org .hibernate .reactive .logging .impl .LoggerFactory ;
4446import org .hibernate .reactive .persister .entity .impl .ReactiveEntityPersister ;
47+ import org .hibernate .reactive .session .impl .ReactiveQueryExecutorLookup ;
4548import org .hibernate .stat .spi .StatisticsImplementor ;
4649
50+ import static org .hibernate .engine .internal .ManagedTypeHelper .asPersistentAttributeInterceptable ;
51+ import static org .hibernate .engine .internal .ManagedTypeHelper .isPersistentAttributeInterceptable ;
4752import static org .hibernate .pretty .MessageHelper .infoString ;
4853import static org .hibernate .reactive .session .impl .SessionUtil .checkEntityFound ;
4954import static org .hibernate .reactive .session .impl .SessionUtil .throwEntityNotFound ;
@@ -91,7 +96,7 @@ public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException
9196 // Since this method is not reactive, we're not expecting to hit the
9297 // database here (if we do, it's a bug) and so we can assume the
9398 // returned CompletionStage is already completed
94- CompletionStage <Void > checkId = checkId ( event , loadType , persister );
99+ final CompletionStage <Void > checkId = checkId ( event , loadType , persister );
95100 if ( !checkId .toCompletableFuture ().isDone () ) {
96101 // This only happens if the object is loaded from the db
97102 throw new UnexpectedAccessToTheDatabase ();
@@ -101,7 +106,7 @@ public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException
101106 // Since this method is not reactive, we're not expecting to hit the
102107 // database here (if we do, it's a bug) and so we can assume the
103108 // 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 );
105110 if ( !loaded .toCompletableFuture ().isDone () ) {
106111 // This only happens if the object is loaded from the db
107112 throw new UnexpectedAccessToTheDatabase ();
@@ -244,7 +249,8 @@ else if ( idMapping instanceof NonAggregatedIdentifierMapping ) {
244249 }
245250 }
246251 }
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 () );
248254 }
249255
250256 /*
@@ -338,8 +344,7 @@ else if ( persister.hasProxy() ) {
338344 }
339345
340346 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 ();
343348 }
344349
345350 private CompletionStage <Object > loadWithBytecodeProxy (LoadEvent event , EntityPersister persister , EntityKey keyToLoad , LoadType options ) {
@@ -372,12 +377,12 @@ private CompletionStage<Object> loadWithRegularProxy(LoadEvent event, EntityPers
372377 final Object proxy = event .getSession ().getPersistenceContextInternal ().getProxy ( keyToLoad );
373378 if ( proxy != null ) {
374379 // 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 );
376381 }
377382 else if ( options .isAllowProxyCreation () ) {
378383 // return a new proxy
379384 // ORM calls DefaultLoadEventListener#proxyOrCache
380- return completedFuture ( createProxyIfNecessary ( event , persister , keyToLoad , options ) );
385+ return completedFuture ( proxyOrCached ( event , persister , keyToLoad , options ) );
381386 }
382387 else {
383388 return load ( event , persister , keyToLoad , options );
@@ -404,9 +409,7 @@ private static CompletionStage<Object> loadWithProxyFactory(LoadEvent event, Ent
404409 }
405410 else if ( persister .hasSubclasses () ) {
406411 // 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 ) );
410413 }
411414 else {
412415 // no existing proxy, and no subclasses
@@ -427,22 +430,40 @@ private static PersistentAttributeInterceptable createBatchLoadableEnhancedProxy
427430 return persister .getBytecodeEnhancementMetadata ().createEnhancedProxy ( keyToLoad , true , session );
428431 }
429432
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 );
446467 }
447468
448469 /**
@@ -457,7 +478,7 @@ private CompletionStage<Object> proxyImplementation(LoadEvent event, EntityPersi
457478 *
458479 * @return The created/existing proxy
459480 */
460- private CompletionStage <Object > returnNarrowedProxy (
481+ private CompletionStage <Object > narrowedProxy (
461482 LoadEvent event ,
462483 EntityPersister persister ,
463484 EntityKey keyToLoad ,
@@ -468,23 +489,43 @@ private CompletionStage<Object> returnNarrowedProxy(
468489 }
469490
470491 LazyInitializer li = ( (HibernateProxy ) proxy ).getHibernateLazyInitializer ();
471-
472492 if ( li .isUnwrap () ) {
473493 return completedFuture ( li .getImplementation () );
474494 }
475-
476- final PersistenceContext persistenceContext = event .getSession ().getPersistenceContextInternal ();
477- if ( options .isAllowProxyCreation () ) {
478- return completedFuture ( persistenceContext .narrowProxy ( proxy , persister , keyToLoad , null ) );
479- }
480495 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+ }
485506 }
486507 }
487508
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+
488529 /**
489530 * If there is already a corresponding proxy associated with the
490531 * persistence context, return it; otherwise create a proxy, associate it
@@ -497,38 +538,29 @@ private CompletionStage<Object> returnNarrowedProxy(
497538 *
498539 * @return The created/existing proxy
499540 */
500- private Object createProxyIfNecessary (
541+ private static Object createProxyIfNecessary (
501542 LoadEvent event ,
502543 EntityPersister persister ,
503544 EntityKey keyToLoad ,
504545 LoadType options ) {
505546 final PersistenceContext persistenceContext = event .getSession ().getPersistenceContextInternal ();
506547 final Object existing = persistenceContext .getEntity ( keyToLoad );
507- final boolean traceEnabled = LOG .isTraceEnabled ();
508548 if ( existing != null ) {
509549 // return existing object or initialized proxy (unless deleted)
510- if ( traceEnabled ) {
550+ if ( LOG . isTraceEnabled () ) {
511551 LOG .trace ( "Entity found in session cache" );
512552 }
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 ;
521554 }
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 );
524560 }
525- return createProxy ( event , persister , keyToLoad );
526561 }
527562
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 ) {
532564 // return new uninitialized proxy
533565 Object proxy = persister .createProxy ( event .getEntityId (), event .getSession () );
534566 PersistenceContext persistenceContext = event .getSession ().getPersistenceContextInternal ();
@@ -611,28 +643,59 @@ private CompletionStage<Object> doLoad(
611643 EntityPersister persister ,
612644 EntityKey keyToLoad ,
613645 LoadType options ) {
614-
615- final EventSource session = event .getSession ();
616- final boolean traceEnabled = LOG .isTraceEnabled ();
617- if ( traceEnabled ) {
646+ if ( LOG .isTraceEnabled () ) {
618647 LOG .tracev (
619648 "Attempting to resolve: {0}" ,
620- infoString ( persister , event .getEntityId (), session .getFactory () )
649+ infoString ( persister , event .getEntityId (), event . getSession () .getFactory () )
621650 );
622651 }
623652
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+ }
631666 }
667+ }
632668
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 );
634697 if ( entity != null ) {
635- if ( traceEnabled ) {
698+ if ( LOG . isTraceEnabled () ) {
636699 LOG .tracev (
637700 "Resolved object in second-level cache: {0}" ,
638701 infoString ( persister , event .getEntityId (), session .getFactory () )
@@ -642,15 +705,15 @@ private CompletionStage<Object> doLoad(
642705 return completedFuture ( entity );
643706 }
644707 else {
645- if ( traceEnabled ) {
708+ if ( LOG . isTraceEnabled () ) {
646709 LOG .tracev (
647710 "Object not resolved in any cache: {0}" ,
648711 infoString ( persister , event .getEntityId (), session .getFactory () )
649712 );
650713 }
651714 return loadFromDatasource ( event , persister )
652715 .thenApply ( optional -> {
653- if ( optional != null ) {
716+ if ( optional != null ) {
654717 cacheNaturalId ( event , persister , session , optional );
655718 }
656719 return optional ;
@@ -661,9 +724,10 @@ private CompletionStage<Object> doLoad(
661724 private void cacheNaturalId (LoadEvent event , EntityPersister persister , EventSource session , Object entity ) {
662725 if ( entity != null && persister .hasNaturalIdentifier () ) {
663726 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
667731 );
668732 }
669733 }
@@ -692,8 +756,9 @@ protected CompletionStage<Object> loadFromDatasource(LoadEvent event, EntityPers
692756 // persister/loader/initializer sensitive to this fact - possibly
693757 // passing LoadType along
694758
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 ();
697762 }
698763
699764 final StatisticsImplementor statistics = event .getSession ().getFactory ().getStatistics ();
0 commit comments