30
30
import org .hibernate .bytecode .enhance .spi .interceptor .LazyAttributesMetadata ;
31
31
import org .hibernate .bytecode .spi .BytecodeEnhancementMetadata ;
32
32
import org .hibernate .collection .spi .PersistentCollection ;
33
- import org .hibernate .engine .internal .ManagedTypeHelper ;
34
33
import org .hibernate .engine .spi .EntityEntry ;
35
34
import org .hibernate .engine .spi .EntityKey ;
36
35
import org .hibernate .engine .spi .LoadQueryInfluencers ;
69
68
import org .hibernate .sql .ast .tree .select .SelectStatement ;
70
69
import org .hibernate .sql .exec .spi .JdbcParametersList ;
71
70
import org .hibernate .type .BasicType ;
71
+ import org .hibernate .type .Type ;
72
72
73
73
import jakarta .persistence .metamodel .Attribute ;
74
74
75
75
import static java .lang .invoke .MethodHandles .lookup ;
76
76
import static java .util .Collections .emptyMap ;
77
+ import static org .hibernate .engine .internal .ManagedTypeHelper .asPersistentAttributeInterceptable ;
77
78
import static org .hibernate .generator .EventType .INSERT ;
78
79
import static org .hibernate .generator .EventType .UPDATE ;
79
80
import static org .hibernate .internal .util .collections .CollectionHelper .setOfSize ;
105
106
*/
106
107
public interface ReactiveAbstractEntityPersister extends ReactiveEntityPersister {
107
108
109
+ Log LOG = make ( Log .class , lookup () );
110
+
108
111
/**
109
112
* A self-reference of type {@code AbstractEntityPersister}.
110
113
*
@@ -374,32 +377,51 @@ default CompletionStage<Object> reactiveInitializeLazyPropertiesFromDatastore(
374
377
EntityEntry entry ,
375
378
String fieldName ,
376
379
SharedSessionContractImplementor session ) {
380
+ return isNonLazyPropertyName ( fieldName )
381
+ ? initLazyProperty ( entity , id , entry , fieldName , session )
382
+ : initLazyProperties ( entity , id , entry , fieldName , session );
383
+ }
377
384
378
- if ( !hasLazyProperties () ) {
379
- throw new AssertionFailure ( "no lazy properties" );
380
- }
385
+ boolean isNonLazyPropertyName (String fieldName );
381
386
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 );
386
406
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 ) {
388
413
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 );
395
416
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 );
398
421
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 );
403
425
404
426
return reactiveGetSQLLazySelectLoadPlan ( fetchGroup )
405
427
.load ( id , session )
@@ -420,51 +442,55 @@ default CompletionStage<Object> initLazyProperty(
420
442
PersistentAttributeInterceptor interceptor ,
421
443
List <LazyAttributeDescriptor > fetchGroupAttributeDescriptors ,
422
444
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 ) {
428
453
// 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 () ) );
431
457
}
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
432
467
continue ;
433
468
}
434
469
435
- final Object selectedValue = values [ i ++ ];
436
- if ( selectedValue instanceof CompletionStage ) {
470
+ final Object result = results [ index ];
471
+ if ( result instanceof CompletionStage ) {
437
472
// 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
+ );
456
483
}
457
484
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 ) ) {
461
486
interceptor .attributeInitialized ( fetchGroupAttributeDescriptor .getName () );
487
+ finalResultStage = finalResultStage .thenApply ( finalResult -> result );
462
488
}
463
489
}
464
490
}
465
491
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" );
468
494
return result ;
469
495
} );
470
496
}
@@ -526,9 +552,11 @@ private CompletionStage<?> loadFromDatabaseOrCache(
526
552
.load ( identifier , entity , lockOptions , session );
527
553
}
528
554
529
- SingleIdEntityLoader <?> determineLoaderToUse ( SharedSessionContractImplementor session , LockOptions lockOptions );
555
+ boolean initializeLazyProperty ( String fieldName , Object entity , EntityEntry entry , LazyAttributeDescriptor fetchGroupAttributeDescriptor , Object propValue );
530
556
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 );
532
560
533
561
Object initializeLazyProperty (String fieldName , Object entity , SharedSessionContractImplementor session );
534
562
0 commit comments