HHH-15644 ClassCastException when batch-fetching association in embeddable
This commit is contained in:
parent
00717c6911
commit
29cfc5a7e0
|
@ -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;
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -25,4 +25,9 @@ public interface EmbeddableInitializer extends FetchParentAccess {
|
|||
default Object getInitializedInstance() {
|
||||
return getCompositeInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isEmbeddable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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".
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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() ) + ")";
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue