From 837d1a32cb3ffbf48653c0bb12679ccebd07a005 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Thu, 16 Mar 2023 17:40:05 +0100 Subject: [PATCH] HHH-16248 Check referenced model part declaring type when batch fetching associations --- ...ractBatchEntitySelectFetchInitializer.java | 13 +++- ...nsideEmbeddableSelectFetchInitializer.java | 77 +++++++++---------- .../BatchEntitySelectFetchInitializer.java | 67 ++++++++-------- 3 files changed, 81 insertions(+), 76 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java index da763fa34a..1d6010aaef 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java @@ -10,6 +10,7 @@ import java.util.function.Consumer; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; @@ -130,8 +131,16 @@ public abstract class AbstractBatchEntitySelectFetchInitializer extends Abstract ); } - protected static int getPropertyIndex(EntityInitializer entityInitializer, String propertyName) { - return entityInitializer.getConcreteDescriptor().findAttributeMapping( propertyName ).getStateArrayPosition(); + protected AttributeMapping getParentEntityAttribute(String attributeName) { + final AttributeMapping parentAttribute = firstEntityInitializer.getConcreteDescriptor() + .findAttributeMapping( attributeName ); + if ( parentAttribute != null && parentAttribute.getDeclaringType() == referencedModelPart.getDeclaringType() + .findContainingEntityMapping() ) { + // These checks are needed to avoid setting the instance using the wrong (child's) model part or + // setting it multiple times in case parent and child share the same attribute name for the association. + return parentAttribute; + } + return null; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntityInsideEmbeddableSelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntityInsideEmbeddableSelectFetchInitializer.java index 401b0b8991..f5c1d3c7cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntityInsideEmbeddableSelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntityInsideEmbeddableSelectFetchInitializer.java @@ -17,6 +17,7 @@ import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.log.LoggingHelper; +import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.spi.NavigablePath; @@ -27,7 +28,7 @@ import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer { - private final Map> toBatchLoad = new HashMap<>(); + private Map> toBatchLoad; private final String rootEmbeddablePropertyName; /** @@ -71,27 +72,23 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB @Override protected void registerResolutionListener() { - final List batchParentInfos = getBatchInfos(); - - parentAccess.registerResolutionListener( - o -> - batchParentInfos.add( - new ParentInfo( - firstEntityInitializer.getEntityKey(), - o, - getPropertyIndex( firstEntityInitializer, rootEmbeddablePropertyName ) - ) - ) - ); + parentAccess.registerResolutionListener( parentInstance -> { + final AttributeMapping parentAttribute = getParentEntityAttribute( rootEmbeddablePropertyName ); + if ( parentAttribute != null ) { + getParentInfos().add( new ParentInfo( + firstEntityInitializer.getEntityKey(), + parentInstance, + parentAttribute.getStateArrayPosition() + ) ); + } + } ); } - private List getBatchInfos() { - List objects = toBatchLoad.get( entityKey ); - if ( objects == null ) { - objects = new ArrayList<>(); - toBatchLoad.put( entityKey, objects ); + private List getParentInfos() { + if ( toBatchLoad == null ) { + toBatchLoad = new HashMap<>(); } - return objects; + return toBatchLoad.computeIfAbsent( entityKey, key -> new ArrayList<>() ); } @Override @@ -113,27 +110,29 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB @Override public void endLoading(ExecutionContext context) { - toBatchLoad.forEach( - (entityKey, parentInfos) -> { - final SharedSessionContractImplementor session = context.getSession(); - final Object loadedInstance = loadInstance( entityKey, referencedModelPart, session ); - for ( ParentInfo parentInfo : parentInfos ) { - final PersistenceContext persistenceContext = session.getPersistenceContext(); - setInstance( - firstEntityInitializer, - referencedModelPart, - rootEmbeddablePropertyName, - parentInfo.propertyIndex, - loadedInstance, - parentInfo.parentInstance, - parentInfo.initializerEntityKey, - persistenceContext.getEntry( persistenceContext.getEntity( parentInfo.initializerEntityKey ) ), - session - ); + if ( toBatchLoad != null ) { + toBatchLoad.forEach( + (entityKey, parentInfos) -> { + final SharedSessionContractImplementor session = context.getSession(); + final Object loadedInstance = loadInstance( entityKey, referencedModelPart, session ); + for ( ParentInfo parentInfo : parentInfos ) { + final PersistenceContext persistenceContext = session.getPersistenceContext(); + setInstance( + firstEntityInitializer, + referencedModelPart, + rootEmbeddablePropertyName, + parentInfo.propertyIndex, + loadedInstance, + parentInfo.parentInstance, + parentInfo.initializerEntityKey, + persistenceContext.getEntry( persistenceContext.getEntity( parentInfo.initializerEntityKey ) ), + session + ); + } } - } - ); - toBatchLoad.clear(); + ); + toBatchLoad.clear(); + } parentAccess = null; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java index da8a44b099..189fbd44aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java @@ -15,6 +15,7 @@ import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.log.LoggingHelper; +import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.spi.NavigablePath; @@ -24,7 +25,7 @@ import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.entity.EntityInitializer; public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer { - private final Map> toBatchLoad = new HashMap<>(); + private Map> toBatchLoad; public BatchEntitySelectFetchInitializer( FetchParentAccess parentAccess, @@ -37,25 +38,19 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect @Override protected void registerResolutionListener() { - final List parents = getParentInfos(); - parentAccess.registerResolutionListener( - o -> - parents.add( - new ParentInfo( - o, - getPropertyIndex( firstEntityInitializer, referencedModelPart.getPartName() ) - ) - ) - ); + parentAccess.registerResolutionListener( parentInstance -> { + final AttributeMapping parentAttribute = getParentEntityAttribute( referencedModelPart.getAttributeName() ); + if ( parentAttribute != null ) { + getParentInfos().add( new ParentInfo( parentInstance, parentAttribute.getStateArrayPosition() ) ); + } + } ); } private List getParentInfos() { - List objects = toBatchLoad.get( entityKey ); - if ( objects == null ) { - objects = new ArrayList<>(); - toBatchLoad.put( entityKey, objects ); + if ( toBatchLoad == null ) { + toBatchLoad = new HashMap<>(); } - return objects; + return toBatchLoad.computeIfAbsent( entityKey, key -> new ArrayList<>() ); } @Override @@ -75,26 +70,28 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect @Override public void endLoading(ExecutionContext context) { - toBatchLoad.forEach( - (entityKey, parentInfos) -> { - final SharedSessionContractImplementor session = context.getSession(); - final Object instance = loadInstance( entityKey, referencedModelPart, session ); - for ( ParentInfo parentInfo : parentInfos ) { - final Object parentInstance = parentInfo.parentInstance; - setInstance( - firstEntityInitializer, - referencedModelPart, - referencedModelPart.getPartName(), - parentInfo.propertyIndex, - session, - instance, - parentInstance, - session.getPersistenceContext().getEntry( parentInstance ) - ); + if ( toBatchLoad != null ) { + toBatchLoad.forEach( + (entityKey, parentInfos) -> { + final SharedSessionContractImplementor session = context.getSession(); + final Object instance = loadInstance( entityKey, referencedModelPart, session ); + for ( ParentInfo parentInfo : parentInfos ) { + final Object parentInstance = parentInfo.parentInstance; + setInstance( + firstEntityInitializer, + referencedModelPart, + referencedModelPart.getPartName(), + parentInfo.propertyIndex, + session, + instance, + parentInstance, + session.getPersistenceContext().getEntry( parentInstance ) + ); + } } - } - ); - toBatchLoad.clear(); + ); + toBatchLoad.clear(); + } parentAccess = null; }