Skip to content

Commit d6fa584

Browse files
committed
update DefaultReactiveLoadEventListener
1 parent 36887cf commit d6fa584

File tree

3 files changed

+156
-91
lines changed

3 files changed

+156
-91
lines changed

hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLoadEventListener.java

Lines changed: 144 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,20 @@
1515
import org.hibernate.PersistentObjectException;
1616
import org.hibernate.TypeMismatchException;
1717
import org.hibernate.action.internal.DelayedPostInsertIdentifier;
18+
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
1819
import org.hibernate.cache.spi.access.EntityDataAccess;
1920
import org.hibernate.cache.spi.access.SoftLock;
20-
import org.hibernate.engine.spi.EntityEntry;
2121
import org.hibernate.engine.spi.EntityKey;
2222
import org.hibernate.engine.spi.PersistenceContext;
2323
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
24+
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
2425
import org.hibernate.engine.spi.SessionImplementor;
25-
import org.hibernate.engine.spi.Status;
26+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
2627
import org.hibernate.event.spi.EventSource;
2728
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;
2930
import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper;
31+
import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.PersistenceContextEntry;
3032
import org.hibernate.metamodel.mapping.AttributeMapping;
3133
import org.hibernate.metamodel.mapping.AttributeMappingsList;
3234
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
@@ -42,8 +44,11 @@
4244
import org.hibernate.reactive.logging.impl.Log;
4345
import org.hibernate.reactive.logging.impl.LoggerFactory;
4446
import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister;
47+
import org.hibernate.reactive.session.impl.ReactiveQueryExecutorLookup;
4548
import org.hibernate.stat.spi.StatisticsImplementor;
4649

50+
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
51+
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable;
4752
import static org.hibernate.pretty.MessageHelper.infoString;
4853
import static org.hibernate.reactive.session.impl.SessionUtil.checkEntityFound;
4954
import 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

Comments
 (0)