3030import org .hibernate .bytecode .enhance .spi .interceptor .LazyAttributesMetadata ;
3131import org .hibernate .bytecode .spi .BytecodeEnhancementMetadata ;
3232import org .hibernate .collection .spi .PersistentCollection ;
33- import org .hibernate .engine .internal .ManagedTypeHelper ;
3433import org .hibernate .engine .spi .EntityEntry ;
3534import org .hibernate .engine .spi .EntityKey ;
3635import org .hibernate .engine .spi .LoadQueryInfluencers ;
6968import org .hibernate .sql .ast .tree .select .SelectStatement ;
7069import org .hibernate .sql .exec .spi .JdbcParametersList ;
7170import org .hibernate .type .BasicType ;
71+ import org .hibernate .type .Type ;
7272
7373import jakarta .persistence .metamodel .Attribute ;
7474
7575import static java .lang .invoke .MethodHandles .lookup ;
7676import static java .util .Collections .emptyMap ;
77+ import static org .hibernate .engine .internal .ManagedTypeHelper .asPersistentAttributeInterceptable ;
7778import static org .hibernate .generator .EventType .INSERT ;
7879import static org .hibernate .generator .EventType .UPDATE ;
7980import static org .hibernate .internal .util .collections .CollectionHelper .setOfSize ;
105106 */
106107public interface ReactiveAbstractEntityPersister extends ReactiveEntityPersister {
107108
109+ Log LOG = make ( Log .class , lookup () );
110+
108111 /**
109112 * A self-reference of type {@code AbstractEntityPersister}.
110113 *
@@ -374,32 +377,51 @@ default CompletionStage<Object> reactiveInitializeLazyPropertiesFromDatastore(
374377 EntityEntry entry ,
375378 String fieldName ,
376379 SharedSessionContractImplementor session ) {
380+ return isNonLazyPropertyName ( fieldName )
381+ ? initLazyProperty ( entity , id , entry , fieldName , session )
382+ : initLazyProperties ( entity , id , entry , fieldName , session );
383+ }
377384
378- if ( !hasLazyProperties () ) {
379- throw new AssertionFailure ( "no lazy properties" );
380- }
385+ boolean isNonLazyPropertyName (String fieldName );
381386
382- final PersistentAttributeInterceptor interceptor = ManagedTypeHelper .asPersistentAttributeInterceptable ( entity ).$$_hibernate_getInterceptor ();
383- if ( interceptor == null ) {
384- throw new AssertionFailure ( "Expecting bytecode interceptor to be non-null" );
385- }
387+ private CompletionStage <Object > initLazyProperty (
388+ Object entity ,
389+ Object id ,
390+ EntityEntry entry ,
391+ String fieldName ,
392+ SharedSessionContractImplementor session ) {
393+ // An eager property can be lazy because of an applied EntityGraph
394+ final int propertyIndex = getPropertyIndex ( fieldName );
395+ final List <ModelPart > partsToSelect = List .of ( getAttributeMapping ( propertyIndex ) );
396+ return reactiveGetOrCreateLazyLoadPlan ( fieldName , partsToSelect )
397+ .load ( id , session )
398+ .thenApply ( results -> {
399+ final Object result = results [0 ];
400+ initializeLazyProperty ( entity , entry , result , propertyIndex , getPropertyTypes ()[propertyIndex ] );
401+ return result ;
402+ } );
403+ }
404+
405+ ReactiveSingleIdArrayLoadPlan reactiveGetOrCreateLazyLoadPlan (String fieldName , List <ModelPart > partsToSelect );
386406
387- make ( Log .class , lookup () ).tracef ( "Initializing lazy properties from datastore (triggered for `%s`)" , fieldName );
407+ private CompletionStage <Object > initLazyProperties (
408+ Object entity ,
409+ Object id ,
410+ EntityEntry entry ,
411+ String fieldName ,
412+ SharedSessionContractImplementor session ) {
388413
389- final String fetchGroup = getEntityPersister ().getBytecodeEnhancementMetadata ()
390- .getLazyAttributesMetadata ()
391- .getFetchGroupName ( fieldName );
392- final List <LazyAttributeDescriptor > fetchGroupAttributeDescriptors = getEntityPersister ().getBytecodeEnhancementMetadata ()
393- .getLazyAttributesMetadata ()
394- .getFetchGroupAttributeDescriptors ( fetchGroup );
414+ assert hasLazyProperties ();
415+ LOG .tracef ( "Initializing lazy properties from datastore (triggered for '%s')" , fieldName );
395416
396- @ SuppressWarnings ("deprecation" )
397- Set <String > initializedLazyAttributeNames = interceptor .getInitializedLazyAttributeNames ();
417+ final var interceptor = asPersistentAttributeInterceptable ( entity ).$$_hibernate_getInterceptor ();
418+ assert interceptor != null : "Expecting bytecode interceptor to be non-null" ;
419+ final Set <String > initializedLazyAttributeNames = interceptor .getInitializedLazyAttributeNames ();
420+ LOG .tracef ( "Initializing lazy properties from datastore (triggered for `%s`)" , fieldName );
398421
399- // FIXME: How do I pass this to the query?
400- Object [] arguments = PreparedStatementAdaptor .bind (
401- statement -> getIdentifierType ().nullSafeSet ( statement , id , 1 , session )
402- );
422+ final var lazyAttributesMetadata = getBytecodeEnhancementMetadata ().getLazyAttributesMetadata ();
423+ final String fetchGroup = lazyAttributesMetadata .getFetchGroupName ( fieldName );
424+ final var fetchGroupAttributeDescriptors = lazyAttributesMetadata .getFetchGroupAttributeDescriptors ( fetchGroup );
403425
404426 return reactiveGetSQLLazySelectLoadPlan ( fetchGroup )
405427 .load ( id , session )
@@ -420,51 +442,55 @@ default CompletionStage<Object> initLazyProperty(
420442 PersistentAttributeInterceptor interceptor ,
421443 List <LazyAttributeDescriptor > fetchGroupAttributeDescriptors ,
422444 Set <String > initializedLazyAttributeNames ,
423- Object [] values ) { // Load all the lazy properties that are in the same fetch group
424- CompletionStage <Object > resultStage = nullFuture ();
425- int i = 0 ;
426- for ( LazyAttributeDescriptor fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) {
427- if ( initializedLazyAttributeNames .contains ( fetchGroupAttributeDescriptor .getName () ) ) {
445+ Object [] results ) { // Load all the lazy properties that are in the same fetch group
446+ CompletionStage <Object > finalResultStage = nullFuture ();
447+ final int [] i = { 0 };
448+ for ( var fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) {
449+ final String attributeName = fetchGroupAttributeDescriptor .getName ();
450+ final boolean previousInitialized = initializedLazyAttributeNames .contains ( attributeName );
451+ final int index = i [0 ]++;
452+ if ( previousInitialized ) {
428453 // Already initialized
429- if ( fetchGroupAttributeDescriptor .getName ().equals ( fieldName ) ) {
430- resultStage = completedFuture ( entry .getLoadedValue ( fetchGroupAttributeDescriptor .getName () ) );
454+ if ( attributeName .equals ( fieldName ) ) {
455+ finalResultStage = finalResultStage
456+ .thenApply ( finalResult -> entry .getLoadedValue ( fetchGroupAttributeDescriptor .getName () ) );
431457 }
458+ // it's already been initialized (e.g. by a write) so we don't want to overwrite
459+ // TODO: we should consider un-marking an attribute as dirty based on the selected value
460+ // - we know the current value:
461+ // getPropertyValue( entity, fetchGroupAttributeDescriptor.getAttributeIndex() );
462+ // - we know the selected value (see selectedValue below)
463+ // - we can use the attribute Type to tell us if they are the same
464+ // - assuming entity is a SelfDirtinessTracker we can also know if the attribute is currently
465+ // considered dirty, and if really not dirty we would do the un-marking
466+ // - of course that would mean a new method on SelfDirtinessTracker to allow un-marking
432467 continue ;
433468 }
434469
435- final Object selectedValue = values [ i ++ ];
436- if ( selectedValue instanceof CompletionStage ) {
470+ final Object result = results [ index ];
471+ if ( result instanceof CompletionStage ) {
437472 // This happens with a lazy one-to-one (bytecode enhancement enabled)
438- CompletionStage <Object > selectedValueStage = (CompletionStage <Object >) selectedValue ;
439- resultStage = resultStage
440- .thenCompose ( result -> selectedValueStage
441- .thenApply ( selected -> {
442- final boolean set = initializeLazyProperty (
443- fieldName ,
444- entity ,
445- entry ,
446- fetchGroupAttributeDescriptor .getLazyIndex (),
447- selected
448- );
449- if ( set ) {
450- interceptor .attributeInitialized ( fetchGroupAttributeDescriptor .getName () );
451- return selected ;
452- }
453- return result ;
454- } )
455- );
473+ final CompletionStage <Object > resultStage = (CompletionStage <Object >) result ;
474+ finalResultStage = finalResultStage .thenCompose ( finalResult -> resultStage
475+ .thenApply ( value -> {
476+ if ( initializeLazyProperty ( fieldName , entity , entry , fetchGroupAttributeDescriptor , result ) ) {
477+ interceptor .attributeInitialized ( fetchGroupAttributeDescriptor .getName () );
478+ return value ;
479+ }
480+ return finalResult ;
481+ } )
482+ );
456483 }
457484 else {
458- final boolean set = initializeLazyProperty ( fieldName , entity , entry , fetchGroupAttributeDescriptor .getLazyIndex (), selectedValue );
459- if ( set ) {
460- resultStage = completedFuture ( selectedValue );
485+ if ( initializeLazyProperty ( fieldName , entity , entry , fetchGroupAttributeDescriptor , result ) ) {
461486 interceptor .attributeInitialized ( fetchGroupAttributeDescriptor .getName () );
487+ finalResultStage = finalResultStage .thenApply ( finalResult -> result );
462488 }
463489 }
464490 }
465491
466- return resultStage .thenApply ( result -> {
467- make ( Log . class , lookup () ) .trace ( "Done initializing lazy properties" );
492+ return finalResultStage .thenApply ( result -> {
493+ LOG .trace ( "Done initializing lazy properties" );
468494 return result ;
469495 } );
470496 }
@@ -526,9 +552,11 @@ private CompletionStage<?> loadFromDatabaseOrCache(
526552 .load ( identifier , entity , lockOptions , session );
527553 }
528554
529- SingleIdEntityLoader <?> determineLoaderToUse ( SharedSessionContractImplementor session , LockOptions lockOptions );
555+ boolean initializeLazyProperty ( String fieldName , Object entity , EntityEntry entry , LazyAttributeDescriptor fetchGroupAttributeDescriptor , Object propValue );
530556
531- boolean initializeLazyProperty (String fieldName , Object entity , EntityEntry entry , int lazyIndex , Object selectedValue );
557+ void initializeLazyProperty (Object entity , EntityEntry entry , Object propValue , int index , Type type );
558+
559+ SingleIdEntityLoader <?> determineLoaderToUse (SharedSessionContractImplementor session , LockOptions lockOptions );
532560
533561 Object initializeLazyProperty (String fieldName , Object entity , SharedSessionContractImplementor session );
534562
0 commit comments