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.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

View File

@ -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<EntityKey, List<ParentInfo>> toBatchLoad = new HashMap<>();
private Map<EntityKey, List<ParentInfo>> toBatchLoad;
private final String rootEmbeddablePropertyName;
/**
@ -71,27 +72,23 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
@Override
protected void registerResolutionListener() {
final List<ParentInfo> 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<ParentInfo> getBatchInfos() {
List<ParentInfo> objects = toBatchLoad.get( entityKey );
if ( objects == null ) {
objects = new ArrayList<>();
toBatchLoad.put( entityKey, objects );
private List<ParentInfo> 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;
}

View File

@ -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<EntityKey, List<ParentInfo>> toBatchLoad = new HashMap<>();
private Map<EntityKey, List<ParentInfo>> toBatchLoad;
public BatchEntitySelectFetchInitializer(
FetchParentAccess parentAccess,
@ -37,25 +38,19 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect
@Override
protected void registerResolutionListener() {
final List<ParentInfo> 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<ParentInfo> getParentInfos() {
List<ParentInfo> 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;
}