HHH-15644 ClassCastException when batch-fetching association in embeddable

This commit is contained in:
Andrea Boriero 2022-11-07 23:01:02 +01:00 committed by Andrea Boriero
parent 00717c6911
commit 29cfc5a7e0
13 changed files with 511 additions and 206 deletions

View File

@ -47,6 +47,7 @@ import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.FetchableContainer;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.type.descriptor.java.JavaType;
@ -591,6 +592,11 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
return null;
}
@Override
public EntityInitializer findFirstEntityInitializer() {
return null;
}
@Override
public Object getParentKey() {
return null;

View File

@ -6,8 +6,13 @@
*/
package org.hibernate.persister.walking.spi;
import org.hibernate.NotYetImplementedFor6Exception;
/**
* @author Steve Ebersole
*/
public interface AttributeSource {
default int getPropertyIndex(String propertyName) {
throw new NotYetImplementedFor6Exception( getClass() );
}
}

View File

@ -9,6 +9,7 @@ package org.hibernate.sql.results.graph;
import java.util.function.Consumer;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
/**
* Provides access to information about the owner/parent of a fetch
@ -22,6 +23,13 @@ public interface FetchParentAccess extends Initializer {
*/
FetchParentAccess findFirstEntityDescriptorAccess();
default EntityInitializer findFirstEntityInitializer(){
if ( this instanceof EntityInitializer ) {
return (EntityInitializer) this;
}
return (EntityInitializer) findFirstEntityDescriptorAccess();
}
Object getParentKey();
NavigablePath getNavigablePath();
@ -32,4 +40,8 @@ public interface FetchParentAccess extends Initializer {
* @apiNote If already resolved, the callback is triggered immediately
*/
void registerResolutionListener(Consumer<Object> resolvedParentConsumer);
default boolean isEmbeddable(){
return false;
}
}

View File

@ -137,6 +137,11 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
return getFetchParentAccess().findFirstEntityDescriptorAccess();
}
@Override
public EntityInitializer findFirstEntityInitializer() {
return findFirstEntityDescriptorAccess().findFirstEntityInitializer();
}
@Override
public void resolveKey(RowProcessingState processingState) {
// nothing to do

View File

@ -25,4 +25,9 @@ public interface EmbeddableInitializer extends FetchParentAccess {
default Object getInitializedInstance() {
return getCompositeInstance();
}
@Override
default boolean isEmbeddable() {
return true;
}
}

View File

@ -29,6 +29,11 @@ public interface EntityInitializer extends FetchParentAccess {
return this;
}
@Override
default EntityInitializer findFirstEntityInitializer() {
return this;
}
/**
* Get the entity instance for the currently processing "row".
*

View File

@ -0,0 +1,224 @@
/*
* 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.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.graph.entity.EntityLoadingLogging;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import static org.hibernate.internal.log.LoggingHelper.toLoggableString;
public abstract class AbstractBatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
implements EntityInitializer {
protected FetchParentAccess parentAccess;
private final NavigablePath navigablePath;
protected final EntityPersister concreteDescriptor;
protected final DomainResultAssembler identifierAssembler;
protected final ToOneAttributeMapping referencedModelPart;
protected Object entityInstance;
protected EntityKey entityKey;
private boolean isInitialized;
public AbstractBatchEntitySelectFetchInitializer(
FetchParentAccess parentAccess,
ToOneAttributeMapping referencedModelPart,
NavigablePath fetchedNavigable,
EntityPersister concreteDescriptor,
DomainResultAssembler identifierAssembler) {
this.parentAccess = parentAccess;
this.referencedModelPart = referencedModelPart;
this.navigablePath = fetchedNavigable;
this.concreteDescriptor = concreteDescriptor;
this.identifierAssembler = identifierAssembler;
}
public ModelPart getInitializedPart() {
return referencedModelPart;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override
public void resolveKey(RowProcessingState rowProcessingState) {
}
@Override
public void resolveInstance(RowProcessingState rowProcessingState) {
}
@Override
public void initializeInstance(RowProcessingState rowProcessingState) {
if ( isInitialized ) {
return;
}
if ( !isAttributeAssignableToConcreteDescriptor() ) {
return;
}
final Object entityIdentifier = identifierAssembler.assemble( rowProcessingState );
if ( entityIdentifier == null ) {
return;
}
entityKey = new EntityKey( entityIdentifier, concreteDescriptor );
final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal();
entityInstance = persistenceContext.getEntity( entityKey );
if ( entityInstance != null ) {
return;
}
Initializer initializer = rowProcessingState.getJdbcValuesSourceProcessingState()
.findInitializer( entityKey );
if ( initializer != null ) {
if ( EntityLoadingLogging.DEBUG_ENABLED ) {
EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Found an initializer for entity (%s) : %s",
getConcreteName(),
toLoggableString( getNavigablePath(), entityIdentifier ),
entityIdentifier
);
}
initializer.resolveInstance( rowProcessingState );
entityInstance = initializer.getInitializedInstance();
// EARLY EXIT!!!
return;
}
final LoadingEntityEntry existingLoadingEntry = rowProcessingState.getSession()
.getPersistenceContext()
.getLoadContexts()
.findLoadingEntityEntry( entityKey );
if ( existingLoadingEntry != null ) {
if ( existingLoadingEntry.getEntityInitializer() != this ) {
// the entity is already being loaded elsewhere
if ( EntityLoadingLogging.DEBUG_ENABLED ) {
EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
getConcreteName(),
toLoggableString( getNavigablePath(), entityIdentifier ),
existingLoadingEntry.getEntityInitializer()
);
}
this.entityInstance = existingLoadingEntry.getEntityInstance();
// EARLY EXIT!!!
return;
}
}
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( entityKey );
addParentInfo();
isInitialized = true;
}
protected abstract String getConcreteName();
protected abstract void addParentInfo();
protected boolean isAttributeAssignableToConcreteDescriptor() {
if ( parentAccess instanceof EntityInitializer ) {
final AbstractEntityPersister concreteDescriptor = (AbstractEntityPersister) ( (EntityInitializer) parentAccess ).getConcreteDescriptor();
if ( concreteDescriptor.isPolymorphic() ) {
final AbstractEntityPersister declaringType = (AbstractEntityPersister) referencedModelPart.getDeclaringType();
if ( concreteDescriptor != declaringType ) {
if ( !declaringType.getSubclassEntityNames().contains( concreteDescriptor.getName() ) ) {
return false;
}
}
}
}
return true;
}
@Override
public void finishUpRow(RowProcessingState rowProcessingState) {
entityInstance = null;
clearResolutionListeners();
isInitialized = false;
}
@Override
public EntityPersister getEntityDescriptor() {
return concreteDescriptor;
}
@Override
public Object getEntityInstance() {
return entityInstance;
}
@Override
public EntityKey getEntityKey() {
return entityKey;
}
@Override
public Object getParentKey() {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public void registerResolutionListener(Consumer<Object> listener) {
if ( entityInstance != null ) {
listener.accept( entityInstance );
}
else {
super.registerResolutionListener( listener );
}
}
protected static Object loadInstance(
EntityKey entityKey,
ToOneAttributeMapping referencedModelPart,
SharedSessionContractImplementor session) {
return session.internalLoad(
entityKey.getEntityName(),
entityKey.getIdentifier(),
true,
referencedModelPart.isInternalLoadNullable()
);
}
protected static int getPropertyIndex(FetchParentAccess parentAccess, String propertyName) {
return parentAccess.findFirstEntityInitializer().getEntityDescriptor().getPropertyIndex( propertyName );
}
@Override
public EntityPersister getConcreteDescriptor() {
return concreteDescriptor;
}
}

View File

@ -0,0 +1,165 @@
/*
* 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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.engine.spi.EntityEntry;
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.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.EntityInitializer;
public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer {
private static final String CONCRETE_NAME = BatchEntityInsideEmbeddableSelectFetchInitializer.class.getSimpleName();
/*
Object[0] will contain the parent EntityKey and Object[1] the parent embeddable instance,
*/
private Map<EntityKey, List<Object[]>> toBatchLoad = new HashMap<>();
public BatchEntityInsideEmbeddableSelectFetchInitializer(
FetchParentAccess parentAccess,
ToOneAttributeMapping referencedModelPart,
NavigablePath fetchedNavigable,
EntityPersister concreteDescriptor,
DomainResultAssembler identifierAssembler) {
super( parentAccess, referencedModelPart, fetchedNavigable, concreteDescriptor, identifierAssembler );
}
@Override
protected String getConcreteName() {
return CONCRETE_NAME;
}
@Override
protected void addParentInfo() {
final List<Object[]> parents = getBatchInfos();
parentAccess.registerResolutionListener(
o ->
parents.add(
new Object[] {
parentAccess.findFirstEntityInitializer().getEntityKey(),
o
}
)
);
}
private List<Object[]> getBatchInfos() {
List<Object[]> objects = toBatchLoad.get( entityKey );
if ( objects == null ) {
objects = new ArrayList<>();
toBatchLoad.put( entityKey, objects );
}
return objects;
}
@Override
public void endLoading(ExecutionContext context) {
final EntityInitializer entityInitializer = parentAccess.findFirstEntityInitializer();
final String rootEmbeddablePropertyName = getRootEmbeddablePropertyName();
final int rootEmbeddablePropertyIndex = getPropertyIndex( parentAccess, rootEmbeddablePropertyName );
toBatchLoad.forEach(
(entityKey, parentInfos) -> {
final SharedSessionContractImplementor session = context.getSession();
final Object loadedInstance = loadInstance( entityKey, referencedModelPart, session );
for ( Object[] parentInfo : parentInfos ) {
final PersistenceContext persistenceContext = session.getPersistenceContext();
setInstance(
entityInitializer,
referencedModelPart,
rootEmbeddablePropertyName,
rootEmbeddablePropertyIndex,
loadedInstance,
parentInfo[1],
(EntityKey) parentInfo[0],
persistenceContext.getEntry( persistenceContext.getEntity( (EntityKey) parentInfo[0] ) ),
session
);
}
}
);
toBatchLoad.clear();
parentAccess = null;
}
protected static void setInstance(
EntityInitializer entityInitializer,
ToOneAttributeMapping referencedModelPart,
String rootEmbeddablePropertyName,
int propertyIndex,
Object loadedInstance,
Object embeddableParentInstance,
EntityKey parentEntityKey,
EntityEntry parentEntityEntry,
SharedSessionContractImplementor session) {
referencedModelPart.getPropertyAccess().getSetter().set( embeddableParentInstance, loadedInstance );
updateRootEntityLoadedState(
entityInitializer,
rootEmbeddablePropertyName,
propertyIndex,
parentEntityKey,
parentEntityEntry,
session
);
}
private static void updateRootEntityLoadedState(
EntityInitializer entityInitializer,
String rootEmbeddablePropertyName,
int propertyIndex,
EntityKey parentEntityKey,
EntityEntry parentEntityEntry,
SharedSessionContractImplementor session) {
Object[] loadedState = parentEntityEntry.getLoadedState();
if ( loadedState != null ) {
/*
E.g.
ParentEntity -> RootEmbeddable -> ParentEmbeddable -> toOneAttributeMapping
The value of RootEmbeddable is needed to update the ParentEntity loaded state
*/
final EntityPersister entityDescriptor = entityInitializer.getEntityDescriptor();
final Object rootEmbeddable = entityDescriptor.getPropertyValue(
session.getPersistenceContext().getEntity( parentEntityKey ),
rootEmbeddablePropertyName
);
loadedState[propertyIndex] = entityDescriptor.getPropertyType( rootEmbeddablePropertyName )
.deepCopy( rootEmbeddable, session.getFactory() );
}
}
protected String getRootEmbeddablePropertyName() {
final NavigablePath entityPath = parentAccess.findFirstEntityDescriptorAccess().getNavigablePath();
NavigablePath navigablePath = parentAccess.getNavigablePath();
if ( navigablePath == entityPath ) {
return referencedModelPart.getPartName();
}
while ( navigablePath.getParent() != entityPath ) {
navigablePath = navigablePath.getParent();
}
return navigablePath.getLocalName();
}
@Override
public String toString() {
return "BatchEntityInsideEmbeddableSelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
}
}

View File

@ -10,46 +10,23 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.EntityEntry;
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.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.graph.entity.EntityLoadingLogging;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import static org.hibernate.internal.log.LoggingHelper.toLoggableString;
public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess implements EntityInitializer {
public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer {
private static final String CONCRETE_NAME = BatchEntitySelectFetchInitializer.class.getSimpleName();
private FetchParentAccess parentAccess;
private final NavigablePath navigablePath;
protected final EntityPersister concreteDescriptor;
protected final DomainResultAssembler identifierAssembler;
private final ToOneAttributeMapping referencedModelPart;
protected Object entityInstance;
private EntityKey entityKey;
private Map<EntityKey, List<Object>> toBatchLoad = new HashMap<>();
private boolean isInitialized;
private final Map<EntityKey, List<Object>> toBatchLoad = new HashMap<>();
public BatchEntitySelectFetchInitializer(
FetchParentAccess parentAccess,
@ -57,104 +34,23 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
NavigablePath fetchedNavigable,
EntityPersister concreteDescriptor,
DomainResultAssembler identifierAssembler) {
this.parentAccess = parentAccess;
this.referencedModelPart = referencedModelPart;
this.navigablePath = fetchedNavigable;
this.concreteDescriptor = concreteDescriptor;
this.identifierAssembler = identifierAssembler;
}
public ModelPart getInitializedPart() {
return referencedModelPart;
super( parentAccess, referencedModelPart, fetchedNavigable, concreteDescriptor, identifierAssembler );
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
protected String getConcreteName() {
return CONCRETE_NAME;
}
@Override
public void resolveKey(RowProcessingState rowProcessingState) {
}
@Override
public void resolveInstance(RowProcessingState rowProcessingState) {
}
@Override
public void initializeInstance(RowProcessingState rowProcessingState) {
if ( isInitialized ) {
return;
}
if ( !isAttributeAssignableToConcreteDescriptor() ) {
return;
}
final Object entityIdentifier = identifierAssembler.assemble( rowProcessingState );
if ( entityIdentifier == null ) {
return;
}
entityKey = new EntityKey( entityIdentifier, concreteDescriptor );
final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal();
entityInstance = persistenceContext.getEntity( entityKey );
if ( entityInstance != null ) {
return;
}
Initializer initializer = rowProcessingState.getJdbcValuesSourceProcessingState()
.findInitializer( entityKey );
if ( initializer != null ) {
if ( EntityLoadingLogging.DEBUG_ENABLED ) {
EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Found an initializer for entity (%s) : %s",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
entityIdentifier
);
}
initializer.resolveInstance( rowProcessingState );
entityInstance = initializer.getInitializedInstance();
// EARLY EXIT!!!
return;
}
final LoadingEntityEntry existingLoadingEntry = rowProcessingState.getSession()
.getPersistenceContext()
.getLoadContexts()
.findLoadingEntityEntry( entityKey );
if ( existingLoadingEntry != null ) {
if ( existingLoadingEntry.getEntityInitializer() != this ) {
// the entity is already being loaded elsewhere
if ( EntityLoadingLogging.DEBUG_ENABLED ) {
EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
existingLoadingEntry.getEntityInitializer()
);
}
this.entityInstance = existingLoadingEntry.getEntityInstance();
// EARLY EXIT!!!
return;
}
}
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( entityKey );
List<Object> objects = getObjects();
protected void addParentInfo() {
final List<Object> parents = getBatchInfos();
parentAccess.registerResolutionListener(
o -> objects.add( o )
o -> parents.add( o )
);
isInitialized = true;
}
private List<Object> getObjects() {
private List<Object> getBatchInfos() {
List<Object> objects = toBatchLoad.get( entityKey );
if ( objects == null ) {
objects = new ArrayList<>();
@ -163,97 +59,63 @@ public class BatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
return objects;
}
protected boolean isAttributeAssignableToConcreteDescriptor() {
if ( parentAccess instanceof EntityInitializer ) {
final AbstractEntityPersister concreteDescriptor = (AbstractEntityPersister) ( (EntityInitializer) parentAccess ).getConcreteDescriptor();
if ( concreteDescriptor.isPolymorphic() ) {
final AbstractEntityPersister declaringType = (AbstractEntityPersister) referencedModelPart.getDeclaringType();
if ( concreteDescriptor != declaringType ) {
if ( !declaringType.getSubclassEntityNames().contains( concreteDescriptor.getName() ) ) {
return false;
@Override
public void endLoading(ExecutionContext context) {
final EntityInitializer entityInitializer = parentAccess.findFirstEntityInitializer();
final String propertyName = referencedModelPart.getPartName();
final int propertyIndex = getPropertyIndex( parentAccess, propertyName );
toBatchLoad.forEach(
(entityKey, parentInfos) -> {
final SharedSessionContractImplementor session = context.getSession();
final Object instance = loadInstance( entityKey, referencedModelPart, session );
for ( Object parentInstance : parentInfos ) {
setInstance(
entityInitializer,
referencedModelPart,
propertyName,
propertyIndex,
session,
instance,
parentInstance,
session.getPersistenceContext().getEntry( parentInstance )
);
}
}
}
);
toBatchLoad.clear();
parentAccess = null;
}
protected static void setInstance(
EntityInitializer entityInitializer,
ToOneAttributeMapping referencedModelPart,
String propertyName,
int propertyIndex,
SharedSessionContractImplementor session,
Object instance,
Object parentInstance,
EntityEntry entry) {
referencedModelPart.getPropertyAccess().getSetter().set( parentInstance, instance );
updateParentEntityLoadedState( entityInitializer, propertyName, propertyIndex, session, instance, entry );
}
private static void updateParentEntityLoadedState(
EntityInitializer entityInitializer,
String propertyName,
int propertyIndex,
SharedSessionContractImplementor session,
Object instance,
EntityEntry entry) {
final Object[] loadedState = entry.getLoadedState();
if ( loadedState != null ) {
loadedState[propertyIndex] = entityInitializer.getEntityDescriptor().getPropertyType( propertyName )
.deepCopy( instance, session.getFactory() );
}
return true;
}
@Override
public void finishUpRow(RowProcessingState rowProcessingState) {
entityInstance = null;
clearResolutionListeners();
isInitialized = false;
}
@Override
public EntityPersister getEntityDescriptor() {
return concreteDescriptor;
}
@Override
public Object getEntityInstance() {
return entityInstance;
}
@Override
public EntityKey getEntityKey() {
return entityKey;
}
@Override
public EntityPersister findFirstEntityPersister() {
return getEntityDescriptor();
}
@Override
public Object getParentKey() {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public void registerResolutionListener(Consumer<Object> listener) {
if ( entityInstance != null ) {
listener.accept( entityInstance );
}
else {
super.registerResolutionListener( listener );
}
}
@Override
public EntityPersister getConcreteDescriptor() {
return concreteDescriptor;
}
@Override
public String toString() {
return "EntitySelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
return "BatchEntitySelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
}
@Override
public void endLoading(ExecutionContext context) {
toBatchLoad.forEach(
(entityKey, parentInstances) -> {
final Object instance = context.getSession().internalLoad(
entityKey.getEntityName(),
entityKey.getIdentifier(),
true,
referencedModelPart.isInternalLoadNullable()
);
for ( Object parentInstance : parentInstances ) {
referencedModelPart.getPropertyAccess().getSetter().set(parentInstance, instance);
final EntityEntry entry = context.getSession().getPersistenceContext().getEntry( parentInstance );
if ( entry != null ) {
final Object[] loadedState = entry.getLoadedState();
if ( loadedState != null ) {
loadedState[0] = instance;
}
}
}
}
);
toBatchLoad = null;
parentAccess = null;
}
}

View File

@ -70,6 +70,15 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
);
}
if ( entityPersister.isBatchLoadable() && !creationState.isScrollResult() ) {
if ( parentAccess.isEmbeddable() ) {
return new BatchEntityInsideEmbeddableSelectFetchInitializer(
parentAccess,
fetchedAttribute,
getNavigablePath(),
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
}
return new BatchEntitySelectFetchInitializer(
parentAccess,
fetchedAttribute,

View File

@ -35,6 +35,7 @@ import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Fetchable;
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.EntitySelectFetchByUniqueKeyInitializer;
@ -48,7 +49,7 @@ import org.hibernate.type.descriptor.java.JavaType;
*/
public class CircularFetchImpl implements BiDirectionalFetch, Association {
private final DomainResult<?> keyResult;
private final EntityValuedModelPart referencedModelPart;
private final ToOneAttributeMapping referencedModelPart;
private final EntityMappingType entityMappingType;
private final FetchTiming timing;
private final NavigablePath navigablePath;
@ -59,7 +60,7 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
private final NavigablePath referencedNavigablePath;
public CircularFetchImpl(
EntityValuedModelPart referencedModelPart,
ToOneAttributeMapping referencedModelPart,
EntityMappingType entityMappingType,
FetchTiming timing,
NavigablePath navigablePath,
@ -127,9 +128,18 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
}
final EntityPersister entityPersister = entityMappingType.getEntityPersister();
if ( entityPersister.isBatchLoadable() ) {
if ( parentAccess.isEmbeddable() ) {
return new BatchEntityInsideEmbeddableSelectFetchInitializer(
parentAccess,
referencedModelPart,
getNavigablePath(),
entityPersister,
keyResult.createResultAssembler( parentAccess, creationState )
);
}
return new BatchEntitySelectFetchInitializer(
parentAccess,
(ToOneAttributeMapping) referencedModelPart,
referencedModelPart,
getReferencedPath(),
entityPersister,
keyAssembler

View File

@ -19,8 +19,7 @@ import org.hibernate.type.CompositeType;
* @author Steve Ebersole
*/
public abstract class AbstractCompositionAttribute
extends AbstractNonIdentifierAttribute
implements AttributeSource {
extends AbstractNonIdentifierAttribute {
private final int columnStartPosition;

View File

@ -8,7 +8,6 @@ package org.hibernate.tuple.entity;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AttributeSource;
import org.hibernate.tuple.BaselineAttributeInformation;
import org.hibernate.tuple.component.AbstractCompositionAttribute;
import org.hibernate.type.CompositeType;
@ -17,8 +16,7 @@ import org.hibernate.type.CompositeType;
* @author Steve Ebersole
*/
public class EntityBasedCompositionAttribute
extends AbstractCompositionAttribute
implements AttributeSource {
extends AbstractCompositionAttribute {
public EntityBasedCompositionAttribute(
EntityPersister source,