HHH-16248 Check referenced model part declaring type when batch fetching associations

This commit is contained in:
Marco Belladelli 2023-03-16 17:40:05 +01:00
parent f572f80f6e
commit 837d1a32cb
3 changed files with 81 additions and 76 deletions

View File

@ -10,6 +10,7 @@ import java.util.function.Consumer;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
@ -130,8 +131,16 @@ public abstract class AbstractBatchEntitySelectFetchInitializer extends Abstract
); );
} }
protected static int getPropertyIndex(EntityInitializer entityInitializer, String propertyName) { protected AttributeMapping getParentEntityAttribute(String attributeName) {
return entityInitializer.getConcreteDescriptor().findAttributeMapping( propertyName ).getStateArrayPosition(); 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 @Override

View File

@ -17,6 +17,7 @@ import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.log.LoggingHelper; import org.hibernate.internal.log.LoggingHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spi.NavigablePath; 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; import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer { public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer {
private final Map<EntityKey, List<ParentInfo>> toBatchLoad = new HashMap<>(); private Map<EntityKey, List<ParentInfo>> toBatchLoad;
private final String rootEmbeddablePropertyName; private final String rootEmbeddablePropertyName;
/** /**
@ -71,27 +72,23 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
@Override @Override
protected void registerResolutionListener() { protected void registerResolutionListener() {
final List<ParentInfo> batchParentInfos = getBatchInfos(); parentAccess.registerResolutionListener( parentInstance -> {
final AttributeMapping parentAttribute = getParentEntityAttribute( rootEmbeddablePropertyName );
parentAccess.registerResolutionListener( if ( parentAttribute != null ) {
o -> getParentInfos().add( new ParentInfo(
batchParentInfos.add( firstEntityInitializer.getEntityKey(),
new ParentInfo( parentInstance,
firstEntityInitializer.getEntityKey(), parentAttribute.getStateArrayPosition()
o, ) );
getPropertyIndex( firstEntityInitializer, rootEmbeddablePropertyName ) }
) } );
)
);
} }
private List<ParentInfo> getBatchInfos() { private List<ParentInfo> getParentInfos() {
List<ParentInfo> objects = toBatchLoad.get( entityKey ); if ( toBatchLoad == null ) {
if ( objects == null ) { toBatchLoad = new HashMap<>();
objects = new ArrayList<>();
toBatchLoad.put( entityKey, objects );
} }
return objects; return toBatchLoad.computeIfAbsent( entityKey, key -> new ArrayList<>() );
} }
@Override @Override
@ -113,27 +110,29 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
@Override @Override
public void endLoading(ExecutionContext context) { public void endLoading(ExecutionContext context) {
toBatchLoad.forEach( if ( toBatchLoad != null ) {
(entityKey, parentInfos) -> { toBatchLoad.forEach(
final SharedSessionContractImplementor session = context.getSession(); (entityKey, parentInfos) -> {
final Object loadedInstance = loadInstance( entityKey, referencedModelPart, session ); final SharedSessionContractImplementor session = context.getSession();
for ( ParentInfo parentInfo : parentInfos ) { final Object loadedInstance = loadInstance( entityKey, referencedModelPart, session );
final PersistenceContext persistenceContext = session.getPersistenceContext(); for ( ParentInfo parentInfo : parentInfos ) {
setInstance( final PersistenceContext persistenceContext = session.getPersistenceContext();
firstEntityInitializer, setInstance(
referencedModelPart, firstEntityInitializer,
rootEmbeddablePropertyName, referencedModelPart,
parentInfo.propertyIndex, rootEmbeddablePropertyName,
loadedInstance, parentInfo.propertyIndex,
parentInfo.parentInstance, loadedInstance,
parentInfo.initializerEntityKey, parentInfo.parentInstance,
persistenceContext.getEntry( persistenceContext.getEntity( parentInfo.initializerEntityKey ) ), parentInfo.initializerEntityKey,
session persistenceContext.getEntry( persistenceContext.getEntity( parentInfo.initializerEntityKey ) ),
); session
);
}
} }
} );
); toBatchLoad.clear();
toBatchLoad.clear(); }
parentAccess = null; parentAccess = null;
} }

View File

@ -15,6 +15,7 @@ import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.log.LoggingHelper; import org.hibernate.internal.log.LoggingHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
@ -24,7 +25,7 @@ import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.graph.entity.EntityInitializer;
public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer { public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer {
private final Map<EntityKey, List<ParentInfo>> toBatchLoad = new HashMap<>(); private Map<EntityKey, List<ParentInfo>> toBatchLoad;
public BatchEntitySelectFetchInitializer( public BatchEntitySelectFetchInitializer(
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
@ -37,25 +38,19 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect
@Override @Override
protected void registerResolutionListener() { protected void registerResolutionListener() {
final List<ParentInfo> parents = getParentInfos(); parentAccess.registerResolutionListener( parentInstance -> {
parentAccess.registerResolutionListener( final AttributeMapping parentAttribute = getParentEntityAttribute( referencedModelPart.getAttributeName() );
o -> if ( parentAttribute != null ) {
parents.add( getParentInfos().add( new ParentInfo( parentInstance, parentAttribute.getStateArrayPosition() ) );
new ParentInfo( }
o, } );
getPropertyIndex( firstEntityInitializer, referencedModelPart.getPartName() )
)
)
);
} }
private List<ParentInfo> getParentInfos() { private List<ParentInfo> getParentInfos() {
List<ParentInfo> objects = toBatchLoad.get( entityKey ); if ( toBatchLoad == null ) {
if ( objects == null ) { toBatchLoad = new HashMap<>();
objects = new ArrayList<>();
toBatchLoad.put( entityKey, objects );
} }
return objects; return toBatchLoad.computeIfAbsent( entityKey, key -> new ArrayList<>() );
} }
@Override @Override
@ -75,26 +70,28 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect
@Override @Override
public void endLoading(ExecutionContext context) { public void endLoading(ExecutionContext context) {
toBatchLoad.forEach( if ( toBatchLoad != null ) {
(entityKey, parentInfos) -> { toBatchLoad.forEach(
final SharedSessionContractImplementor session = context.getSession(); (entityKey, parentInfos) -> {
final Object instance = loadInstance( entityKey, referencedModelPart, session ); final SharedSessionContractImplementor session = context.getSession();
for ( ParentInfo parentInfo : parentInfos ) { final Object instance = loadInstance( entityKey, referencedModelPart, session );
final Object parentInstance = parentInfo.parentInstance; for ( ParentInfo parentInfo : parentInfos ) {
setInstance( final Object parentInstance = parentInfo.parentInstance;
firstEntityInitializer, setInstance(
referencedModelPart, firstEntityInitializer,
referencedModelPart.getPartName(), referencedModelPart,
parentInfo.propertyIndex, referencedModelPart.getPartName(),
session, parentInfo.propertyIndex,
instance, session,
parentInstance, instance,
session.getPersistenceContext().getEntry( parentInstance ) parentInstance,
); session.getPersistenceContext().getEntry( parentInstance )
);
}
} }
} );
); toBatchLoad.clear();
toBatchLoad.clear(); }
parentAccess = null; parentAccess = null;
} }