Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.LoadQueryInfluencers;
Expand Down Expand Up @@ -69,11 +68,13 @@
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.spi.JdbcParametersList;
import org.hibernate.type.BasicType;
import org.hibernate.type.Type;

import jakarta.persistence.metamodel.Attribute;

import static java.lang.invoke.MethodHandles.lookup;
import static java.util.Collections.emptyMap;
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
import static org.hibernate.generator.EventType.INSERT;
import static org.hibernate.generator.EventType.UPDATE;
import static org.hibernate.internal.util.collections.CollectionHelper.setOfSize;
Expand Down Expand Up @@ -105,6 +106,8 @@
*/
public interface ReactiveAbstractEntityPersister extends ReactiveEntityPersister {

Log LOG = make( Log.class, lookup() );

/**
* A self-reference of type {@code AbstractEntityPersister}.
*
Expand Down Expand Up @@ -374,32 +377,51 @@ default CompletionStage<Object> reactiveInitializeLazyPropertiesFromDatastore(
EntityEntry entry,
String fieldName,
SharedSessionContractImplementor session) {
return isNonLazyPropertyName( fieldName )
? initLazyProperty( entity, id, entry, fieldName, session )
: initLazyProperties( entity, id, entry, fieldName, session );
}

if ( !hasLazyProperties() ) {
throw new AssertionFailure( "no lazy properties" );
}
boolean isNonLazyPropertyName(String fieldName);

final PersistentAttributeInterceptor interceptor = ManagedTypeHelper.asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor();
if ( interceptor == null ) {
throw new AssertionFailure( "Expecting bytecode interceptor to be non-null" );
}
private CompletionStage<Object> initLazyProperty(
Object entity,
Object id,
EntityEntry entry,
String fieldName,
SharedSessionContractImplementor session) {
// An eager property can be lazy because of an applied EntityGraph
final int propertyIndex = getPropertyIndex( fieldName );
final List<ModelPart> partsToSelect = List.of( getAttributeMapping( propertyIndex ) );
return reactiveGetOrCreateLazyLoadPlan( fieldName, partsToSelect )
.load( id, session )
.thenApply( results -> {
final Object result = results[0];
initializeLazyProperty( entity, entry, result, propertyIndex, getPropertyTypes()[propertyIndex] );
return result;
} );
}

ReactiveSingleIdArrayLoadPlan reactiveGetOrCreateLazyLoadPlan(String fieldName, List<ModelPart> partsToSelect);

make( Log.class, lookup() ).tracef( "Initializing lazy properties from datastore (triggered for `%s`)", fieldName );
private CompletionStage<Object> initLazyProperties(
Object entity,
Object id,
EntityEntry entry,
String fieldName,
SharedSessionContractImplementor session) {

final String fetchGroup = getEntityPersister().getBytecodeEnhancementMetadata()
.getLazyAttributesMetadata()
.getFetchGroupName( fieldName );
final List<LazyAttributeDescriptor> fetchGroupAttributeDescriptors = getEntityPersister().getBytecodeEnhancementMetadata()
.getLazyAttributesMetadata()
.getFetchGroupAttributeDescriptors( fetchGroup );
assert hasLazyProperties();
LOG.tracef( "Initializing lazy properties from datastore (triggered for '%s')", fieldName );

@SuppressWarnings("deprecation")
Set<String> initializedLazyAttributeNames = interceptor.getInitializedLazyAttributeNames();
final var interceptor = asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor();
assert interceptor != null : "Expecting bytecode interceptor to be non-null";
final Set<String> initializedLazyAttributeNames = interceptor.getInitializedLazyAttributeNames();
LOG.tracef( "Initializing lazy properties from datastore (triggered for `%s`)", fieldName );

// FIXME: How do I pass this to the query?
Object[] arguments = PreparedStatementAdaptor.bind(
statement -> getIdentifierType().nullSafeSet( statement, id, 1, session )
);
final var lazyAttributesMetadata = getBytecodeEnhancementMetadata().getLazyAttributesMetadata();
final String fetchGroup = lazyAttributesMetadata.getFetchGroupName( fieldName );
final var fetchGroupAttributeDescriptors = lazyAttributesMetadata.getFetchGroupAttributeDescriptors( fetchGroup );

return reactiveGetSQLLazySelectLoadPlan( fetchGroup )
.load( id, session )
Expand All @@ -420,51 +442,55 @@ default CompletionStage<Object> initLazyProperty(
PersistentAttributeInterceptor interceptor,
List<LazyAttributeDescriptor> fetchGroupAttributeDescriptors,
Set<String> initializedLazyAttributeNames,
Object[] values) { // Load all the lazy properties that are in the same fetch group
CompletionStage<Object> resultStage = nullFuture();
int i = 0;
for ( LazyAttributeDescriptor fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) {
if ( initializedLazyAttributeNames.contains( fetchGroupAttributeDescriptor.getName() ) ) {
Object[] results) { // Load all the lazy properties that are in the same fetch group
CompletionStage<Object> finalResultStage = nullFuture();
final int[] i = { 0 };
for ( var fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) {
final String attributeName = fetchGroupAttributeDescriptor.getName();
final boolean previousInitialized = initializedLazyAttributeNames.contains( attributeName );
final int index = i[0]++;
if ( previousInitialized ) {
// Already initialized
if ( fetchGroupAttributeDescriptor.getName().equals( fieldName ) ) {
resultStage = completedFuture( entry.getLoadedValue( fetchGroupAttributeDescriptor.getName() ) );
if ( attributeName.equals( fieldName ) ) {
finalResultStage = finalResultStage
.thenApply( finalResult -> entry.getLoadedValue( fetchGroupAttributeDescriptor.getName() ) );
}
// it's already been initialized (e.g. by a write) so we don't want to overwrite
// TODO: we should consider un-marking an attribute as dirty based on the selected value
// - we know the current value:
// getPropertyValue( entity, fetchGroupAttributeDescriptor.getAttributeIndex() );
// - we know the selected value (see selectedValue below)
// - we can use the attribute Type to tell us if they are the same
// - assuming entity is a SelfDirtinessTracker we can also know if the attribute is currently
// considered dirty, and if really not dirty we would do the un-marking
// - of course that would mean a new method on SelfDirtinessTracker to allow un-marking
continue;
}

final Object selectedValue = values[i++];
if ( selectedValue instanceof CompletionStage ) {
final Object result = results[index];
if ( result instanceof CompletionStage ) {
// This happens with a lazy one-to-one (bytecode enhancement enabled)
CompletionStage<Object> selectedValueStage = (CompletionStage<Object>) selectedValue;
resultStage = resultStage
.thenCompose( result -> selectedValueStage
.thenApply( selected -> {
final boolean set = initializeLazyProperty(
fieldName,
entity,
entry,
fetchGroupAttributeDescriptor.getLazyIndex(),
selected
);
if ( set ) {
interceptor.attributeInitialized( fetchGroupAttributeDescriptor.getName() );
return selected;
}
return result;
} )
);
final CompletionStage<Object> resultStage = (CompletionStage<Object>) result;
finalResultStage = finalResultStage.thenCompose( finalResult -> resultStage
.thenApply( value -> {
if ( initializeLazyProperty( fieldName, entity, entry, fetchGroupAttributeDescriptor, result ) ) {
interceptor.attributeInitialized( fetchGroupAttributeDescriptor.getName() );
return value;
}
return finalResult;
} )
);
}
else {
final boolean set = initializeLazyProperty( fieldName, entity, entry, fetchGroupAttributeDescriptor.getLazyIndex(), selectedValue );
if ( set ) {
resultStage = completedFuture( selectedValue );
if ( initializeLazyProperty( fieldName, entity, entry, fetchGroupAttributeDescriptor, result ) ) {
interceptor.attributeInitialized( fetchGroupAttributeDescriptor.getName() );
finalResultStage = finalResultStage.thenApply( finalResult -> result );
}
}
}

return resultStage.thenApply( result -> {
make( Log.class, lookup() ).trace( "Done initializing lazy properties" );
return finalResultStage.thenApply( result -> {
LOG.trace( "Done initializing lazy properties" );
return result;
} );
}
Expand Down Expand Up @@ -526,9 +552,11 @@ private CompletionStage<?> loadFromDatabaseOrCache(
.load( identifier, entity, lockOptions, session );
}

SingleIdEntityLoader<?> determineLoaderToUse(SharedSessionContractImplementor session, LockOptions lockOptions);
boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, LazyAttributeDescriptor fetchGroupAttributeDescriptor, Object propValue);

boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, int lazyIndex, Object selectedValue);
void initializeLazyProperty(Object entity, EntityEntry entry, Object propValue, int index, Type type);

SingleIdEntityLoader<?> determineLoaderToUse(SharedSessionContractImplementor session, LockOptions lockOptions);

Object initializeLazyProperty(String fieldName, Object entity, SharedSessionContractImplementor session);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

import org.hibernate.FetchMode;
Expand All @@ -23,6 +24,7 @@
import org.hibernate.generator.Generator;
import org.hibernate.generator.values.GeneratedValues;
import org.hibernate.id.IdentityGenerator;
import org.hibernate.loader.ast.internal.LoaderSelectBuilder;
import org.hibernate.loader.ast.spi.BatchLoaderFactory;
import org.hibernate.loader.ast.spi.EntityBatchLoader;
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
Expand All @@ -34,6 +36,7 @@
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedIdentifierMappingImpl;
import org.hibernate.metamodel.mapping.internal.GeneratedValuesProcessor;
Expand All @@ -48,6 +51,7 @@
import org.hibernate.query.named.NamedQueryMemento;
import org.hibernate.reactive.loader.ast.internal.ReactiveMultiIdEntityLoaderArrayParam;
import org.hibernate.reactive.loader.ast.internal.ReactiveMultiIdEntityLoaderStandard;
import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdArrayLoadPlan;
import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdEntityLoaderProvidedQueryImpl;
import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdEntityLoaderStandardImpl;
import org.hibernate.reactive.loader.ast.internal.ReactiveSingleUniqueKeyEntityLoaderStandard;
Expand All @@ -64,6 +68,8 @@
import org.hibernate.reactive.sql.results.internal.ReactiveEntityResultImpl;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.spi.JdbcParametersList;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
Expand All @@ -90,6 +96,7 @@ public class ReactiveAbstractPersisterDelegate {
private final EntityPersister entityDescriptor;

private Map<SingularAttributeMapping, ReactiveSingleUniqueKeyEntityLoader<Object>> uniqueKeyLoadersNew;
private ConcurrentHashMap<String, ReactiveSingleIdArrayLoadPlan> nonLazyPropertyLoadPlansByName;

public ReactiveAbstractPersisterDelegate(
final EntityPersister entityPersister,
Expand Down Expand Up @@ -347,6 +354,61 @@ else if ( entityIdentifierMapping instanceof EmbeddedIdentifierMappingImpl embed
}
}

/*
* Same as AbstractEntityPersister#getOrCreateLazyLoadPlan
*/
public ReactiveSingleIdArrayLoadPlan getOrCreateLazyLoadPlan(String fieldName, List<ModelPart> partsToSelect) {
var propertyLoadPlansByName = nonLazyPropertyLoadPlansByName;
if ( propertyLoadPlansByName == null ) {
propertyLoadPlansByName = new ConcurrentHashMap<>();
final ReactiveSingleIdArrayLoadPlan newLazyLoanPlan = createLazyLoadPlan( partsToSelect );
propertyLoadPlansByName.put( fieldName, newLazyLoanPlan );
nonLazyPropertyLoadPlansByName = propertyLoadPlansByName;
return newLazyLoanPlan;
}
else {
final ReactiveSingleIdArrayLoadPlan lazyLoanPlan = nonLazyPropertyLoadPlansByName.get( fieldName );
if ( lazyLoanPlan == null ) {
final ReactiveSingleIdArrayLoadPlan newLazyLoanPlan = createLazyLoadPlan( partsToSelect );
nonLazyPropertyLoadPlansByName.put( fieldName, newLazyLoanPlan );
return newLazyLoanPlan;
}
else {
return lazyLoanPlan;
}
}
}

private ReactiveSingleIdArrayLoadPlan createLazyLoadPlan(List<ModelPart> partsToSelect) {
if ( partsToSelect.isEmpty() ) {
// only one-to-one is lazily fetched
return null;
}
else {
final LockOptions lockOptions = new LockOptions();
final JdbcParametersList.Builder jdbcParametersBuilder = JdbcParametersList.newBuilder();
final SelectStatement select = LoaderSelectBuilder.createSelect(
entityDescriptor,
partsToSelect,
entityDescriptor.getIdentifierMapping(),
null,
1,
new LoadQueryInfluencers( entityDescriptor.getFactory() ),
lockOptions,
jdbcParametersBuilder::add,
entityDescriptor.getFactory()
);
return new ReactiveSingleIdArrayLoadPlan(
entityDescriptor,
entityDescriptor.getIdentifierMapping(),
select,
jdbcParametersBuilder.build(),
lockOptions,
entityDescriptor.getFactory()
);
}
}

private static class ReactiveNonAggregatedIdentifierMappingImpl extends NonAggregatedIdentifierMappingImpl {

public ReactiveNonAggregatedIdentifierMappingImpl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeDescriptor;
import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
import org.hibernate.engine.spi.CascadeStyle;
Expand All @@ -27,6 +28,7 @@
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NaturalIdMapping;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
Expand All @@ -52,6 +54,7 @@
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;

import static java.lang.invoke.MethodHandles.lookup;
import static org.hibernate.reactive.logging.impl.LoggerFactory.make;
Expand All @@ -60,8 +63,7 @@
* An {@link ReactiveEntityPersister} backed by {@link JoinedSubclassEntityPersister}
* and {@link ReactiveAbstractEntityPersister}.
*/
public class ReactiveJoinedSubclassEntityPersister extends JoinedSubclassEntityPersister
implements ReactiveAbstractEntityPersister {
public class ReactiveJoinedSubclassEntityPersister extends JoinedSubclassEntityPersister implements ReactiveAbstractEntityPersister {

private static final Log LOG = make( Log.class, lookup() );

Expand All @@ -76,6 +78,16 @@ public ReactiveJoinedSubclassEntityPersister(
reactiveDelegate = new ReactiveAbstractPersisterDelegate( this, persistentClass, new ReactiveRuntimeModelCreationContext( creationContext ) );
}

@Override
public boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, LazyAttributeDescriptor fetchGroupAttributeDescriptor, Object propValue) {
return super.initializeLazyProperty( fieldName, entity, entry, fetchGroupAttributeDescriptor, propValue );
}

@Override
public void initializeLazyProperty(Object entity, EntityEntry entry, Object propValue, int index, Type type) {
super.initializeLazyProperty( entity, entry, propValue, index, type );
}

@Override
protected SingleIdEntityLoader<?> buildSingleIdEntityLoader() {
return reactiveDelegate.buildSingleIdEntityLoader();
Expand Down Expand Up @@ -389,4 +401,13 @@ public ReactiveSingleIdArrayLoadPlan reactiveGetSQLLazySelectLoadPlan(String fet
return this.getLazyLoadPlanByFetchGroup( getSubclassPropertyNameClosure() ).get(fetchGroup );
}

@Override
public ReactiveSingleIdArrayLoadPlan reactiveGetOrCreateLazyLoadPlan(String fieldName, List<ModelPart> partsToSelect) {
return reactiveDelegate.getOrCreateLazyLoadPlan( fieldName, partsToSelect );
}

@Override
public boolean isNonLazyPropertyName(String fieldName) {
return super.isNonLazyPropertyName( fieldName );
}
}
Loading
Loading