HHH-15921 @BatchSize and @IdClass on join column throws exception

This commit is contained in:
Andrea Boriero 2023-01-19 15:06:21 +01:00 committed by Andrea Boriero
parent dbaca049c8
commit b033b88472
30 changed files with 331 additions and 122 deletions

View File

@ -31,7 +31,6 @@ import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
@ -216,7 +215,7 @@ public class TemporaryTable implements Exportable, Contributable {
throw new IllegalStateException( "Not yet ready: " + pluralAttribute ); throw new IllegalStateException( "Not yet ready: " + pluralAttribute );
} }
final ModelPart fkTarget = keyDescriptor.getTargetPart(); final ModelPart fkTarget = keyDescriptor.getTargetPart();
if ( !( fkTarget instanceof EntityIdentifierMapping ) ) { if ( !fkTarget.isEntityIdentifierMapping() ) {
final Value value = entityBinding.getSubclassProperty( pluralAttribute.getAttributeName() ) final Value value = entityBinding.getSubclassProperty( pluralAttribute.getAttributeName() )
.getValue(); .getValue();
final Iterator<Selectable> columnIterator = final Iterator<Selectable> columnIterator =

View File

@ -10,12 +10,13 @@ import java.lang.reflect.Constructor;
import org.hibernate.InstantiationException; import org.hibernate.InstantiationException;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.metamodel.spi.ValueAccess; import org.hibernate.metamodel.spi.ValueAccess;
/** /**
* Support for instantiating embeddables as POJO representation through a constructor * Support for instantiating embeddables as POJO representation through a constructor
*/ */
public class EmbeddableInstantiatorPojoIndirecting extends AbstractPojoInstantiator implements StandardEmbeddableInstantiator { public class EmbeddableInstantiatorPojoIndirecting extends AbstractPojoInstantiator implements EmbeddableInstantiator {
protected final Constructor<?> constructor; protected final Constructor<?> constructor;
protected final int[] index; protected final int[] index;

View File

@ -141,4 +141,9 @@ public interface EntityIdentifierMapping extends ValuedModelPart {
*/ */
VIRTUAL VIRTUAL
} }
@Override
default boolean isEntityIdentifierMapping() {
return true;
}
} }

View File

@ -91,6 +91,10 @@ public interface ModelPart extends MappingModelExpressible {
return false; return false;
} }
default boolean isEntityIdentifierMapping() {
return false;
}
boolean hasPartitionedSelectionMapping(); boolean hasPartitionedSelectionMapping();
/** /**

View File

@ -198,7 +198,7 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
final ModelPart targetPart = toOneAttributeMapping.getForeignKeyDescriptor().getPart( final ModelPart targetPart = toOneAttributeMapping.getForeignKeyDescriptor().getPart(
toOneAttributeMapping.getSideNature().inverse() toOneAttributeMapping.getSideNature().inverse()
); );
if ( targetPart instanceof EntityIdentifierMapping ) { if ( targetPart.isEntityIdentifierMapping() ) {
propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o ); propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o );
} }
else { else {

View File

@ -206,7 +206,7 @@ public class InverseNonAggregatedIdentifierMapping extends EmbeddedAttributeMapp
final ModelPart targetPart = toOneAttributeMapping.getForeignKeyDescriptor().getPart( final ModelPart targetPart = toOneAttributeMapping.getForeignKeyDescriptor().getPart(
toOneAttributeMapping.getSideNature().inverse() toOneAttributeMapping.getSideNature().inverse()
); );
if ( targetPart instanceof EntityIdentifierMapping ) { if ( targetPart.isEntityIdentifierMapping() ) {
propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o ); propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o );
} }
else { else {

View File

@ -193,7 +193,7 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
@Override @Override
public boolean isReferenceToPrimaryKey() { public boolean isReferenceToPrimaryKey() {
return getForeignKeyDescriptor().getTargetPart() instanceof EntityIdentifierMapping; return getForeignKeyDescriptor().getTargetPart().isEntityIdentifierMapping();
} }
@Override @Override

View File

@ -230,7 +230,7 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
final ModelPart targetPart = toOneAttributeMapping.getForeignKeyDescriptor().getPart( final ModelPart targetPart = toOneAttributeMapping.getForeignKeyDescriptor().getPart(
toOneAttributeMapping.getSideNature().inverse() toOneAttributeMapping.getSideNature().inverse()
); );
if ( targetPart instanceof EntityIdentifierMapping ) { if ( targetPart.isEntityIdentifierMapping() ) {
propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o ); propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o );
} }
else { else {

View File

@ -442,7 +442,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
} }
} }
final ModelPart modelPart = side.getModelPart(); final ModelPart modelPart = side.getModelPart();
if ( modelPart instanceof EntityIdentifierMapping ) { if ( modelPart.isEntityIdentifierMapping() ) {
return ( (EntityIdentifierMapping) modelPart ).getIdentifierIfNotUnsaved( targetObject, session ); return ( (EntityIdentifierMapping) modelPart ).getIdentifierIfNotUnsaved( targetObject, session );
} }
return ( (PropertyBasedMapping) modelPart ).getPropertyAccess().getGetter().get( targetObject ); return ( (PropertyBasedMapping) modelPart ).getPropertyAccess().getGetter().get( targetObject );

View File

@ -770,7 +770,7 @@ public class ToOneAttributeMapping
@Override @Override
public boolean isReferenceToPrimaryKey() { public boolean isReferenceToPrimaryKey() {
return foreignKeyDescriptor.getSide( sideNature.inverse() ).getModelPart() instanceof EntityIdentifierMapping; return foreignKeyDescriptor.getSide( sideNature.inverse() ).getModelPart().isEntityIdentifierMapping();
} }
@Override @Override

View File

@ -5564,7 +5564,7 @@ public abstract class AbstractEntityPersister
final ModelPart superDefinedAttribute = superMappingType.findSubPart( name, superMappingType ); final ModelPart superDefinedAttribute = superMappingType.findSubPart( name, superMappingType );
if ( superDefinedAttribute != null ) { if ( superDefinedAttribute != null ) {
// Prefer the identifier mapping of the concrete class // Prefer the identifier mapping of the concrete class
if ( superDefinedAttribute instanceof EntityIdentifierMapping ) { if ( superDefinedAttribute.isEntityIdentifierMapping() ) {
final ModelPart identifierModelPart = getIdentifierModelPart( name, treatTargetType ); final ModelPart identifierModelPart = getIdentifierModelPart( name, treatTargetType );
if ( identifierModelPart != null ) { if ( identifierModelPart != null ) {
return identifierModelPart; return identifierModelPart;

View File

@ -113,7 +113,7 @@ public class ResultsHelper {
} }
public static String attributeName(ModelPart identifierMapping) { public static String attributeName(ModelPart identifierMapping) {
if ( identifierMapping instanceof EntityIdentifierMapping ) { if ( identifierMapping.isEntityIdentifierMapping() ) {
return identifierMapping instanceof SingleAttributeIdentifierMapping return identifierMapping instanceof SingleAttributeIdentifierMapping
? ( (SingleAttributeIdentifierMapping) identifierMapping ).getAttributeName() ? ( (SingleAttributeIdentifierMapping) identifierMapping ).getAttributeName()
: null; : null;

View File

@ -251,8 +251,8 @@ public class MatchingIdSelectionHelper {
if ( pluralAttribute.getSeparateCollectionTable() != null ) { if ( pluralAttribute.getSeparateCollectionTable() != null ) {
// Ensure that the FK target columns are available // Ensure that the FK target columns are available
final boolean useFkTarget = !( pluralAttribute.getKeyDescriptor() final boolean useFkTarget = !pluralAttribute.getKeyDescriptor()
.getTargetPart() instanceof EntityIdentifierMapping ); .getTargetPart().isEntityIdentifierMapping();
if ( useFkTarget ) { if ( useFkTarget ) {
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup(); final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
pluralAttribute.getKeyDescriptor().getTargetPart().applySqlSelections( pluralAttribute.getKeyDescriptor().getTargetPart().applySqlSelections(

View File

@ -77,8 +77,8 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
pluralAttribute -> { pluralAttribute -> {
if ( pluralAttribute.getSeparateCollectionTable() != null ) { if ( pluralAttribute.getSeparateCollectionTable() != null ) {
// Ensure that the FK target columns are available // Ensure that the FK target columns are available
final boolean useFkTarget = !( pluralAttribute.getKeyDescriptor() final boolean useFkTarget = !pluralAttribute.getKeyDescriptor()
.getTargetPart() instanceof EntityIdentifierMapping ); .getTargetPart().isEntityIdentifierMapping();
if ( useFkTarget ) { if ( useFkTarget ) {
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup(); final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
pluralAttribute.getKeyDescriptor().getTargetPart().applySqlSelections( pluralAttribute.getKeyDescriptor().getTargetPart().applySqlSelections(

View File

@ -103,7 +103,7 @@ public class InlineDeleteHandler implements DeleteHandler {
// collection table // collection table
final ModelPart fkTargetPart = pluralAttribute.getKeyDescriptor().getTargetPart(); final ModelPart fkTargetPart = pluralAttribute.getKeyDescriptor().getTargetPart();
final int valueIndex; final int valueIndex;
if ( fkTargetPart instanceof EntityIdentifierMapping ) { if ( fkTargetPart.isEntityIdentifierMapping() ) {
valueIndex = 0; valueIndex = 0;
} }
else { else {

View File

@ -258,7 +258,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor(); final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor();
final QuerySpec idSelectFkSubQuery; final QuerySpec idSelectFkSubQuery;
// todo (6.0): based on the location of the attribute mapping, we could prune the table group of the subquery // todo (6.0): based on the location of the attribute mapping, we could prune the table group of the subquery
if ( fkDescriptor.getTargetPart() instanceof EntityIdentifierMapping ) { if ( fkDescriptor.getTargetPart().isEntityIdentifierMapping() ) {
idSelectFkSubQuery = matchingIdSubQuerySpec; idSelectFkSubQuery = matchingIdSubQuerySpec;
} }
else { else {
@ -538,7 +538,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
(tableReference, attributeMapping) -> { (tableReference, attributeMapping) -> {
final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor(); final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor();
final QuerySpec idTableFkSubQuery; final QuerySpec idTableFkSubQuery;
if ( fkDescriptor.getTargetPart() instanceof EntityIdentifierMapping ) { if ( fkDescriptor.getTargetPart().isEntityIdentifierMapping() ) {
idTableFkSubQuery = idTableIdentifierSubQuery; idTableFkSubQuery = idTableIdentifierSubQuery;
} }
else { else {

View File

@ -9,6 +9,7 @@ package org.hibernate.sql.results.graph;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
@ -104,8 +105,22 @@ public interface Initializer {
return false; return false;
} }
/**
* A utility method to avoid casting explicitly to EntityInitializer
*
* @return EntityInitializer if this is an instance of EntityInitializer otherwise {@code null}
*/
default EntityInitializer asEntityInitializer() { default EntityInitializer asEntityInitializer() {
return null; return null;
} }
/**
* A utility method to avoid casting explicitly to EmbeddableInitializer
*
* @return EmbeddableInitializer if this is an instance of EmbeddableInitializer otherwise {@code null}
*/
default EmbeddableInitializer asEmbeddableInitializer() {
return null;
}
} }

View File

@ -219,6 +219,8 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
notifyResolutionListeners( compositeInstance ); notifyResolutionListeners( compositeInstance );
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( compositeInstance ); final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( compositeInstance );
// If the composite instance has a lazy initializer attached, this means that the embeddable is actually virtual
// and the compositeInstance == entity, so we have to inject the row state into the entity when it finishes resolution
if ( lazyInitializer != null ) { if ( lazyInitializer != null ) {
final Initializer parentInitializer = processingState.resolveInitializer( navigablePath.getParent() ); final Initializer parentInitializer = processingState.resolveInitializer( navigablePath.getParent() );
if ( parentInitializer != this ) { if ( parentInitializer != this ) {

View File

@ -27,8 +27,8 @@ public interface EmbeddableInitializer extends FetchParentAccess {
default RowProcessingState wrapProcessingState(RowProcessingState processingState) { default RowProcessingState wrapProcessingState(RowProcessingState processingState) {
final FetchParentAccess fetchParentAccess = getFetchParentAccess(); final FetchParentAccess fetchParentAccess = getFetchParentAccess();
if ( fetchParentAccess != null ) { if ( fetchParentAccess != null ) {
if ( fetchParentAccess instanceof EmbeddableInitializer ) { if ( fetchParentAccess.isEmbeddableInitializer() ) {
return ( (EmbeddableInitializer) fetchParentAccess ).wrapProcessingState( processingState ); return ( fetchParentAccess.asEmbeddableInitializer() ).wrapProcessingState( processingState );
} }
} }
return processingState; return processingState;
@ -43,4 +43,9 @@ public interface EmbeddableInitializer extends FetchParentAccess {
default boolean isEmbeddableInitializer() { default boolean isEmbeddableInitializer() {
return true; return true;
} }
@Override
default EmbeddableInitializer asEmbeddableInitializer() {
return this;
}
} }

View File

@ -152,7 +152,7 @@ public class AggregateEmbeddableFetchImpl extends AbstractFetchParent implements
public DomainResultAssembler createAssembler( public DomainResultAssembler createAssembler(
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
final EmbeddableInitializer initializer = (EmbeddableInitializer) creationState.resolveInitializer( final EmbeddableInitializer initializer = creationState.resolveInitializer(
getNavigablePath(), getNavigablePath(),
getReferencedModePart(), getReferencedModePart(),
() -> new AggregateEmbeddableFetchInitializer( () -> new AggregateEmbeddableFetchInitializer(
@ -161,7 +161,9 @@ public class AggregateEmbeddableFetchImpl extends AbstractFetchParent implements
creationState, creationState,
aggregateSelection aggregateSelection
) )
); ).asEmbeddableInitializer();
assert initializer != null;
return new EmbeddableAssembler( initializer ); return new EmbeddableAssembler( initializer );
} }

View File

@ -151,7 +151,7 @@ public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implem
public DomainResultAssembler<T> createResultAssembler( public DomainResultAssembler<T> createResultAssembler(
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
final EmbeddableInitializer initializer = (EmbeddableInitializer) creationState.resolveInitializer( final EmbeddableInitializer initializer = creationState.resolveInitializer(
initializerNavigablePath, initializerNavigablePath,
getReferencedModePart(), getReferencedModePart(),
() -> new AggregateEmbeddableResultInitializer( () -> new AggregateEmbeddableResultInitializer(
@ -160,7 +160,9 @@ public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implem
creationState, creationState,
aggregateSelection aggregateSelection
) )
); ).asEmbeddableInitializer();
assert initializer != null;
return new EmbeddableAssembler( initializer ); return new EmbeddableAssembler( initializer );
} }

View File

@ -122,7 +122,7 @@ public class EmbeddableExpressionResultImpl<T> extends AbstractFetchParent imple
public DomainResultAssembler<T> createResultAssembler( public DomainResultAssembler<T> createResultAssembler(
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
final EmbeddableInitializer initializer = (EmbeddableInitializer) creationState.resolveInitializer( final EmbeddableInitializer initializer = creationState.resolveInitializer(
getNavigablePath(), getNavigablePath(),
getReferencedModePart(), getReferencedModePart(),
() -> new EmbeddableResultInitializer( () -> new EmbeddableResultInitializer(
@ -130,7 +130,9 @@ public class EmbeddableExpressionResultImpl<T> extends AbstractFetchParent imple
parentAccess, parentAccess,
creationState creationState
) )
); ).asEmbeddableInitializer();
assert initializer != null;
//noinspection unchecked //noinspection unchecked
return new EmbeddableAssembler( initializer ); return new EmbeddableAssembler( initializer );

View File

@ -129,7 +129,7 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab
public DomainResultAssembler createAssembler( public DomainResultAssembler createAssembler(
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
final EmbeddableInitializer initializer = (EmbeddableInitializer) creationState.resolveInitializer( final EmbeddableInitializer initializer = creationState.resolveInitializer(
getNavigablePath(), getNavigablePath(),
getReferencedModePart(), getReferencedModePart(),
() -> new EmbeddableFetchInitializer( () -> new EmbeddableFetchInitializer(
@ -137,7 +137,9 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab
this, this,
creationState creationState
) )
); ).asEmbeddableInitializer();
assert initializer != null;
return new EmbeddableAssembler( initializer ); return new EmbeddableAssembler( initializer );
} }

View File

@ -95,11 +95,13 @@ public class EmbeddableForeignKeyResultImpl<T>
public DomainResultAssembler<T> createResultAssembler( public DomainResultAssembler<T> createResultAssembler(
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
final EmbeddableInitializer initializer = (EmbeddableInitializer) creationState.resolveInitializer( final EmbeddableInitializer initializer = creationState.resolveInitializer(
getNavigablePath(), getNavigablePath(),
getReferencedModePart(), getReferencedModePart(),
() -> new EmbeddableResultInitializer( this, parentAccess, creationState ) () -> new EmbeddableResultInitializer( this, parentAccess, creationState )
); ).asEmbeddableInitializer();
assert initializer != null;
//noinspection unchecked //noinspection unchecked
return new EmbeddableAssembler( initializer ); return new EmbeddableAssembler( initializer );

View File

@ -121,7 +121,7 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
public DomainResultAssembler<T> createResultAssembler( public DomainResultAssembler<T> createResultAssembler(
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
final EmbeddableInitializer initializer = (EmbeddableInitializer) creationState.resolveInitializer( final EmbeddableInitializer initializer = creationState.resolveInitializer(
initializerNavigablePath, initializerNavigablePath,
getReferencedModePart(), getReferencedModePart(),
() -> new EmbeddableResultInitializer( () -> new EmbeddableResultInitializer(
@ -129,7 +129,9 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
parentAccess, parentAccess,
creationState creationState
) )
); ).asEmbeddableInitializer();
assert initializer != null;
//noinspection unchecked //noinspection unchecked
return new EmbeddableAssembler( initializer ); return new EmbeddableAssembler( initializer );

View File

@ -6,9 +6,6 @@
*/ */
package org.hibernate.sql.results.graph.entity.internal; package org.hibernate.sql.results.graph.entity.internal;
import java.util.Collections;
import java.util.List;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.Fetch;

View File

@ -0,0 +1,121 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.sql.results.graph.entity.internal;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.log.LoggingHelper;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
/**
* Loads entities from the persistence context or creates proxies if not found there,
* and initializes all proxies in a batch.
*/
public class BatchInitializeEntitySelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer {
private final Set<EntityKey> toBatchLoad = new HashSet<>();
private State state = State.UNINITIALIZED;
public BatchInitializeEntitySelectFetchInitializer(
FetchParentAccess parentAccess,
ToOneAttributeMapping referencedModelPart,
NavigablePath fetchedNavigable,
EntityPersister concreteDescriptor,
DomainResultAssembler<?> identifierAssembler) {
super( parentAccess, referencedModelPart, fetchedNavigable, concreteDescriptor, identifierAssembler );
}
@Override
protected void registerResolutionListener() {
// No-op, because we resolve a proxy
}
@Override
public void resolveKey(RowProcessingState rowProcessingState) {
if ( state != State.UNINITIALIZED ) {
return;
}
super.resolveKey( rowProcessingState );
state = entityKey == null ? State.MISSING : State.KEY_RESOLVED;
}
@Override
public void resolveInstance(RowProcessingState rowProcessingState) {
if ( state != State.KEY_RESOLVED ) {
return;
}
state = State.INITIALIZED;
final SharedSessionContractImplementor session = rowProcessingState.getSession();
entityInstance = session.getPersistenceContext().getEntity( entityKey );
if ( entityInstance == null ) {
final LoadingEntityEntry loadingEntityEntry = rowProcessingState.getJdbcValuesSourceProcessingState()
.findLoadingEntityLocally( entityKey );
if ( loadingEntityEntry != null ) {
loadingEntityEntry.getEntityInitializer().resolveInstance( rowProcessingState );
entityInstance = loadingEntityEntry.getEntityInstance();
}
else {
if ( entityInstance == null ) {
// Force creating a proxy
entityInstance = session.internalLoad(
entityKey.getEntityName(),
entityKey.getIdentifier(),
false,
false
);
toBatchLoad.add( entityKey );
}
}
}
}
@Override
public boolean isEntityInitialized() {
return state == State.INITIALIZED;
}
@Override
public void finishUpRow(RowProcessingState rowProcessingState) {
super.finishUpRow( rowProcessingState );
state = State.UNINITIALIZED;
}
@Override
public void endLoading(ExecutionContext context) {
final SharedSessionContractImplementor session = context.getSession();
for ( EntityKey key : toBatchLoad ) {
loadInstance( key, referencedModelPart, session );
}
toBatchLoad.clear();
parentAccess = null;
}
@Override
public String toString() {
return "BatchInitializeEntitySelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
}
enum State {
UNINITIALIZED,
MISSING,
KEY_RESOLVED,
INITIALIZED
}
}

View File

@ -8,7 +8,6 @@ package org.hibernate.sql.results.graph.entity.internal;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -17,7 +16,6 @@ import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
/** /**
* An eager entity fetch performed as a subsequent (n+1) select * An eager entity fetch performed as a subsequent (n+1) select
@ -52,52 +50,22 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
} }
@Override @Override
public DomainResultAssembler<?> createAssembler(FetchParentAccess parentAccess, AssemblerCreationState creationState) { public DomainResultAssembler<?> createAssembler(
FetchParentAccess parentAccess,
AssemblerCreationState creationState) {
final Initializer initializer = creationState.resolveInitializer( final Initializer initializer = creationState.resolveInitializer(
getNavigablePath(), getNavigablePath(),
getFetchedMapping(), getFetchedMapping(),
() -> { () ->
EntitySelectFetchInitializerBuilder.createInitializer(
EntityPersister entityPersister = getReferencedMappingContainer().getEntityPersister();
final ToOneAttributeMapping fetchedAttribute = (ToOneAttributeMapping) getFetchedMapping();
if ( selectByUniqueKey ) {
return new EntitySelectFetchByUniqueKeyInitializer(
parentAccess, parentAccess,
fetchedAttribute, (ToOneAttributeMapping) getFetchedMapping(),
getReferencedMappingContainer().getEntityPersister(),
keyResult,
getNavigablePath(), getNavigablePath(),
entityPersister, selectByUniqueKey,
keyResult.createResultAssembler( parentAccess, creationState ) creationState
); )
}
if ( entityPersister.isBatchLoadable() && !creationState.isScrollResult() ) {
if ( parentAccess.isEmbeddableInitializer() ) {
return new BatchEntityInsideEmbeddableSelectFetchInitializer(
parentAccess,
fetchedAttribute,
getNavigablePath(),
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
}
return new BatchEntitySelectFetchInitializer(
parentAccess,
fetchedAttribute,
getNavigablePath(),
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
}
else {
return new EntitySelectFetchInitializer(
parentAccess,
fetchedAttribute,
getNavigablePath(),
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
}
}
); );
return new EntityAssembler( getResultJavaType(), initializer.asEntityInitializer() ); return new EntityAssembler( getResultJavaType(), initializer.asEntityInitializer() );

View File

@ -0,0 +1,111 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.sql.results.graph.entity.internal;
import org.hibernate.metamodel.internal.StandardEmbeddableInstantiator;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
public class EntitySelectFetchInitializerBuilder {
public static AbstractFetchParentAccess createInitializer(
FetchParentAccess parentAccess,
ToOneAttributeMapping fetchedAttribute,
EntityPersister entityPersister,
DomainResult<?> keyResult,
NavigablePath navigablePath,
boolean selectByUniqueKey,
AssemblerCreationState creationState) {
if ( selectByUniqueKey ) {
return new EntitySelectFetchByUniqueKeyInitializer(
parentAccess,
fetchedAttribute,
navigablePath,
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
}
final BatchMode batchMode = determineBatchMode( entityPersister, parentAccess, creationState );
switch ( batchMode ) {
case NONE:
return new EntitySelectFetchInitializer(
parentAccess,
fetchedAttribute,
navigablePath,
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
case BATCH_LOAD:
if ( parentAccess.isEmbeddableInitializer() ) {
return new BatchEntityInsideEmbeddableSelectFetchInitializer(
parentAccess,
fetchedAttribute,
navigablePath,
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
}
else {
return new BatchEntitySelectFetchInitializer(
parentAccess,
fetchedAttribute,
navigablePath,
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
}
case BATCH_INITIALIZE:
return new BatchInitializeEntitySelectFetchInitializer(
parentAccess,
fetchedAttribute,
navigablePath,
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
}
throw new IllegalStateException( "Should be unreachable" );
}
private static BatchMode determineBatchMode(EntityPersister entityPersister, FetchParentAccess parentAccess, AssemblerCreationState creationState) {
if ( !entityPersister.isBatchLoadable() || creationState.isScrollResult() ) {
return BatchMode.NONE;
}
while ( parentAccess.isEmbeddableInitializer() ) {
final EmbeddableInitializer embeddableInitializer = parentAccess.asEmbeddableInitializer();
final EmbeddableValuedModelPart initializedPart = embeddableInitializer.getInitializedPart();
// For entity identifier mappings we can't batch load,
// because the entity identifier needs the instance in the resolveKey phase,
// but batch loading is inherently executed out of order
if ( initializedPart.isEntityIdentifierMapping()
// todo: check if the virtual check is necessary
|| initializedPart.isVirtual()
// If the parent embeddable has a custom instantiator, we can't inject entities later through setValues()
|| !( initializedPart.getMappedType().getRepresentationStrategy().getInstantiator() instanceof StandardEmbeddableInstantiator ) ) {
return entityPersister.hasSubclasses() ? BatchMode.NONE : BatchMode.BATCH_INITIALIZE;
}
parentAccess = parentAccess.getFetchParentAccess();
if ( parentAccess == null ) {
break;
}
}
return BatchMode.BATCH_LOAD;
}
enum BatchMode {
NONE,
BATCH_LOAD,
BATCH_INITIALIZE
}
}

View File

@ -9,7 +9,6 @@ package org.hibernate.sql.results.internal.domain;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.BiDirectionalFetch; import org.hibernate.sql.results.graph.BiDirectionalFetch;
@ -20,11 +19,8 @@ import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer;
import org.hibernate.sql.results.graph.entity.internal.BatchEntitySelectFetchInitializer;
import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer; import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer;
import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchByUniqueKeyInitializer; import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializerBuilder;
import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializer;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
@ -95,50 +91,21 @@ public class CircularFetchImpl implements BiDirectionalFetch {
public DomainResultAssembler<?> createAssembler( public DomainResultAssembler<?> createAssembler(
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
final DomainResultAssembler<?> keyAssembler = keyResult.createResultAssembler( parentAccess, creationState );
final Initializer initializer = creationState.resolveInitializer( final Initializer initializer = creationState.resolveInitializer(
getNavigablePath(), getNavigablePath(),
referencedModelPart, referencedModelPart,
() -> { () -> {
if ( timing == FetchTiming.IMMEDIATE ) { if ( timing == FetchTiming.IMMEDIATE ) {
if ( selectByUniqueKey ) { return EntitySelectFetchInitializerBuilder.createInitializer(
return new EntitySelectFetchByUniqueKeyInitializer( parentAccess,
parentAccess, fetchable,
fetchable, entityMappingType.getEntityPersister(),
getNavigablePath(), keyResult,
entityMappingType.getEntityPersister(), getNavigablePath(),
keyAssembler selectByUniqueKey,
); creationState
} );
final EntityPersister entityPersister = entityMappingType.getEntityPersister();
if ( entityPersister.isBatchLoadable() ) {
if ( parentAccess.isEmbeddableInitializer() ) {
return new BatchEntityInsideEmbeddableSelectFetchInitializer(
parentAccess,
referencedModelPart,
getNavigablePath(),
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
}
return new BatchEntitySelectFetchInitializer(
parentAccess,
referencedModelPart,
getReferencedPath(),
entityPersister,
keyAssembler
);
}
else {
return new EntitySelectFetchInitializer(
parentAccess,
(ToOneAttributeMapping) referencedModelPart,
getReferencedPath(),
entityPersister,
keyAssembler
);
}
} }
else { else {
return new EntityDelayedFetchInitializer( return new EntityDelayedFetchInitializer(
@ -146,7 +113,7 @@ public class CircularFetchImpl implements BiDirectionalFetch {
getReferencedPath(), getReferencedPath(),
fetchable, fetchable,
selectByUniqueKey, selectByUniqueKey,
keyAssembler keyResult.createResultAssembler( parentAccess, creationState )
); );
} }
} }
@ -158,6 +125,8 @@ public class CircularFetchImpl implements BiDirectionalFetch {
); );
} }
@Override @Override
public FetchTiming getTiming() { public FetchTiming getTiming() {
return timing; return timing;