HHH-17030 Rework the initializer interactions and polish the interface design
This commit is contained in:
parent
3263ac4fd0
commit
d02d8d7af6
|
@ -178,10 +178,13 @@ public abstract class AbstractEntityInsertAction extends EntityAction {
|
|||
PersistenceContext persistenceContext) {
|
||||
if ( object != null ) {
|
||||
final EmbeddableMappingType descriptor = attributeMapping.getEmbeddableTypeDescriptor();
|
||||
final EmbeddableMappingType.ConcreteEmbeddableType concreteEmbeddableType = descriptor.findSubtypeBySubclass(
|
||||
object.getClass().getName()
|
||||
);
|
||||
final AttributeMappingsList attributeMappings = descriptor.getAttributeMappings();
|
||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||
final AttributeMapping attribute = attributeMappings.get( i );
|
||||
if ( descriptor.declaresAttribute( object.getClass().getName(), attributeMapping ) ) {
|
||||
if ( concreteEmbeddableType.declaresAttribute( attribute ) ) {
|
||||
if ( attribute.isPluralAttributeMapping() ) {
|
||||
addCollectionKey(
|
||||
attribute.asPluralAttributeMapping(),
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.hibernate.sql.results.graph.AssemblerCreationState;
|
|||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
|
||||
/**
|
||||
|
@ -41,7 +42,7 @@ public interface CollectionInitializerProducer {
|
|||
* @param collectionValueKeyAssembler allows creation of a
|
||||
* {@link org.hibernate.sql.results.graph.DomainResult} for
|
||||
* either side of the collection foreign key
|
||||
* @deprecated Use {@link #produceInitializer(NavigablePath, PluralAttributeMapping, FetchParentAccess, LockMode, DomainResult, DomainResult, boolean, AssemblerCreationState)} instead
|
||||
* @deprecated Use {@link #produceInitializer(NavigablePath, PluralAttributeMapping, InitializerParent, LockMode, DomainResult, DomainResult, boolean, AssemblerCreationState)} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
CollectionInitializer produceInitializer(
|
||||
|
@ -56,7 +57,7 @@ public interface CollectionInitializerProducer {
|
|||
default CollectionInitializer produceInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping attribute,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
|
@ -67,30 +68,30 @@ public interface CollectionInitializerProducer {
|
|||
if ( collectionKeyResult == null ) {
|
||||
collectionKeyAssembler = null;
|
||||
collectionValueKeyAssembler = collectionValueKeyResult.createResultAssembler(
|
||||
null,
|
||||
(InitializerParent) null,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
else if ( collectionKeyResult == collectionValueKeyResult ) {
|
||||
collectionKeyAssembler = collectionValueKeyAssembler = collectionKeyResult.createResultAssembler(
|
||||
null,
|
||||
(InitializerParent) null,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
else {
|
||||
collectionKeyAssembler = collectionKeyResult.createResultAssembler(
|
||||
null,
|
||||
(InitializerParent) null,
|
||||
creationState
|
||||
);
|
||||
collectionValueKeyAssembler = collectionValueKeyResult.createResultAssembler(
|
||||
null,
|
||||
(InitializerParent) null,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
return produceInitializer(
|
||||
navigablePath,
|
||||
attribute,
|
||||
parentAccess,
|
||||
(FetchParentAccess) parent,
|
||||
lockMode,
|
||||
collectionKeyAssembler,
|
||||
collectionValueKeyAssembler,
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
|
|||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
|
@ -271,6 +272,8 @@ public class BatchFetchQueue {
|
|||
|
||||
final LinkedHashSet<EntityKey> set =
|
||||
batchLoadableEntityKeys.get( entityDescriptor.getEntityName() );
|
||||
final EntityPersister entityPersister = entityDescriptor.getEntityPersister();
|
||||
final Type identifierType = entityPersister.getIdentifierType();
|
||||
if ( set != null ) {
|
||||
for ( EntityKey key : set ) {
|
||||
if ( checkForEnd && i == end ) {
|
||||
|
@ -278,12 +281,11 @@ public class BatchFetchQueue {
|
|||
return ids;
|
||||
}
|
||||
|
||||
if ( entityDescriptor.getEntityPersister().getIdentifierType()
|
||||
.isEqual( loadingId, key.getIdentifier() ) ) {
|
||||
if ( identifierType.isEqual( loadingId, key.getIdentifier() ) ) {
|
||||
end = i;
|
||||
}
|
||||
else {
|
||||
if ( !isCached( key, entityDescriptor.getEntityPersister() ) ) {
|
||||
if ( !isCached( key, entityPersister ) ) {
|
||||
ids[i++] = key.getIdentifier();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@ import org.hibernate.sql.results.spi.RowReader;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class FetchingScrollableResultsImpl<R> extends AbstractScrollableResults<R> {
|
||||
private final EntityInitializer resultInitializer;
|
||||
|
||||
private R currentRow;
|
||||
|
||||
private int currentPosition;
|
||||
|
@ -50,17 +48,10 @@ public class FetchingScrollableResultsImpl<R> extends AbstractScrollableResults<
|
|||
persistenceContext
|
||||
);
|
||||
|
||||
resultInitializer = extractResultInitializer( rowReader );
|
||||
|
||||
this.maxPosition = jdbcValuesSourceProcessingState.getQueryOptions().getEffectiveLimit().getMaxRows();
|
||||
beforeFirst = true;
|
||||
}
|
||||
|
||||
private static <R> EntityInitializer extractResultInitializer(RowReader<R> rowReader) {
|
||||
Initializer initializer = rowReader.getInitializers().get( rowReader.getInitializers().size() - 1 );
|
||||
return initializer.asEntityInitializer(); //might return null when it's not an EntityInitializer (intentional)
|
||||
}
|
||||
|
||||
@Override
|
||||
protected R getCurrentRow() {
|
||||
return currentRow;
|
||||
|
@ -332,7 +323,6 @@ public class FetchingScrollableResultsImpl<R> extends AbstractScrollableResults<
|
|||
if ( rowProcessingState.next() ) {
|
||||
final EntityKey entityKey2 = getEntityKey();
|
||||
if ( !entityKey.equals( entityKey2 ) ) {
|
||||
resultInitializer.finishUpRow( rowProcessingState );
|
||||
resultProcessed = true;
|
||||
last = false;
|
||||
}
|
||||
|
@ -364,9 +354,6 @@ public class FetchingScrollableResultsImpl<R> extends AbstractScrollableResults<
|
|||
}
|
||||
|
||||
private EntityKey getEntityKey() {
|
||||
resultInitializer.resolveKey( getRowProcessingState() );
|
||||
final EntityKey entityKey = resultInitializer.getEntityKey();
|
||||
resultInitializer.finishUpRow( getRowProcessingState() );
|
||||
return entityKey;
|
||||
return getRowReader().resolveSingleResultEntityKey( getRowProcessingState() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -356,6 +356,7 @@ public class CacheEntityLoaderHelper {
|
|||
final StatefulPersistenceContext statefulPersistenceContext = (StatefulPersistenceContext) session.getPersistenceContext();
|
||||
|
||||
if ( ( isManagedEntity( entity ) ) ) {
|
||||
statefulPersistenceContext.addEntity( entityKey, entity );
|
||||
statefulPersistenceContext.addReferenceEntry(
|
||||
entity,
|
||||
Status.READ_ONLY
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.internal.util.IndexedConsumer;
|
||||
import org.hibernate.internal.util.MutableInteger;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
|
||||
import org.hibernate.property.access.spi.Getter;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
|
@ -52,16 +55,39 @@ public interface EmbeddableMappingType extends ManagedMappingType, SelectableMap
|
|||
return getDiscriminatorMapping() != null;
|
||||
}
|
||||
|
||||
interface ConcreteEmbeddableType {
|
||||
|
||||
EmbeddableInstantiator getInstantiator();
|
||||
|
||||
int getSubclassId();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the provided embeddable class contains the
|
||||
* specified attribute mapping, {@code false} otherwise.
|
||||
* @implNote This method always returns {@code true} for non-polymorphic embeddable types
|
||||
*
|
||||
* @param attributeMapping the attribute to check
|
||||
*/
|
||||
boolean declaresAttribute(AttributeMapping attributeMapping);
|
||||
|
||||
boolean declaresAttribute(int attributeIndex);
|
||||
|
||||
Object getDiscriminatorValue();
|
||||
}
|
||||
|
||||
default ConcreteEmbeddableType findSubtypeByDiscriminator(Object discriminatorValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default ConcreteEmbeddableType findSubtypeBySubclass(String subclassName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the provided embeddable class contains the
|
||||
* specified attribute mapping, {@code false} otherwise.
|
||||
* @implNote This method always returns {@code true} for non-polymorphic embeddable types
|
||||
*
|
||||
* @param embeddableClassName the embeddable subclass in which the attribute must be declared
|
||||
* @param attributeMapping the attribute to check
|
||||
* Returns the concrete embeddable subtypes or an empty collection if {@link #isPolymorphic()} is {@code false}.
|
||||
*/
|
||||
default boolean declaresAttribute(String embeddableClassName, AttributeMapping attributeMapping) {
|
||||
return true;
|
||||
default Collection<ConcreteEmbeddableType> getConcreteEmbeddableTypes() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
default SelectableMapping getAggregateMapping() {
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
package org.hibernate.metamodel.mapping.internal;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
|
@ -47,6 +49,7 @@ import org.hibernate.metamodel.mapping.SelectableMapping;
|
|||
import org.hibernate.metamodel.mapping.SelectableMappings;
|
||||
import org.hibernate.metamodel.mapping.SelectablePath;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
|
||||
|
@ -68,7 +71,8 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
/**
|
||||
* Base support for EmbeddableMappingType implementations
|
||||
*/
|
||||
public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType {
|
||||
public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType,
|
||||
EmbeddableMappingType.ConcreteEmbeddableType {
|
||||
final protected MutableAttributeMappingList attributeMappings;
|
||||
protected SelectableMappings selectableMappings;
|
||||
|
||||
|
@ -76,6 +80,31 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType
|
|||
this.attributeMappings = attributeMappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableInstantiator getInstantiator() {
|
||||
return getRepresentationStrategy().getInstantiator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubclassId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean declaresAttribute(AttributeMapping attributeMapping) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean declaresAttribute(int attributeIndex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDiscriminatorValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<?> getMappedJavaType() {
|
||||
return getRepresentationStrategy().getMappedJavaType();
|
||||
|
|
|
@ -47,6 +47,7 @@ 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.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
@ -572,6 +573,13 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
|
|||
public DomainResultAssembler<Object[]> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<Object[]> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return new AssemblerImpl(
|
||||
fetches,
|
||||
navigablePath,
|
||||
|
@ -591,7 +599,7 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
|
|||
// FetchParent
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public Initializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
throw new UnsupportedOperationException( "Compound natural id mappings should not use an initializer" );
|
||||
}
|
||||
|
||||
|
@ -647,7 +655,7 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
|
|||
this.subAssemblers = new DomainResultAssembler[fetches.size()];
|
||||
int i = 0;
|
||||
for ( Fetch fetch : fetches ) {
|
||||
subAssemblers[i++] = fetch.createAssembler( null, creationState );
|
||||
subAssemblers[i++] = fetch.createAssembler( (InitializerParent) null, creationState );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -669,6 +677,18 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> void forEachResultAssembler(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
for ( DomainResultAssembler<?> subAssembler : subAssemblers ) {
|
||||
final Initializer initializer = subAssembler.getInitializer();
|
||||
// In case of natural id mapping selection every initializer is a "result initializer",
|
||||
// regardless of what Initializer#isResultInitializer reports
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Object[]> getAssembledJavaType() {
|
||||
return jtd;
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
package org.hibernate.metamodel.mapping.internal;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
|
@ -52,6 +54,7 @@ import org.hibernate.metamodel.mapping.SelectableMapping;
|
|||
import org.hibernate.metamodel.mapping.SelectableMappings;
|
||||
import org.hibernate.metamodel.mapping.SelectablePath;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -171,7 +174,8 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
|
||||
private final EmbeddableValuedModelPart valueMapping;
|
||||
private final EmbeddableDiscriminatorMapping discriminatorMapping;
|
||||
private final Map<String, Set<AttributeMapping>> declaredAttributesBySubclass;
|
||||
private final Map<String, ConcreteEmbeddableTypeImpl> concreteEmbeddableBySubclass;
|
||||
private final Map<Object, ConcreteEmbeddableTypeImpl> concreteEmbeddableByDiscriminator;
|
||||
|
||||
private final boolean createEmptyCompositesEnabled;
|
||||
private final SelectableMapping aggregateMapping;
|
||||
|
@ -193,7 +197,28 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
this.embeddableJtd = representationStrategy.getMappedJavaType();
|
||||
this.valueMapping = embeddedPartBuilder.apply( this );
|
||||
this.discriminatorMapping = generateDiscriminatorMapping( bootDescriptor, creationContext );
|
||||
this.declaredAttributesBySubclass = bootDescriptor.isPolymorphic() ? new HashMap<>() : null;
|
||||
if ( bootDescriptor.isPolymorphic() ) {
|
||||
this.concreteEmbeddableByDiscriminator = new HashMap<>();
|
||||
this.concreteEmbeddableBySubclass = new HashMap<>();
|
||||
|
||||
int subclassId = 0;
|
||||
// Sort the entries by embeddable class name to have a somewhat stable subclass id
|
||||
final Set<Map.Entry<Object, String>> entries = new TreeSet<>( Map.Entry.comparingByValue() );
|
||||
entries.addAll( bootDescriptor.getDiscriminatorValues().entrySet() );
|
||||
for ( final Map.Entry<Object, String> discriminatorEntry : entries ) {
|
||||
final ConcreteEmbeddableTypeImpl concreteEmbeddableType = new ConcreteEmbeddableTypeImpl(
|
||||
representationStrategy.getInstantiatorForDiscriminator( discriminatorEntry.getKey() ),
|
||||
discriminatorEntry.getKey(),
|
||||
subclassId++
|
||||
);
|
||||
concreteEmbeddableByDiscriminator.put( discriminatorEntry.getKey(), concreteEmbeddableType );
|
||||
concreteEmbeddableBySubclass.put( discriminatorEntry.getValue(), concreteEmbeddableType );
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.concreteEmbeddableByDiscriminator = null;
|
||||
this.concreteEmbeddableBySubclass = null;
|
||||
}
|
||||
|
||||
this.createEmptyCompositesEnabled = ConfigurationHelper.getBoolean(
|
||||
Environment.CREATE_EMPTY_COMPOSITES_ENABLED,
|
||||
|
@ -337,7 +362,8 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
this.representationStrategy = inverseMappingType.getRepresentationStrategy();
|
||||
this.valueMapping = valueMapping;
|
||||
this.discriminatorMapping = null;
|
||||
this.declaredAttributesBySubclass = null;
|
||||
this.concreteEmbeddableBySubclass = null;
|
||||
this.concreteEmbeddableByDiscriminator = null;
|
||||
this.createEmptyCompositesEnabled = inverseMappingType.isCreateEmptyCompositesEnabled();
|
||||
this.aggregateMapping = null;
|
||||
this.aggregateMappingRequiresColumnWriter = false;
|
||||
|
@ -634,12 +660,9 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
|
||||
if ( isPolymorphic() ) {
|
||||
final String declaringClass = bootDescriptor.getPropertyDeclaringClass( bootPropertyDescriptor );
|
||||
for ( final String subclass : bootDescriptor.getDiscriminatorValues().values() ) {
|
||||
if ( isDefinedInClassOrSuperclass( bootDescriptor, declaringClass, subclass ) ) {
|
||||
declaredAttributesBySubclass.computeIfAbsent(
|
||||
subclass,
|
||||
k -> new HashSet<>()
|
||||
).add( attributeMapping );
|
||||
for ( Map.Entry<String, ConcreteEmbeddableTypeImpl> entry : concreteEmbeddableBySubclass.entrySet() ) {
|
||||
if ( isDefinedInClassOrSuperclass( bootDescriptor, declaringClass, entry.getKey() ) ) {
|
||||
entry.getValue().declaredAttributes.set( attributeMapping.getStateArrayPosition() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -819,19 +842,65 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean declaresAttribute(String embeddableClassName, AttributeMapping attributeMapping) {
|
||||
if ( declaredAttributesBySubclass == null ) {
|
||||
return true;
|
||||
private static final class ConcreteEmbeddableTypeImpl implements ConcreteEmbeddableType {
|
||||
private final EmbeddableInstantiator instantiator;
|
||||
private final Object discriminatorValue;
|
||||
private final int subclassId;
|
||||
private final BitSet declaredAttributes;
|
||||
|
||||
public ConcreteEmbeddableTypeImpl(EmbeddableInstantiator instantiator, Object discriminatorValue, int subclassId) {
|
||||
this.instantiator = instantiator;
|
||||
this.discriminatorValue = discriminatorValue;
|
||||
this.subclassId = subclassId;
|
||||
this.declaredAttributes = new BitSet();
|
||||
}
|
||||
final Set<AttributeMapping> declaredAttributes = declaredAttributesBySubclass.get( embeddableClassName );
|
||||
return declaredAttributes != null && declaredAttributes.contains( attributeMapping );
|
||||
|
||||
@Override
|
||||
public EmbeddableInstantiator getInstantiator() {
|
||||
return instantiator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDiscriminatorValue() {
|
||||
return discriminatorValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSubclassId() {
|
||||
return subclassId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean declaresAttribute(AttributeMapping attributeMapping) {
|
||||
return declaredAttributes.get( attributeMapping.getStateArrayPosition() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean declaresAttribute(int attributeIndex) {
|
||||
return declaredAttributes.get( attributeIndex );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConcreteEmbeddableType findSubtypeByDiscriminator(Object discriminatorValue) {
|
||||
return concreteEmbeddableByDiscriminator == null ? this : concreteEmbeddableByDiscriminator.get( discriminatorValue );
|
||||
}
|
||||
|
||||
public ConcreteEmbeddableType findSubtypeBySubclass(String subclassName) {
|
||||
return concreteEmbeddableBySubclass == null ? this : concreteEmbeddableBySubclass.get( subclassName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConcreteEmbeddableType> getConcreteEmbeddableTypes() {
|
||||
//noinspection unchecked
|
||||
return (Collection<ConcreteEmbeddableType>) (Collection<?>) concreteEmbeddableBySubclass.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(Object instance, int position) {
|
||||
final AttributeMapping attributeMapping = getAttributeMapping( position );
|
||||
if ( declaresAttribute( instance.getClass().getName(), attributeMapping ) ) {
|
||||
final ConcreteEmbeddableType concreteEmbeddableType = findSubtypeBySubclass( instance.getClass().getName() );
|
||||
if ( concreteEmbeddableType.declaresAttribute( attributeMapping ) ) {
|
||||
return attributeMapping.getValue( instance );
|
||||
}
|
||||
return null;
|
||||
|
@ -845,11 +914,11 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
else {
|
||||
final int numberOfAttributes = getNumberOfAttributeMappings();
|
||||
final Object[] results = new Object[numberOfAttributes + 1];
|
||||
final String compositeClassName = compositeInstance.getClass().getName();
|
||||
final ConcreteEmbeddableType concreteEmbeddableType = findSubtypeBySubclass( compositeInstance.getClass().getName() );
|
||||
int i = 0;
|
||||
for ( ; i < numberOfAttributes; i++ ) {
|
||||
final AttributeMapping attributeMapping = getAttributeMapping( i );
|
||||
if ( declaresAttribute( compositeClassName, attributeMapping ) ) {
|
||||
if ( concreteEmbeddableType.declaresAttribute( attributeMapping ) ) {
|
||||
final Getter getter = attributeMapping.getAttributeMetadata()
|
||||
.getPropertyAccess()
|
||||
.getGetter();
|
||||
|
@ -871,9 +940,10 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
}
|
||||
else {
|
||||
final String compositeClassName = component.getClass().getName();
|
||||
final ConcreteEmbeddableType concreteEmbeddableType = findSubtypeBySubclass( compositeClassName );
|
||||
for ( int i = 0; i < getNumberOfAttributeMappings(); i++ ) {
|
||||
final AttributeMapping attributeMapping = getAttributeMapping( i );
|
||||
if ( declaresAttribute( compositeClassName, attributeMapping ) ) {
|
||||
if ( concreteEmbeddableType.declaresAttribute( attributeMapping ) ) {
|
||||
attributeMapping.getPropertyAccess().getSetter().set( component, values[i] );
|
||||
}
|
||||
else if ( values[i] != null ) {
|
||||
|
@ -928,11 +998,13 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
}
|
||||
}
|
||||
else {
|
||||
final String compositeClassName = domainValue == null ? null : domainValue.getClass().getName();
|
||||
final ConcreteEmbeddableType concreteEmbeddableType = domainValue == null
|
||||
? null
|
||||
: findSubtypeBySubclass( domainValue.getClass().getName() );
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
final AttributeMapping attributeMapping = attributeMappings.get( i );
|
||||
if ( !attributeMapping.isPluralAttributeMapping() ) {
|
||||
final Object attributeValue = domainValue == null || !declaresAttribute( compositeClassName, attributeMapping )
|
||||
final Object attributeValue = concreteEmbeddableType == null || !concreteEmbeddableType.declaresAttribute( attributeMapping )
|
||||
? null
|
||||
: attributeMapping.getPropertyAccess().getGetter().get( domainValue );
|
||||
span += attributeMapping.breakDownJdbcValues(
|
||||
|
@ -946,7 +1018,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
}
|
||||
}
|
||||
if ( isPolymorphic() ) {
|
||||
final Object d = domainValue == null ? null : discriminatorMapping.getDiscriminatorValue( compositeClassName );
|
||||
final Object d = concreteEmbeddableType == null ? null : concreteEmbeddableType.getDiscriminatorValue();
|
||||
span += discriminatorMapping.breakDownJdbcValues( d, offset + span, x, y, valueConsumer, session );
|
||||
}
|
||||
}
|
||||
|
@ -981,18 +1053,20 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
}
|
||||
}
|
||||
else {
|
||||
final String compositeClassName = domainValue == null ? null : domainValue.getClass().getName();
|
||||
final ConcreteEmbeddableType concreteEmbeddableType = domainValue == null
|
||||
? null
|
||||
: findSubtypeBySubclass( domainValue.getClass().getName() );
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
final AttributeMapping attributeMapping = attributeMappings.get( i );
|
||||
if ( !attributeMapping.isPluralAttributeMapping() ) {
|
||||
final Object attributeValue = domainValue == null || !declaresAttribute( compositeClassName, attributeMapping )
|
||||
final Object attributeValue = concreteEmbeddableType == null || !concreteEmbeddableType.declaresAttribute( attributeMapping )
|
||||
? null
|
||||
: attributeMapping.getPropertyAccess().getGetter().get( domainValue );
|
||||
span += attributeMapping.decompose( attributeValue, offset + span, x, y, valueConsumer, session );
|
||||
}
|
||||
}
|
||||
if ( isPolymorphic() ) {
|
||||
final Object d = domainValue == null ? null : discriminatorMapping.getDiscriminatorValue( compositeClassName );
|
||||
final Object d = concreteEmbeddableType == null ? null : concreteEmbeddableType.getDiscriminatorValue();
|
||||
span += discriminatorMapping.decompose( d, offset + span, x, y, valueConsumer, session );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,6 +95,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.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
|
||||
import org.hibernate.sql.results.graph.entity.EntityFetch;
|
||||
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
|
||||
|
@ -1855,6 +1856,13 @@ public class ToOneAttributeMapping
|
|||
public DomainResultAssembler createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultAssembler;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ import org.hibernate.sql.results.graph.AssemblerCreationState;
|
|||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.jdbc.internal.StandardJdbcValuesMapping;
|
||||
|
||||
|
@ -78,12 +78,12 @@ public class JdbcValuesMappingImpl extends StandardJdbcValuesMapping {
|
|||
@Override
|
||||
public <P extends FetchParent> Initializer resolveInitializer(
|
||||
P resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
InitializerProducer<P> producer) {
|
||||
return creationState.resolveInitializer(
|
||||
resultGraphNode,
|
||||
parentAccess,
|
||||
(node, parent, state) -> producer.createInitializer( node, parent, this )
|
||||
parent,
|
||||
(node, p, state) -> producer.createInitializer( node, p, this )
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.BitSet;
|
|||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
|
@ -21,11 +22,12 @@ import org.hibernate.sql.results.graph.Fetch;
|
|||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.entity.EntityResult;
|
||||
import org.hibernate.sql.results.graph.entity.internal.EntityAssembler;
|
||||
import org.hibernate.sql.results.graph.entity.internal.EntityResultInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.internal.EntityInitializerImpl;
|
||||
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
||||
|
||||
/**
|
||||
|
@ -141,29 +143,60 @@ public class EntityResultImpl implements EntityResult, InitializerProducer<Entit
|
|||
public DomainResultAssembler<?> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return new EntityAssembler( getResultJavaType(), creationState.resolveInitializer( this, parentAccess, this ).asEntityInitializer() );
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return new EntityAssembler( getResultJavaType(), creationState.resolveInitializer( this, parent, this ).asEntityInitializer() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
EntityResultImpl resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return new EntityResultInitializer(
|
||||
return new EntityInitializerImpl(
|
||||
this,
|
||||
getNavigablePath(),
|
||||
lockMode,
|
||||
identifierFetch,
|
||||
discriminatorFetch,
|
||||
null,
|
||||
null,
|
||||
NotFoundAction.EXCEPTION,
|
||||
null,
|
||||
true,
|
||||
creationState
|
||||
);
|
||||
// return new EntityResultInitializer(
|
||||
// this,
|
||||
// getNavigablePath(),
|
||||
// lockMode,
|
||||
// identifierFetch,
|
||||
// discriminatorFetch,
|
||||
// null,
|
||||
// creationState
|
||||
// );
|
||||
// return new EntityInitializerImpl(
|
||||
// this,
|
||||
// lockMode,
|
||||
// identifierFetch,
|
||||
// discriminatorFetch,
|
||||
// null,
|
||||
// null,
|
||||
// NotFoundAction.EXCEPTION,
|
||||
// null,
|
||||
// true,
|
||||
// creationState
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,15 @@ package org.hibernate.query.sqm.sql.internal;
|
|||
|
||||
import java.util.BitSet;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
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.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.instantiation.internal.ArgumentReader;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -47,8 +51,21 @@ public class SqmMapEntryResult<K, V, R extends Map.Entry<K, V>> implements Domai
|
|||
public DomainResultAssembler<R> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
final DomainResultAssembler<K> keyAssembler = keyResult.createResultAssembler( null, creationState );
|
||||
final DomainResultAssembler<V> valueAssembler = valueResult.createResultAssembler( null, creationState );
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<R> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
final DomainResultAssembler<K> keyAssembler = keyResult.createResultAssembler(
|
||||
(InitializerParent) null,
|
||||
creationState
|
||||
);
|
||||
final DomainResultAssembler<V> valueAssembler = valueResult.createResultAssembler(
|
||||
(InitializerParent) null,
|
||||
creationState
|
||||
);
|
||||
|
||||
return new DomainResultAssembler<>() {
|
||||
@Override
|
||||
|
@ -63,6 +80,12 @@ public class SqmMapEntryResult<K, V, R extends Map.Entry<K, V>> implements Domai
|
|||
public JavaType<R> getAssembledJavaType() {
|
||||
return javaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> void forEachResultAssembler(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
keyAssembler.forEachResultAssembler( consumer, arg );
|
||||
valueAssembler.forEachResultAssembler( consumer, arg );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ public class OutputsImpl implements Outputs {
|
|||
jdbcValues
|
||||
);
|
||||
|
||||
rowReader.getInitializersList().startLoading( rowProcessingState );
|
||||
rowReader.startLoading( rowProcessingState );
|
||||
|
||||
while ( rowProcessingState.next() ) {
|
||||
results.add( rowReader.readRow( rowProcessingState, processingOptions ) );
|
||||
|
|
|
@ -206,7 +206,7 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
|
|||
jdbcValues
|
||||
);
|
||||
|
||||
rowReader.getInitializersList().startLoading( rowProcessingState );
|
||||
rowReader.startLoading( rowProcessingState );
|
||||
|
||||
final T result = resultsConsumer.consume(
|
||||
jdbcValues,
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Base support for FetchParentAccess implementations. Mainly adds support for
|
||||
* registering and managing resolution listeners
|
||||
*/
|
||||
public abstract class AbstractFetchParentAccess implements FetchParentAccess {
|
||||
private List<Consumer<Object>> listeners;
|
||||
private boolean parentShallowCached;
|
||||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> listener) {
|
||||
if ( listeners == null ) {
|
||||
listeners = new ArrayList<>();
|
||||
}
|
||||
|
||||
listeners.add( listener );
|
||||
}
|
||||
|
||||
protected void clearResolutionListeners() {
|
||||
if ( listeners != null ) {
|
||||
listeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyResolutionListeners(Object resolvedInstance) {
|
||||
if ( listeners == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ( Consumer<Object> listener : listeners ) {
|
||||
listener.accept( resolvedInstance );
|
||||
}
|
||||
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
protected boolean isParentShallowCached() {
|
||||
return parentShallowCached;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markShallowCached() {
|
||||
parentShallowCached = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
parentShallowCached = false;
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ public interface AssemblerCreationState {
|
|||
|
||||
<P extends FetchParent> Initializer resolveInitializer(
|
||||
P resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
InitializerProducer<P> producer);
|
||||
|
||||
SqlAstCreationContext getSqlAstCreationContext();
|
||||
|
|
|
@ -30,8 +30,19 @@ public interface DomainResult<J> extends DomainResultGraphNode {
|
|||
|
||||
/**
|
||||
* Create an assembler (and any initializers) for this result.
|
||||
* @deprecated Use {@link #createResultAssembler(InitializerParent, AssemblerCreationState)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
DomainResultAssembler<J> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState);
|
||||
|
||||
/**
|
||||
* Create an assembler (and any initializers) for this result.
|
||||
*/
|
||||
default DomainResultAssembler<J> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (FetchParentAccess) parent, creationState );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
@ -52,4 +54,12 @@ public interface DomainResultAssembler<J> {
|
|||
default @Nullable Initializer getInitializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the consumer with every initializer part of this assembler that returns {@code true} for
|
||||
* {@link Initializer#isResultInitializer()}.
|
||||
*/
|
||||
default <X> void forEachResultAssembler(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,17 @@ public interface Fetch extends DomainResultGraphNode {
|
|||
|
||||
/**
|
||||
* Create the assembler for this fetch
|
||||
* @deprecated Use {@link #createAssembler(InitializerParent, AssemblerCreationState)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
DomainResultAssembler<?> createAssembler(FetchParentAccess parentAccess, AssemblerCreationState creationState);
|
||||
|
||||
/**
|
||||
* Create the assembler for this fetch
|
||||
*/
|
||||
default DomainResultAssembler<?> createAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return createAssembler( (FetchParentAccess) parent, creationState );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ public interface FetchParent extends DomainResultGraphNode {
|
|||
}
|
||||
}
|
||||
|
||||
Initializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState);
|
||||
Initializer createInitializer(InitializerParent parent, AssemblerCreationState creationState);
|
||||
|
||||
default FetchParent getRoot() {
|
||||
if ( this instanceof Fetch ) {
|
||||
|
|
|
@ -27,22 +27,31 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface FetchParentAccess extends Initializer {
|
||||
public interface FetchParentAccess extends InitializerParent {
|
||||
/**
|
||||
* Find the first entity access up the fetch parent graph
|
||||
* @deprecated use {@link #findOwningEntityInitializer()} instead
|
||||
*/
|
||||
@Nullable FetchParentAccess findFirstEntityDescriptorAccess();
|
||||
|
||||
default @Nullable EntityInitializer findFirstEntityInitializer() {
|
||||
final EntityInitializer entityInitializer = this.asEntityInitializer();
|
||||
if ( entityInitializer != null ) {
|
||||
return entityInitializer;
|
||||
}
|
||||
final FetchParentAccess entityDescriptorAccess = findFirstEntityDescriptorAccess();
|
||||
return entityDescriptorAccess == null ? null : entityDescriptorAccess.asEntityInitializer();
|
||||
@Deprecated(forRemoval = true)
|
||||
default @Nullable EntityInitializer findFirstEntityDescriptorAccess() {
|
||||
return findOwningEntityInitializer();
|
||||
}
|
||||
|
||||
@Nullable Object getParentKey();
|
||||
default @Nullable EntityInitializer findFirstEntityInitializer() {
|
||||
// Keep this method around for binary backwards compatibility
|
||||
return InitializerParent.super.findFirstEntityInitializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link EntityInitializer#getEntityIdentifier()} on {@link #findFirstEntityInitializer()} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default @Nullable Object getParentKey() {
|
||||
EntityInitializer entityInitializer = asEntityInitializer();
|
||||
return entityInitializer == null || ( entityInitializer = findOwningEntityInitializer() ) == null
|
||||
? null
|
||||
: entityInitializer.getEntityIdentifier();
|
||||
}
|
||||
|
||||
NavigablePath getNavigablePath();
|
||||
|
||||
|
@ -50,113 +59,31 @@ public interface FetchParentAccess extends Initializer {
|
|||
* Register a listener to be notified when the parent is "resolved"
|
||||
*
|
||||
* @apiNote If already resolved, the callback is triggered immediately
|
||||
* @deprecated Not used anymore
|
||||
*/
|
||||
void registerResolutionListener(Consumer<Object> resolvedParentConsumer);
|
||||
@Deprecated(forRemoval = true)
|
||||
default void registerResolutionListener(Consumer<Object> resolvedParentConsumer) {
|
||||
throw new UnsupportedOperationException( "Don't use this method. It will be removed." );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getParent()} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default @Nullable FetchParentAccess getFetchParentAccess() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable FetchParentAccess getOwningParent();
|
||||
|
||||
static @Nullable FetchParentAccess determineOwningParent(@Nullable FetchParentAccess parentAccess) {
|
||||
if ( parentAccess == null
|
||||
|| parentAccess.isEntityInitializer()
|
||||
|| parentAccess.isCollectionInitializer()
|
||||
|| parentAccess.isEmbeddableInitializer() ) {
|
||||
return parentAccess;
|
||||
}
|
||||
return parentAccess.getOwningParent();
|
||||
}
|
||||
|
||||
@Nullable EntityMappingType getOwnedModelPartDeclaringType();
|
||||
|
||||
static @Nullable EntityMappingType determineOwnedModelPartDeclaringType(
|
||||
ModelPart modelPart,
|
||||
@Nullable FetchParentAccess parentAccess,
|
||||
@Nullable FetchParentAccess owningParent) {
|
||||
final EntityInitializer entityInitializer = owningParent != null ?
|
||||
owningParent.findFirstEntityInitializer() :
|
||||
null;
|
||||
if ( entityInitializer == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while ( parentAccess != null && parentAccess != owningParent ) {
|
||||
modelPart = parentAccess.getInitializedPart();
|
||||
parentAccess = parentAccess.getFetchParentAccess();
|
||||
}
|
||||
if ( modelPart != null && entityInitializer.getEntityDescriptor().getEntityMetamodel().isPolymorphic() ) {
|
||||
return modelPart.asAttributeMapping() != null ?
|
||||
modelPart.asAttributeMapping().getDeclaringType().findContainingEntityMapping() :
|
||||
modelPart.asEntityMappingType();
|
||||
}
|
||||
return null;
|
||||
@Override
|
||||
default @Nullable InitializerParent getParent() {
|
||||
return getFetchParentAccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Not needed anymore.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default boolean shouldSkipInitializer(RowProcessingState rowProcessingState) {
|
||||
if ( isPartOfKey() ) {
|
||||
// We can never skip an initializer if it is part of a key
|
||||
return false;
|
||||
}
|
||||
|
||||
FetchParentAccess owningParent = getOwningParent();
|
||||
if ( owningParent != null ) {
|
||||
if ( owningParent instanceof AbstractImmediateCollectionInitializer ) {
|
||||
final AbstractImmediateCollectionInitializer collectionInitializer = (AbstractImmediateCollectionInitializer) owningParent;
|
||||
// If this initializer is owned by an immediate collection initializer,
|
||||
// skipping only depends on whether the collection key is resolvable or not
|
||||
return collectionInitializer.resolveCollectionKey( rowProcessingState ) == null;
|
||||
}
|
||||
EntityInitializer entityInitializer = owningParent.asEntityInitializer();
|
||||
if ( entityInitializer == null ) {
|
||||
final EmbeddableInitializer embeddableInitializer = owningParent.asEmbeddableInitializer();
|
||||
assert embeddableInitializer != null;
|
||||
final EmbeddableMappingType descriptor = embeddableInitializer.getInitializedPart()
|
||||
.getEmbeddableTypeDescriptor();
|
||||
if ( descriptor.isPolymorphic() ) {
|
||||
// The embeddable is polymorphic, check if the current subtype defines the initialized attribute
|
||||
final AttributeMapping attribute = getInitializedPart().asAttributeMapping();
|
||||
if ( attribute != null ) {
|
||||
embeddableInitializer.resolveKey( rowProcessingState );
|
||||
final String embeddableClassName = descriptor.getDiscriminatorMapping()
|
||||
.resolveDiscriminatorValue( embeddableInitializer.getDiscriminatorValue() )
|
||||
.getIndicatedEntityName();
|
||||
if ( !descriptor.declaresAttribute( embeddableClassName, attribute ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( embeddableInitializer.isResultInitializer() ) {
|
||||
// We can never skip an initializer if it is part of an embeddable domain result,
|
||||
// because that embeddable always has to be materialized with its full state
|
||||
return false;
|
||||
}
|
||||
else if ( ( entityInitializer = embeddableInitializer.findFirstEntityInitializer() ) == null ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// We must resolve the key of the parent in order to determine the concrete descriptor
|
||||
entityInitializer.resolveKey( rowProcessingState );
|
||||
final EntityPersister concreteDescriptor = entityInitializer.getConcreteDescriptor();
|
||||
if ( concreteDescriptor == null ) {
|
||||
// Skip processing this initializer if the parent owning initializer is missing
|
||||
return true;
|
||||
}
|
||||
// We can skip if the parent is either null or already initialized,
|
||||
if ( ( entityInitializer.getEntityKey() == null || entityInitializer.isEntityInitialized() )
|
||||
// but only if the query cache put does not depend on the initializer accessing JdbcValues.
|
||||
// If result caching is disabled, there are no dependencies
|
||||
&& rowProcessingState.getQueryOptions().isResultCachingEnabled() != Boolean.TRUE ) {
|
||||
return true;
|
||||
}
|
||||
final EntityMappingType declaringType = getOwnedModelPartDeclaringType();
|
||||
if ( declaringType != null && concreteDescriptor != declaringType ) {
|
||||
// Skip the initializer if the declaring type is not a super type
|
||||
// of the parent entity initializer's concrete type
|
||||
return !declaringType.getSubclassEntityNames().contains( concreteDescriptor.getEntityName() );
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,35 +28,110 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
*/
|
||||
@Incubating
|
||||
public interface Initializer {
|
||||
|
||||
Initializer[] EMPTY_ARRAY = new Initializer[0];
|
||||
|
||||
/**
|
||||
* Returns the parent {@link Initializer} or {@code null} if this is a result initializer.
|
||||
*/
|
||||
default @Nullable InitializerParent getParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first entity access up the fetch parent graph
|
||||
* @deprecated use {@link #findOwningEntityInitializer()} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default @Nullable EntityInitializer findFirstEntityDescriptorAccess() {
|
||||
return findOwningEntityInitializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the entity initializer that owns this initializer
|
||||
* by traversing up {@link #getParent()}.
|
||||
*/
|
||||
default @Nullable EntityInitializer findOwningEntityInitializer() {
|
||||
return Initializer.findOwningEntityInitializer( getParent() );
|
||||
}
|
||||
/**
|
||||
* Find the entity initializer that owns this initializer
|
||||
* by traversing up {@link #getParent()}.
|
||||
*/
|
||||
static @Nullable EntityInitializer findOwningEntityInitializer(@Nullable Initializer parent) {
|
||||
if ( parent == null || parent.isCollectionInitializer() ) {
|
||||
return null;
|
||||
}
|
||||
final EntityInitializer entityInitializer = parent.asEntityInitializer();
|
||||
if ( entityInitializer != null ) {
|
||||
return entityInitializer;
|
||||
}
|
||||
return findOwningEntityInitializer( parent.getParent() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first {@link EntityInitializer},
|
||||
* returning {@code this} if {@link #isEntityInitializer()} returns {@code true}.
|
||||
* @deprecated Use {@link #findOwningEntityInitializer()} instead, optionally in combination with
|
||||
* {@link #asEntityInitializer()} if the type of the {@code this} {@link Initializer} is unknown.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default @Nullable EntityInitializer findFirstEntityInitializer() {
|
||||
final EntityInitializer entityInitializer = this.asEntityInitializer();
|
||||
if ( entityInitializer != null ) {
|
||||
return entityInitializer;
|
||||
}
|
||||
return findOwningEntityInitializer();
|
||||
}
|
||||
|
||||
NavigablePath getNavigablePath();
|
||||
|
||||
ModelPart getInitializedPart();
|
||||
|
||||
Object getInitializedInstance();
|
||||
|
||||
default void startLoading(RowProcessingState rowProcessingState) {
|
||||
}
|
||||
/**
|
||||
* The current state of this initializer.
|
||||
*/
|
||||
State getState();
|
||||
|
||||
default void markShallowCached() {
|
||||
}
|
||||
/**
|
||||
* Step 0 - Callback for initializers before the first row is read.
|
||||
* It is the responsibility of this initializer to recurse to the sub-initializers.
|
||||
*
|
||||
* This is useful for e.g. preparing initializers in case of a cache hit.
|
||||
*/
|
||||
void startLoading(RowProcessingState rowProcessingState);
|
||||
// by default - nothing to do
|
||||
|
||||
/**
|
||||
* Step 1 - Resolve the key value for this initializer for the current
|
||||
* row.
|
||||
* row and then recurse to the sub-initializers.
|
||||
*
|
||||
* After this point, the initializer knows the entity/collection/component
|
||||
* key for the current row
|
||||
* After this point, the initializer knows whether further processing is necessary
|
||||
* for the current row i.e. if the object is missing.
|
||||
*/
|
||||
void resolveKey(RowProcessingState rowProcessingState);
|
||||
void resolveKey();
|
||||
|
||||
/**
|
||||
* Step 2 - Using the key resolved in {@link #resolveKey}, resolve the
|
||||
* Step 2.1 - Using the key resolved in {@link #resolveKey}, resolve the
|
||||
* instance (of the thing initialized) to use for the current row.
|
||||
*
|
||||
* After this point, the initializer knows the entity/collection/component
|
||||
* instance for the current row based on the resolved key
|
||||
* instance for the current row based on the resolved key.
|
||||
* If the resolving was successful, {@link #getInitializedInstance()} will return that instance.
|
||||
*/
|
||||
void resolveInstance(RowProcessingState rowProcessingState);
|
||||
void resolveInstance();
|
||||
|
||||
/**
|
||||
* Step 2.2 - Use the given instance as resolved instance for this initializer.
|
||||
* Initializers are supposed to recursively call this method for sub-initializers.
|
||||
*
|
||||
* This alternative initialization protocol is used when a parent instance was already part of the persistence context.
|
||||
*/
|
||||
default void resolveInstance(@Nullable Object instance) {
|
||||
resolveKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 3 - Initialize the state of the instance resolved in
|
||||
|
@ -65,7 +140,7 @@ public interface Initializer {
|
|||
* All resolved state for the current row is injected into the resolved
|
||||
* instance
|
||||
*/
|
||||
void initializeInstance(RowProcessingState rowProcessingState);
|
||||
void initializeInstance();
|
||||
|
||||
/**
|
||||
* Step 3.1 - Initialize the state of the instance as extracted from the given parentInstance.
|
||||
|
@ -76,7 +151,7 @@ public interface Initializer {
|
|||
* in which case there is no data available in the {@link org.hibernate.sql.results.jdbc.internal.JdbcValuesCacheHit}
|
||||
* to initialize potentially lazy associations.
|
||||
*/
|
||||
default void initializeInstanceFromParent(Object parentInstance, RowProcessingState rowProcessingState) {
|
||||
default void initializeInstanceFromParent(Object parentInstance) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,7 +159,7 @@ public interface Initializer {
|
|||
* Provides ability to complete processing from the current row and
|
||||
* prepare for the next row.
|
||||
*/
|
||||
void finishUpRow(RowProcessingState rowProcessingState);
|
||||
void finishUpRow();
|
||||
|
||||
/**
|
||||
* Lifecycle method called at the very end of the result values processing
|
||||
|
@ -93,15 +168,29 @@ public interface Initializer {
|
|||
// by default - nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this initializer is part of a key i.e. entity identifier, foreign key or collection key.
|
||||
*/
|
||||
boolean isPartOfKey();
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #isPartOfKey(NavigablePath, InitializerParent)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
static boolean isPartOfKey(NavigablePath navigablePath, FetchParentAccess parentAccess) {
|
||||
return parentAccess != null && parentAccess.isEmbeddableInitializer() && parentAccess.isPartOfKey()
|
||||
return isPartOfKey( navigablePath, (InitializerParent) parentAccess );
|
||||
}
|
||||
|
||||
static boolean isPartOfKey(NavigablePath navigablePath, InitializerParent parent) {
|
||||
return parent != null && parent.isEmbeddableInitializer() && parent.isPartOfKey()
|
||||
|| navigablePath instanceof EntityIdentifierNavigablePath
|
||||
|| ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() )
|
||||
|| ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getLocalName() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this is a result or fetch initializer.
|
||||
*/
|
||||
boolean isResultInitializer();
|
||||
|
||||
default boolean isEmbeddableInitializer() {
|
||||
|
@ -143,4 +232,11 @@ public interface Initializer {
|
|||
return null;
|
||||
}
|
||||
|
||||
enum State {
|
||||
UNINITIALIZED,
|
||||
MISSING,
|
||||
KEY_RESOLVED,
|
||||
RESOLVED,
|
||||
INITIALIZED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Provides access to information about the owner/parent of a fetch
|
||||
* in relation to the current "row" being processed.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface InitializerParent extends Initializer {
|
||||
|
||||
}
|
|
@ -9,12 +9,12 @@ package org.hibernate.sql.results.graph;
|
|||
/**
|
||||
* Producer for {@link Initializer} based on a {@link FetchParent}.
|
||||
*
|
||||
* @see AssemblerCreationState#resolveInitializer(FetchParent, FetchParentAccess, InitializerProducer)
|
||||
* @see AssemblerCreationState#resolveInitializer(FetchParent, InitializerParent, InitializerProducer)
|
||||
* @since 6.5
|
||||
*/
|
||||
public interface InitializerProducer<P extends FetchParent> {
|
||||
Initializer createInitializer(
|
||||
P resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.BitSet;
|
|||
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
|
@ -190,6 +191,13 @@ public class BasicFetch<T> implements Fetch, BasicResultGraphNode<T> {
|
|||
public DomainResultAssembler createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<T> createAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return assembler;
|
||||
}
|
||||
|
||||
|
@ -197,6 +205,13 @@ public class BasicFetch<T> implements Fetch, BasicResultGraphNode<T> {
|
|||
public DomainResultAssembler<T> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<T> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return assembler;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.BitSet;
|
|||
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
|
@ -152,6 +153,13 @@ public class BasicResult<T> implements DomainResult<T>, BasicResultGraphNode<T>
|
|||
public DomainResultAssembler<T> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<T> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return assembler;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ public interface CollectionInitializer extends FetchParentAccess {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
@Nullable CollectionKey resolveCollectionKey(RowProcessingState rowProcessingState);
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,28 +6,27 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.collection.internal;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.collection.spi.CollectionSemantics;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.CollectionKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.log.LoggingHelper;
|
||||
import org.hibernate.metamodel.CollectionClassification;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
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.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionLoadingLogger;
|
||||
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.graph.internal.AbstractInitializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -37,13 +36,12 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractCollectionInitializer implements CollectionInitializer {
|
||||
private final NavigablePath collectionPath;
|
||||
private final FetchParentAccess owningParent;
|
||||
private final EntityMappingType ownedModelPartDeclaringType;
|
||||
public abstract class AbstractCollectionInitializer extends AbstractInitializer implements CollectionInitializer {
|
||||
protected final NavigablePath collectionPath;
|
||||
protected final PluralAttributeMapping collectionAttributeMapping;
|
||||
protected final boolean isResultInitializer;
|
||||
protected final @Nullable FetchParentAccess parentAccess;
|
||||
protected final @Nullable InitializerParent parent;
|
||||
protected final @Nullable EntityInitializer owningEntityInitializer;
|
||||
|
||||
/**
|
||||
* refers to the collection's container value - which collection-key?
|
||||
|
@ -51,87 +49,127 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
|
|||
protected final @Nullable DomainResultAssembler<?> collectionKeyResultAssembler;
|
||||
|
||||
protected @Nullable PersistentCollection<?> collectionInstance;
|
||||
protected @Nullable Object collectionKeyValue;
|
||||
protected @Nullable CollectionKey collectionKey;
|
||||
|
||||
protected boolean parentShallowCached;
|
||||
|
||||
// per-row state
|
||||
protected State state = State.UNINITIALIZED;
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractCollectionInitializer(NavigablePath, PluralAttributeMapping, InitializerParent, DomainResult, boolean, AssemblerCreationState)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
protected AbstractCollectionInitializer(
|
||||
NavigablePath collectionPath,
|
||||
PluralAttributeMapping collectionAttributeMapping,
|
||||
FetchParentAccess parent,
|
||||
@Nullable DomainResult<?> collectionKeyResult,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
this(
|
||||
collectionPath,
|
||||
collectionAttributeMapping,
|
||||
(InitializerParent) parent,
|
||||
collectionKeyResult,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
protected AbstractCollectionInitializer(
|
||||
NavigablePath collectionPath,
|
||||
PluralAttributeMapping collectionAttributeMapping,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
@Nullable DomainResult<?> collectionKeyResult,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
this.collectionPath = collectionPath;
|
||||
this.owningParent = FetchParentAccess.determineOwningParent( parentAccess );
|
||||
this.ownedModelPartDeclaringType = FetchParentAccess.determineOwnedModelPartDeclaringType( collectionAttributeMapping, parentAccess, owningParent );
|
||||
this.collectionAttributeMapping = collectionAttributeMapping;
|
||||
this.isResultInitializer = isResultInitializer;
|
||||
this.parentAccess = parentAccess;
|
||||
this.parent = parent;
|
||||
this.owningEntityInitializer = Initializer.findOwningEntityInitializer( parent );
|
||||
this.collectionKeyResultAssembler = collectionKeyResult == null
|
||||
? null
|
||||
: collectionKeyResult.createResultAssembler( this, creationState );
|
||||
: collectionKeyResult.createResultAssembler( (InitializerParent) this, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
public void resolveKey() {
|
||||
if ( state != State.UNINITIALIZED ) {
|
||||
// already resolved
|
||||
return;
|
||||
}
|
||||
|
||||
final CollectionKey oldKey = collectionKey;
|
||||
final PersistentCollection<?> oldCollectionInstance = collectionInstance;
|
||||
collectionKey = null;
|
||||
collectionInstance = null;
|
||||
state = State.MISSING;
|
||||
if ( parentShallowCached || shouldSkipInitializer( rowProcessingState ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A null collection key result assembler means that we can use the parent key
|
||||
final Object collectionKeyValue;
|
||||
if ( collectionKeyResultAssembler == null ) {
|
||||
assert parentAccess != null;
|
||||
collectionKeyValue = parentAccess.getParentKey();
|
||||
}
|
||||
else {
|
||||
state = State.KEY_RESOLVED;
|
||||
collectionKeyValue = null;
|
||||
if ( collectionKeyResultAssembler != null ) {
|
||||
final Initializer initializer = collectionKeyResultAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializer.resolveKey();
|
||||
if ( initializer.getState() == State.MISSING ) {
|
||||
setMissing();
|
||||
}
|
||||
return;
|
||||
}
|
||||
collectionKeyValue = collectionKeyResultAssembler.assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
if ( collectionKeyValue != null ) {
|
||||
final CollectionPersister persister = collectionAttributeMapping.getCollectionDescriptor();
|
||||
// Try to reuse the previous collection key and collection if possible
|
||||
if ( oldKey != null && persister.getKeyType().isEqual( oldKey.getKey(), collectionKeyValue ) ) {
|
||||
collectionKey = oldKey;
|
||||
collectionInstance = oldCollectionInstance;
|
||||
state = oldCollectionInstance == null ? State.MISSING : State.RESOLVED;
|
||||
}
|
||||
else {
|
||||
collectionKey = new CollectionKey( persister, collectionKeyValue );
|
||||
state = State.KEY_RESOLVED;
|
||||
}
|
||||
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
CollectionLoadingLogger.COLL_LOAD_LOGGER.debugf(
|
||||
"(%s) Current row collection key : %s",
|
||||
this.getClass().getSimpleName(),
|
||||
LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() )
|
||||
);
|
||||
if ( collectionKeyValue == null ) {
|
||||
setMissing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setMissing() {
|
||||
state = State.MISSING;
|
||||
collectionKey = null;
|
||||
collectionKeyValue = null;
|
||||
collectionInstance = null;
|
||||
}
|
||||
|
||||
protected void resolveCollectionKey(RowProcessingState rowProcessingState, boolean checkPreviousRow) {
|
||||
final CollectionKey oldKey = collectionKey;
|
||||
final PersistentCollection<?> oldCollectionInstance = collectionInstance;
|
||||
collectionKey = null;
|
||||
collectionInstance = null;
|
||||
|
||||
if ( collectionKeyValue == null ) {
|
||||
if ( collectionKeyResultAssembler == null ) {
|
||||
assert owningEntityInitializer != null;
|
||||
collectionKeyValue = owningEntityInitializer.getEntityIdentifier();
|
||||
}
|
||||
else {
|
||||
collectionKeyValue = collectionKeyResultAssembler.assemble( rowProcessingState );
|
||||
}
|
||||
if ( collectionKeyValue == null ) {
|
||||
state = State.MISSING;
|
||||
collectionKey = null;
|
||||
collectionInstance = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
final CollectionPersister persister = collectionAttributeMapping.getCollectionDescriptor();
|
||||
// Try to reuse the previous collection key and collection if possible
|
||||
if ( checkPreviousRow && oldKey != null && persister.getKeyType().isEqual(
|
||||
oldKey.getKey(),
|
||||
collectionKeyValue
|
||||
) ) {
|
||||
collectionKey = oldKey;
|
||||
collectionInstance = oldCollectionInstance;
|
||||
state = oldCollectionInstance == null ? State.MISSING : State.RESOLVED;
|
||||
}
|
||||
else {
|
||||
collectionKey = new CollectionKey( persister, collectionKeyValue );
|
||||
state = State.KEY_RESOLVED;
|
||||
}
|
||||
}
|
||||
|
||||
protected void resolveInstance(RowProcessingState rowProcessingState, boolean isEager) {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
// already resolved
|
||||
return;
|
||||
}
|
||||
|
||||
resolveCollectionKey( rowProcessingState, false );
|
||||
if ( state == State.KEY_RESOLVED ) {
|
||||
assert parentAccess != null;
|
||||
assert parent != null;
|
||||
// We can avoid processing further if the parent is already initialized,
|
||||
// as the value produced by this initializer will never be used anyway.
|
||||
final EntityInitializer entityInitializer = parentAccess.findFirstEntityInitializer();
|
||||
if ( entityInitializer != null && entityInitializer.isEntityInitialized() ) {
|
||||
if ( owningEntityInitializer != null && owningEntityInitializer.isEntityInitialized() ) {
|
||||
// It doesn't matter if it's eager or lazy, the collection object can not be referred to,
|
||||
// so it doesn't make sense to create or initialize it
|
||||
state = State.MISSING;
|
||||
|
@ -141,7 +179,6 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
|
|||
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
final FetchParentAccess fetchParentAccess = parentAccess.findFirstEntityDescriptorAccess();
|
||||
|
||||
final LoadingCollectionEntry loadingEntry = persistenceContext.getLoadContexts()
|
||||
.findLoadingCollectionEntry( collectionKey );
|
||||
|
@ -149,9 +186,8 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
|
|||
if ( loadingEntry != null ) {
|
||||
collectionInstance = loadingEntry.getCollectionInstance();
|
||||
if ( collectionInstance.getOwner() == null ) {
|
||||
fetchParentAccess.registerResolutionListener(
|
||||
owner -> collectionInstance.setOwner( owner )
|
||||
);
|
||||
assert owningEntityInitializer.getTargetInstance() != null;
|
||||
collectionInstance.setOwner( owningEntityInitializer.getTargetInstance() );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -161,9 +197,8 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
|
|||
if ( existing != null ) {
|
||||
collectionInstance = existing;
|
||||
if ( collectionInstance.getOwner() == null ) {
|
||||
fetchParentAccess.registerResolutionListener(
|
||||
owner -> collectionInstance.setOwner( owner )
|
||||
);
|
||||
assert owningEntityInitializer.getTargetInstance() != null;
|
||||
collectionInstance.setOwner( owningEntityInitializer.getTargetInstance() );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -178,9 +213,8 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
|
|||
session
|
||||
);
|
||||
|
||||
fetchParentAccess.registerResolutionListener(
|
||||
owner -> collectionInstance.setOwner( owner )
|
||||
);
|
||||
assert owningEntityInitializer.getTargetInstance() != null;
|
||||
collectionInstance.setOwner( owningEntityInitializer.getTargetInstance() );
|
||||
|
||||
persistenceContext.addUninitializedCollection(
|
||||
collectionDescriptor,
|
||||
|
@ -197,6 +231,47 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
|
|||
}
|
||||
}
|
||||
}
|
||||
public void resolveInstance(Object instance, RowProcessingState rowProcessingState, boolean isEager) {
|
||||
if ( instance == null ) {
|
||||
setMissing();
|
||||
}
|
||||
else {
|
||||
final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal();
|
||||
final PersistentCollection<?> persistentCollection;
|
||||
if ( collectionAttributeMapping.getCollectionDescriptor()
|
||||
.getCollectionSemantics()
|
||||
.getCollectionClassification() == CollectionClassification.ARRAY ) {
|
||||
persistentCollection = persistenceContext.getCollectionHolder( instance );
|
||||
}
|
||||
else {
|
||||
persistentCollection = (PersistentCollection<?>) instance;
|
||||
}
|
||||
// resolving the collection key seems unnecessary
|
||||
// collectionKeyValue = persistentCollection.getKey();
|
||||
// resolveCollectionKey( rowProcessingState, false );
|
||||
collectionInstance = persistentCollection;
|
||||
state = State.RESOLVED;
|
||||
if ( isEager && !collectionInstance.wasInitialized() ) {
|
||||
persistenceContext.addNonLazyCollection( collectionInstance );
|
||||
}
|
||||
if ( collectionKeyResultAssembler != null
|
||||
&& !rowProcessingState.isQueryCacheHit()
|
||||
&& rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE ) {
|
||||
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
|
||||
collectionKeyResultAssembler.resolveState( rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
if ( collectionKeyResultAssembler != null ) {
|
||||
final Initializer initializer = collectionKeyResultAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PersistentCollection<?> getCollectionInstance() {
|
||||
|
@ -219,22 +294,12 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
|
|||
|
||||
@Override
|
||||
public @Nullable FetchParentAccess getFetchParentAccess() {
|
||||
return parentAccess;
|
||||
return (FetchParentAccess) parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FetchParentAccess getOwningParent() {
|
||||
return owningParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable EntityMappingType getOwnedModelPartDeclaringType() {
|
||||
return ownedModelPartDeclaringType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FetchParentAccess findFirstEntityDescriptorAccess() {
|
||||
return parentAccess == null ? null : parentAccess.findFirstEntityDescriptorAccess();
|
||||
public @Nullable InitializerParent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -242,11 +307,6 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> resolvedParentConsumer) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartOfKey() {
|
||||
// A collection can never be part of a key
|
||||
|
@ -260,30 +320,7 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
|
|||
|
||||
@Override
|
||||
public @Nullable CollectionKey resolveCollectionKey(RowProcessingState rowProcessingState) {
|
||||
resolveKey( rowProcessingState );
|
||||
resolveInstance();
|
||||
return collectionKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
state = State.UNINITIALIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markShallowCached() {
|
||||
parentShallowCached = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
parentShallowCached = false;
|
||||
}
|
||||
|
||||
protected enum State {
|
||||
UNINITIALIZED,
|
||||
MISSING,
|
||||
KEY_RESOLVED,
|
||||
RESOLVED,
|
||||
INITIALIZED
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
package org.hibernate.sql.results.graph.collection.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.collection.spi.CollectionSemantics;
|
||||
|
@ -26,8 +26,10 @@ import org.hibernate.sql.results.graph.DomainResult;
|
|||
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.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionLoadingLogger;
|
||||
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.internal.LoadingCollectionEntryImpl;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
|
@ -62,7 +64,10 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol
|
|||
private Object collectionValueKey;
|
||||
private LoadingCollectionEntryImpl responsibility;
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractImmediateCollectionInitializer(NavigablePath, PluralAttributeMapping, InitializerParent, LockMode, DomainResult, DomainResult, boolean, AssemblerCreationState)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public AbstractImmediateCollectionInitializer(
|
||||
NavigablePath collectionPath,
|
||||
PluralAttributeMapping collectionAttributeMapping,
|
||||
|
@ -72,53 +77,139 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol
|
|||
DomainResult<?> collectionValueKeyResult,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
this(
|
||||
collectionPath,
|
||||
collectionAttributeMapping,
|
||||
(InitializerParent) parentAccess,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
public AbstractImmediateCollectionInitializer(
|
||||
NavigablePath collectionPath,
|
||||
PluralAttributeMapping collectionAttributeMapping,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
super(
|
||||
collectionPath,
|
||||
collectionAttributeMapping,
|
||||
parentAccess,
|
||||
parent,
|
||||
collectionKeyResult,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
);
|
||||
this.collectionValueKeyResultAssembler = collectionKeyResult == collectionValueKeyResult
|
||||
? null
|
||||
: collectionValueKeyResult.createResultAssembler( this, creationState );
|
||||
: collectionValueKeyResult.createResultAssembler( (InitializerParent) this, creationState );
|
||||
}
|
||||
|
||||
protected abstract String getSimpleConcreteImplName();
|
||||
|
||||
protected abstract void forEachAssembler(Consumer<DomainResultAssembler<?>> consumer);
|
||||
|
||||
@Override
|
||||
public void startLoading(RowProcessingState rowProcessingState) {
|
||||
if ( rowProcessingState.isQueryCacheHit() && getInitializingCollectionDescriptor().useShallowQueryCacheLayout() && !parentShallowCached ) {
|
||||
shallowCached = true;
|
||||
// Inform sub-initializers if this is a query cache hit for a shallow entry
|
||||
markSubInitializersAsShallowCached();
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
super.forEachSubInitializer( consumer, arg );
|
||||
if ( collectionValueKeyResultAssembler != null ) {
|
||||
final Initializer initializer = collectionValueKeyResultAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markShallowCached() {
|
||||
super.markShallowCached();
|
||||
markSubInitializersAsShallowCached();
|
||||
}
|
||||
|
||||
private void markSubInitializersAsShallowCached() {
|
||||
forEachAssembler( assembler -> {
|
||||
final Initializer initializer = assembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializer.markShallowCached();
|
||||
}
|
||||
} );
|
||||
public void startLoading(RowProcessingState rowProcessingState) {
|
||||
if ( rowProcessingState.isQueryCacheHit() && getInitializingCollectionDescriptor().useShallowQueryCacheLayout() ) {
|
||||
shallowCached = true;
|
||||
}
|
||||
super.startLoading( rowProcessingState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
public void resolveKey() {
|
||||
if ( state != State.UNINITIALIZED ) {
|
||||
// already resolved
|
||||
return;
|
||||
}
|
||||
super.resolveKey();
|
||||
collectionValueKey = null;
|
||||
// Can't resolve any sub-initializers if the collection is shallow cached
|
||||
if ( state != State.MISSING && !shallowCached ) {
|
||||
if ( collectionValueKeyResultAssembler == null ) {
|
||||
// A null collectionValueKeyResultAssembler means that we should use the parent key.
|
||||
// Since this method can only be called when the parent exists, we know the collection is not missing
|
||||
resolveKeySubInitializers( rowProcessingState );
|
||||
}
|
||||
else {
|
||||
resolveCollectionContentKey( rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the collection value key is missing.
|
||||
*/
|
||||
private boolean resolveCollectionContentKey(RowProcessingState rowProcessingState) {
|
||||
assert collectionValueKeyResultAssembler != null;
|
||||
final Initializer initializer = collectionValueKeyResultAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializer.resolveKey();
|
||||
if ( initializer.getState() == State.MISSING ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
collectionValueKey = collectionValueKeyResultAssembler.assemble( rowProcessingState );
|
||||
if ( collectionValueKey == null ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// If we get here, a collectionValueKey exists or is likely to exist,
|
||||
// so we need to call resolveKey on the index and element initializers of the collection
|
||||
// to initialize this resolved collection instance later
|
||||
resolveKeySubInitializers( rowProcessingState );
|
||||
return false;
|
||||
}
|
||||
|
||||
private void resolveKeySubInitializers(RowProcessingState rowProcessingState) {
|
||||
final DomainResultAssembler<?> indexAssembler = getIndexAssembler();
|
||||
final Initializer indexInitializer;
|
||||
if ( indexAssembler != null && ( indexInitializer = indexAssembler.getInitializer() ) != null ) {
|
||||
indexInitializer.resolveKey();
|
||||
}
|
||||
final Initializer elementInitializer = getElementAssembler().getInitializer();
|
||||
if ( elementInitializer != null ) {
|
||||
elementInitializer.resolveKey();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance() {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
// already resolved
|
||||
return;
|
||||
}
|
||||
|
||||
resolveCollectionKey( rowProcessingState, true );
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
CollectionLoadingLogger.COLL_LOAD_LOGGER.debugf(
|
||||
"(%s) Current row collection key : %s",
|
||||
this.getClass().getSimpleName(),
|
||||
LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() )
|
||||
);
|
||||
}
|
||||
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isTraceEnabled() ) {
|
||||
COLL_LOAD_LOGGER.tracef(
|
||||
"(%s) Beginning Initializer#resolveInstance for collection : %s",
|
||||
|
@ -136,91 +227,84 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol
|
|||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// First, look for a LoadingCollectionEntry
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
final LoadingCollectionEntry existingLoadingEntry = persistenceContext.getLoadContexts()
|
||||
.findLoadingCollectionEntry( collectionKey );
|
||||
final PersistentCollection<?> existing;
|
||||
final PersistentCollection<?> existingUnowned;
|
||||
if ( existingLoadingEntry != null ) {
|
||||
collectionInstance = existingLoadingEntry.getCollectionInstance();
|
||||
|
||||
if ( !shallowCached ) {
|
||||
final LoadingCollectionEntry existingLoadingEntry = persistenceContext.getLoadContexts()
|
||||
.findLoadingCollectionEntry( collectionKey );
|
||||
if ( existingLoadingEntry != null ) {
|
||||
collectionInstance = existingLoadingEntry.getCollectionInstance();
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
COLL_LOAD_LOGGER.debugf(
|
||||
"(%s) Found existing loading collection entry [%s]; using loading collection instance - %s",
|
||||
getSimpleConcreteImplName(),
|
||||
LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ),
|
||||
toLoggableString( collectionInstance )
|
||||
);
|
||||
}
|
||||
|
||||
if ( existingLoadingEntry.getInitializer() == this ) {
|
||||
assert !shallowCached;
|
||||
// we are responsible for loading the collection values
|
||||
responsibility = (LoadingCollectionEntryImpl) existingLoadingEntry;
|
||||
}
|
||||
else {
|
||||
// the entity is already being loaded elsewhere
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
COLL_LOAD_LOGGER.debugf(
|
||||
"(%s) Found existing loading collection entry [%s]; using loading collection instance - %s",
|
||||
"(%s) Collection [%s] being loaded by another initializer [%s] - skipping processing",
|
||||
getSimpleConcreteImplName(),
|
||||
LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ),
|
||||
existingLoadingEntry.getInitializer()
|
||||
);
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
}
|
||||
}
|
||||
else if ( ( existing = persistenceContext.getCollection( collectionKey ) ) != null ) {
|
||||
collectionInstance = existing;
|
||||
|
||||
// we found the corresponding collection instance on the Session. If
|
||||
// it is already initialized we have nothing to do
|
||||
|
||||
if ( collectionInstance.wasInitialized() ) {
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
COLL_LOAD_LOGGER.debugf(
|
||||
"(%s) Found existing collection instance [%s] in Session; skipping processing - [%s]",
|
||||
getSimpleConcreteImplName(),
|
||||
LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ),
|
||||
toLoggableString( collectionInstance )
|
||||
);
|
||||
}
|
||||
|
||||
if ( existingLoadingEntry.getInitializer() == this ) {
|
||||
// we are responsible for loading the collection values
|
||||
responsibility = (LoadingCollectionEntryImpl) existingLoadingEntry;
|
||||
}
|
||||
else {
|
||||
// the entity is already being loaded elsewhere
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
COLL_LOAD_LOGGER.debugf(
|
||||
"(%s) Collection [%s] being loaded by another initializer [%s] - skipping processing",
|
||||
getSimpleConcreteImplName(),
|
||||
LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ),
|
||||
existingLoadingEntry.getInitializer()
|
||||
);
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
}
|
||||
else if ( !shallowCached ) {
|
||||
takeResponsibility( rowProcessingState, collectionKey );
|
||||
}
|
||||
}
|
||||
if ( collectionInstance == null ) {
|
||||
final PersistentCollection<?> existing = persistenceContext.getCollection( collectionKey );
|
||||
if ( existing != null ) {
|
||||
collectionInstance = existing;
|
||||
else if ( ( existingUnowned = persistenceContext.useUnownedCollection( collectionKey ) ) != null ) {
|
||||
collectionInstance = existingUnowned;
|
||||
|
||||
// we found the corresponding collection instance on the Session. If
|
||||
// it is already initialized we have nothing to do
|
||||
// we found the corresponding collection instance as unowned on the Session. If
|
||||
// it is already initialized we have nothing to do
|
||||
|
||||
if ( collectionInstance.wasInitialized() ) {
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
COLL_LOAD_LOGGER.debugf(
|
||||
"(%s) Found existing collection instance [%s] in Session; skipping processing - [%s]",
|
||||
getSimpleConcreteImplName(),
|
||||
LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ),
|
||||
toLoggableString( collectionInstance )
|
||||
);
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
}
|
||||
else if ( !shallowCached ) {
|
||||
takeResponsibility( rowProcessingState, collectionKey );
|
||||
if ( collectionInstance.wasInitialized() ) {
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
COLL_LOAD_LOGGER.debugf(
|
||||
"(%s) Found existing unowned collection instance [%s] in Session; skipping processing - [%s]",
|
||||
getSimpleConcreteImplName(),
|
||||
LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ),
|
||||
toLoggableString( collectionInstance )
|
||||
);
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
}
|
||||
else {
|
||||
final PersistentCollection<?> existingUnowned = persistenceContext.useUnownedCollection( collectionKey );
|
||||
if ( existingUnowned != null ) {
|
||||
collectionInstance = existingUnowned;
|
||||
|
||||
// we found the corresponding collection instance as unowned on the Session. If
|
||||
// it is already initialized we have nothing to do
|
||||
|
||||
if ( collectionInstance.wasInitialized() ) {
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
COLL_LOAD_LOGGER.debugf(
|
||||
"(%s) Found existing unowned collection instance [%s] in Session; skipping processing - [%s]",
|
||||
getSimpleConcreteImplName(),
|
||||
LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ),
|
||||
toLoggableString( collectionInstance )
|
||||
);
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
}
|
||||
else if ( !shallowCached ) {
|
||||
takeResponsibility( rowProcessingState, collectionKey );
|
||||
}
|
||||
}
|
||||
else if ( !shallowCached ) {
|
||||
takeResponsibility( rowProcessingState, collectionKey );
|
||||
}
|
||||
}
|
||||
|
||||
if ( collectionInstance == null ) {
|
||||
else {
|
||||
final CollectionPersister collectionDescriptor = getCollectionAttributeMapping().getCollectionDescriptor();
|
||||
final CollectionSemantics<?, ?> collectionSemantics = collectionDescriptor.getCollectionSemantics();
|
||||
|
||||
|
@ -239,29 +323,18 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol
|
|||
);
|
||||
}
|
||||
|
||||
if ( owningEntityInitializer != null ) {
|
||||
assert owningEntityInitializer.getTargetInstance() != null;
|
||||
collectionInstance.setOwner( owningEntityInitializer.getTargetInstance() );
|
||||
}
|
||||
|
||||
persistenceContext.addUninitializedCollection(
|
||||
collectionDescriptor,
|
||||
collectionInstance,
|
||||
collectionKey.getKey()
|
||||
);
|
||||
|
||||
if ( shallowCached ) {
|
||||
// If this is a query cache hit with the shallow query cache layout,
|
||||
// we have to lazy load the collection instead
|
||||
persistenceContext.addNonLazyCollection( collectionInstance );
|
||||
|
||||
final FetchParentAccess entityParentAccess = findFirstEntityDescriptorAccess();
|
||||
if ( entityParentAccess != null ) {
|
||||
entityParentAccess.registerResolutionListener(
|
||||
owner -> collectionInstance.setOwner( owner )
|
||||
);
|
||||
}
|
||||
|
||||
if ( collectionSemantics.getCollectionClassification() == CollectionClassification.ARRAY ) {
|
||||
session.getPersistenceContext().addCollectionHolder( collectionInstance );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( !shallowCached ) {
|
||||
takeResponsibility( rowProcessingState, collectionKey );
|
||||
}
|
||||
}
|
||||
|
@ -275,21 +348,144 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol
|
|||
toLoggableString( collectionInstance )
|
||||
);
|
||||
}
|
||||
|
||||
final FetchParentAccess entityParentAccess = findFirstEntityDescriptorAccess();
|
||||
if ( entityParentAccess != null ) {
|
||||
entityParentAccess.registerResolutionListener(
|
||||
owner -> collectionInstance.setOwner( owner )
|
||||
);
|
||||
}
|
||||
}
|
||||
if ( shallowCached ) {
|
||||
assert responsibility == null;
|
||||
state = State.INITIALIZED;
|
||||
initializeSubInstancesFromParent( rowProcessingState );
|
||||
initializeShallowCached( rowProcessingState );
|
||||
}
|
||||
}
|
||||
|
||||
protected void initializeShallowCached(RowProcessingState rowProcessingState) {
|
||||
assert shallowCached;
|
||||
final PersistenceContext persistenceContext = rowProcessingState.getSession()
|
||||
.getPersistenceContextInternal();
|
||||
// If this is a query cache hit with the shallow query cache layout,
|
||||
// we have to lazy load the collection instead
|
||||
collectionInstance.forceInitialization();
|
||||
if ( collectionAttributeMapping.getCollectionDescriptor()
|
||||
.getCollectionSemantics()
|
||||
.getCollectionClassification() == CollectionClassification.ARRAY ) {
|
||||
persistenceContext.addCollectionHolder( collectionInstance );
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
initializeSubInstancesFromParent( rowProcessingState );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setMissing() {
|
||||
super.setMissing();
|
||||
collectionValueKey = null;
|
||||
responsibility = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(Object instance) {
|
||||
assert state == State.UNINITIALIZED;
|
||||
if ( instance == null ) {
|
||||
setMissing();
|
||||
return;
|
||||
}
|
||||
// Check if the given instance is different from the previous row state to avoid creating CollectionKey
|
||||
if ( collectionInstance != instance ) {
|
||||
final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal();
|
||||
final PersistentCollection<?> persistentCollection;
|
||||
if ( collectionAttributeMapping.getCollectionDescriptor()
|
||||
.getCollectionSemantics()
|
||||
.getCollectionClassification() == CollectionClassification.ARRAY ) {
|
||||
persistentCollection = persistenceContext.getCollectionHolder( instance );
|
||||
}
|
||||
else {
|
||||
persistentCollection = (PersistentCollection<?>) instance;
|
||||
}
|
||||
collectionKeyValue = persistentCollection.getKey();
|
||||
resolveCollectionKey( rowProcessingState, false );
|
||||
collectionInstance = persistentCollection;
|
||||
responsibility = null;
|
||||
}
|
||||
collectionValueKey = null;
|
||||
if ( collectionInstance.wasInitialized() ) {
|
||||
state = State.INITIALIZED;
|
||||
if ( shallowCached ) {
|
||||
initializeShallowCached( rowProcessingState );
|
||||
}
|
||||
else {
|
||||
resolveInstanceSubInitializers( rowProcessingState );
|
||||
}
|
||||
if ( !rowProcessingState.isQueryCacheHit() && rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE ) {
|
||||
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
|
||||
if ( collectionKeyResultAssembler != null ) {
|
||||
collectionKeyResultAssembler.resolveState( rowProcessingState );
|
||||
}
|
||||
if ( !getInitializingCollectionDescriptor().useShallowQueryCacheLayout() ) {
|
||||
if ( collectionValueKeyResultAssembler != null ) {
|
||||
collectionValueKeyResultAssembler.resolveState( rowProcessingState );
|
||||
}
|
||||
resolveCollectionContentState( rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( shallowCached ) {
|
||||
state = State.INITIALIZED;
|
||||
initializeShallowCached( rowProcessingState );
|
||||
}
|
||||
else {
|
||||
state = State.RESOLVED;
|
||||
final boolean rowContainsCollectionContent;
|
||||
if ( collectionValueKeyResultAssembler != null ) {
|
||||
rowContainsCollectionContent = resolveCollectionContentKey( rowProcessingState );
|
||||
}
|
||||
else {
|
||||
rowContainsCollectionContent = true;
|
||||
}
|
||||
if ( responsibility == null ) {
|
||||
final LoadingCollectionEntry existingLoadingEntry = rowProcessingState.getSession()
|
||||
.getPersistenceContextInternal()
|
||||
.getLoadContexts()
|
||||
.findLoadingCollectionEntry( collectionKey );
|
||||
if ( existingLoadingEntry != null ) {
|
||||
if ( existingLoadingEntry.getInitializer() == this ) {
|
||||
// we are responsible for loading the collection values
|
||||
responsibility = (LoadingCollectionEntryImpl) existingLoadingEntry;
|
||||
}
|
||||
else {
|
||||
// the collection is already being loaded elsewhere
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
COLL_LOAD_LOGGER.debugf(
|
||||
"(%s) Collection [%s] being loaded by another initializer [%s] - skipping processing",
|
||||
getSimpleConcreteImplName(),
|
||||
LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ),
|
||||
existingLoadingEntry.getInitializer()
|
||||
);
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
if ( rowContainsCollectionContent && !rowProcessingState.isQueryCacheHit()
|
||||
&& rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE
|
||||
&& !getInitializingCollectionDescriptor().useShallowQueryCacheLayout() ) {
|
||||
// Resolve the state of the content if result caching is enabled and this is not a query cache hit
|
||||
// and the collection doesn't use a shallow query cache layout
|
||||
resolveCollectionContentState( rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
takeResponsibility( rowProcessingState, collectionKey );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void resolveInstanceSubInitializers(RowProcessingState rowProcessingState);
|
||||
|
||||
private void resolveCollectionContentState(RowProcessingState rowProcessingState) {
|
||||
final DomainResultAssembler<?> indexAssembler = getIndexAssembler();
|
||||
if ( indexAssembler != null ) {
|
||||
indexAssembler.resolveState( rowProcessingState );
|
||||
}
|
||||
getElementAssembler().resolveState( rowProcessingState );
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized toString handling for PersistentCollection. All `PersistentCollection#toString`
|
||||
* implementations are crazy expensive as they trigger a load
|
||||
|
@ -314,31 +510,21 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol
|
|||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
if ( state != State.UNINITIALIZED ) {
|
||||
// already resolved
|
||||
return;
|
||||
}
|
||||
super.resolveKey( rowProcessingState );
|
||||
if ( collectionKey != null ) {
|
||||
if ( collectionValueKeyResultAssembler == null ) {
|
||||
collectionValueKey = collectionKey.getKey();
|
||||
}
|
||||
else {
|
||||
collectionValueKey = collectionValueKeyResultAssembler.assemble( rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
public void initializeInstance() {
|
||||
if ( state != State.RESOLVED || responsibility == null ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
|
||||
if ( collectionValueKey == null && collectionValueKeyResultAssembler != null ) {
|
||||
final Initializer initializer = collectionValueKeyResultAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
collectionValueKey = collectionValueKeyResultAssembler.assemble( rowProcessingState );
|
||||
}
|
||||
}
|
||||
|
||||
// the RHS key value of the association - determines if the row contains an element of the initializing collection
|
||||
if ( collectionValueKey != null ) {
|
||||
if ( collectionValueKeyResultAssembler == null || collectionValueKey != null ) {
|
||||
// the row contains an element in the collection...
|
||||
if ( CollectionLoadingLogger.COLL_LOAD_LOGGER.isDebugEnabled() ) {
|
||||
COLL_LOAD_LOGGER.debugf(
|
||||
|
@ -356,7 +542,7 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstanceFromParent(Object parentInstance, RowProcessingState rowProcessingState) {
|
||||
public void initializeInstanceFromParent(Object parentInstance) {
|
||||
collectionInstance = (PersistentCollection<?>) getInitializedPart().getValue( parentInstance );
|
||||
state = State.INITIALIZED;
|
||||
initializeSubInstancesFromParent( rowProcessingState );
|
||||
|
@ -369,12 +555,9 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol
|
|||
|
||||
protected abstract void initializeSubInstancesFromParent(RowProcessingState rowProcessingState);
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
super.finishUpRow( rowProcessingState );
|
||||
public abstract @Nullable DomainResultAssembler<?> getIndexAssembler();
|
||||
|
||||
collectionValueKey = null;
|
||||
}
|
||||
public abstract DomainResultAssembler<?> getElementAssembler();
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
|
|
|
@ -8,7 +8,7 @@ package org.hibernate.sql.results.graph.collection.internal;
|
|||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
|
@ -23,6 +23,7 @@ import org.hibernate.sql.results.graph.DomainResultAssembler;
|
|||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -38,6 +39,10 @@ public class ArrayInitializer extends AbstractImmediateCollectionInitializer {
|
|||
|
||||
private final int indexBase;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #ArrayInitializer(NavigablePath, PluralAttributeMapping, InitializerParent, LockMode, DomainResult, DomainResult, boolean, AssemblerCreationState, Fetch, Fetch)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public ArrayInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping arrayDescriptor,
|
||||
|
@ -49,10 +54,35 @@ public class ArrayInitializer extends AbstractImmediateCollectionInitializer {
|
|||
Fetch elementFetch,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
this(
|
||||
navigablePath,
|
||||
arrayDescriptor,
|
||||
(InitializerParent) parentAccess,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
isResultInitializer,
|
||||
creationState,
|
||||
listIndexFetch,
|
||||
elementFetch
|
||||
);
|
||||
}
|
||||
|
||||
public ArrayInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping arrayDescriptor,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState,
|
||||
Fetch listIndexFetch,
|
||||
Fetch elementFetch) {
|
||||
super(
|
||||
navigablePath,
|
||||
arrayDescriptor,
|
||||
parentAccess,
|
||||
parent,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
|
@ -60,8 +90,8 @@ public class ArrayInitializer extends AbstractImmediateCollectionInitializer {
|
|||
creationState
|
||||
);
|
||||
//noinspection unchecked
|
||||
this.listIndexAssembler = (DomainResultAssembler<Integer>) listIndexFetch.createAssembler( this, creationState );
|
||||
this.elementAssembler = elementFetch.createAssembler( this, creationState );
|
||||
this.listIndexAssembler = (DomainResultAssembler<Integer>) listIndexFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
this.elementAssembler = elementFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
this.indexBase = getCollectionAttributeMapping().getIndexMetadata().getListIndexBase();
|
||||
}
|
||||
|
||||
|
@ -71,9 +101,12 @@ public class ArrayInitializer extends AbstractImmediateCollectionInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void forEachAssembler(Consumer<DomainResultAssembler<?>> consumer) {
|
||||
consumer.accept( listIndexAssembler );
|
||||
consumer.accept( elementAssembler );
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
super.forEachSubInitializer( consumer, arg );
|
||||
final Initializer initializer = elementAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -110,10 +143,12 @@ public class ArrayInitializer extends AbstractImmediateCollectionInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstanceFromParent(Object parentInstance, RowProcessingState rowProcessingState) {
|
||||
public void initializeInstanceFromParent(Object parentInstance) {
|
||||
final Object[] array = (Object[]) getInitializedPart().getValue( parentInstance );
|
||||
assert array != null;
|
||||
collectionInstance = new PersistentArrayHolder<>( rowProcessingState.getSession(), array );
|
||||
collectionInstance = rowProcessingState.getSession()
|
||||
.getPersistenceContextInternal()
|
||||
.getCollectionHolder( array );
|
||||
state = State.INITIALIZED;
|
||||
initializeSubInstancesFromParent( rowProcessingState );
|
||||
}
|
||||
|
@ -124,11 +159,32 @@ public class ArrayInitializer extends AbstractImmediateCollectionInitializer {
|
|||
if ( initializer != null ) {
|
||||
final Iterator iter = getCollectionInstance().elements();
|
||||
while ( iter.hasNext() ) {
|
||||
initializer.initializeInstanceFromParent( iter.next(), rowProcessingState );
|
||||
initializer.initializeInstanceFromParent( iter.next() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resolveInstanceSubInitializers(RowProcessingState rowProcessingState) {
|
||||
final Initializer initializer = elementAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
final Iterator iter = getCollectionInstance().elements();
|
||||
while ( iter.hasNext() ) {
|
||||
initializer.resolveInstance( iter.next() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> getIndexAssembler() {
|
||||
return listIndexAssembler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> getElementAssembler() {
|
||||
return elementAssembler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ArrayInitializer{" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.hibernate.sql.results.graph.DomainResult;
|
|||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
|
||||
/**
|
||||
|
@ -50,7 +51,7 @@ public class ArrayInitializerProducer implements CollectionInitializerProducer {
|
|||
public CollectionInitializer produceInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping attribute,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
|
@ -59,14 +60,14 @@ public class ArrayInitializerProducer implements CollectionInitializerProducer {
|
|||
return new ArrayInitializer(
|
||||
navigablePath,
|
||||
arrayDescriptor,
|
||||
parentAccess,
|
||||
parent,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
listIndexFetch,
|
||||
elementFetch,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
creationState,
|
||||
listIndexFetch,
|
||||
elementFetch
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
package org.hibernate.sql.results.graph.collection.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.collection.spi.PersistentBag;
|
||||
|
@ -23,6 +23,7 @@ import org.hibernate.sql.results.graph.DomainResultAssembler;
|
|||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -39,6 +40,10 @@ public class BagInitializer extends AbstractImmediateCollectionInitializer {
|
|||
private final DomainResultAssembler<?> elementAssembler;
|
||||
private final DomainResultAssembler<?> collectionIdAssembler;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #BagInitializer(NavigablePath, PluralAttributeMapping, InitializerParent, LockMode, DomainResult, DomainResult, boolean, AssemblerCreationState, Fetch, Fetch)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public BagInitializer(
|
||||
PluralAttributeMapping bagDescriptor,
|
||||
FetchParentAccess parentAccess,
|
||||
|
@ -50,20 +55,45 @@ public class BagInitializer extends AbstractImmediateCollectionInitializer {
|
|||
@Nullable Fetch collectionIdFetch,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
this(
|
||||
navigablePath,
|
||||
bagDescriptor,
|
||||
(InitializerParent) parentAccess,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
isResultInitializer,
|
||||
creationState,
|
||||
elementFetch,
|
||||
collectionIdFetch
|
||||
);
|
||||
}
|
||||
|
||||
public BagInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping bagDescriptor,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState,
|
||||
Fetch elementFetch,
|
||||
@Nullable Fetch collectionIdFetch) {
|
||||
super(
|
||||
navigablePath,
|
||||
bagDescriptor,
|
||||
parentAccess,
|
||||
parent,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
);
|
||||
this.elementAssembler = elementFetch.createAssembler( this, creationState );
|
||||
this.elementAssembler = elementFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
this.collectionIdAssembler = collectionIdFetch == null
|
||||
? null
|
||||
: collectionIdFetch.createAssembler( this, creationState );
|
||||
: collectionIdFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -72,8 +102,12 @@ public class BagInitializer extends AbstractImmediateCollectionInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void forEachAssembler(Consumer<DomainResultAssembler<?>> consumer) {
|
||||
consumer.accept( elementAssembler );
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
super.forEachSubInitializer( consumer, arg );
|
||||
final Initializer initializer = elementAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -111,17 +145,46 @@ public class BagInitializer extends AbstractImmediateCollectionInitializer {
|
|||
assert persistentCollection != null;
|
||||
if ( persistentCollection instanceof PersistentBag<?> ) {
|
||||
for ( Object element : ( (PersistentBag<?>) persistentCollection ) ) {
|
||||
initializer.initializeInstanceFromParent( element, rowProcessingState );
|
||||
initializer.initializeInstanceFromParent( element );
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( Object element : ( (PersistentIdentifierBag<?>) persistentCollection ) ) {
|
||||
initializer.initializeInstanceFromParent( element, rowProcessingState );
|
||||
initializer.initializeInstanceFromParent( element );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resolveInstanceSubInitializers(RowProcessingState rowProcessingState) {
|
||||
final Initializer initializer = elementAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
final PersistentCollection<?> persistentCollection = getCollectionInstance();
|
||||
assert persistentCollection != null;
|
||||
if ( persistentCollection instanceof PersistentBag<?> ) {
|
||||
for ( Object element : ( (PersistentBag<?>) persistentCollection ) ) {
|
||||
initializer.resolveInstance( element );
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( Object element : ( (PersistentIdentifierBag<?>) persistentCollection ) ) {
|
||||
initializer.resolveInstance( element );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> getIndexAssembler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> getElementAssembler() {
|
||||
return elementAssembler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BagInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.hibernate.sql.results.graph.DomainResult;
|
|||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -62,23 +63,23 @@ public class BagInitializerProducer implements CollectionInitializerProducer {
|
|||
public CollectionInitializer produceInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping attribute,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
return new BagInitializer(
|
||||
bagDescriptor,
|
||||
parentAccess,
|
||||
navigablePath,
|
||||
bagDescriptor,
|
||||
parent,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
elementFetch,
|
||||
collectionIdFetch,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
creationState,
|
||||
elementFetch,
|
||||
collectionIdFetch
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,13 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.collection.internal;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.collection.spi.PersistentArrayHolder;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
@ -30,6 +33,9 @@ public class CollectionAssembler implements DomainResultAssembler {
|
|||
|
||||
@Override
|
||||
public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
|
||||
assert initializer.getState() != Initializer.State.UNINITIALIZED
|
||||
&& initializer.getState() != Initializer.State.KEY_RESOLVED;
|
||||
// initializer.resolve( rowProcessingState );
|
||||
PersistentCollection<?> collectionInstance = initializer.getCollectionInstance();
|
||||
if ( collectionInstance instanceof PersistentArrayHolder ) {
|
||||
return collectionInstance.getValue();
|
||||
|
@ -46,4 +52,16 @@ public class CollectionAssembler implements DomainResultAssembler {
|
|||
public CollectionInitializer getInitializer() {
|
||||
return initializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveState(RowProcessingState rowProcessingState) {
|
||||
initializer.resolveInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachResultAssembler(BiConsumer consumer, Object arg) {
|
||||
if ( initializer.isResultInitializer() ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,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.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionResultGraphNode;
|
||||
|
@ -92,23 +93,30 @@ public class CollectionDomainResult implements DomainResult, CollectionResultGra
|
|||
public DomainResultAssembler createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return new CollectionAssembler( loadingAttribute, creationState.resolveInitializer( this, parentAccess, this ).asCollectionInitializer() );
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return new CollectionAssembler( loadingAttribute, creationState.resolveInitializer( this, parent, this ).asCollectionInitializer() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionInitializer createInitializer(
|
||||
CollectionDomainResult resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public CollectionInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return initializerProducer.produceInitializer(
|
||||
loadingPath,
|
||||
loadingAttribute,
|
||||
parentAccess,
|
||||
parent,
|
||||
LockMode.READ,
|
||||
fkResult,
|
||||
fkResult,
|
||||
|
|
|
@ -15,6 +15,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.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
||||
|
||||
|
@ -82,18 +83,27 @@ public abstract class CollectionFetch implements FetchParent, Fetch, Initializer
|
|||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> createAssembler(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public DomainResultAssembler<?> createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> createAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return new CollectionAssembler(
|
||||
getFetchedMapping(),
|
||||
creationState.resolveInitializer( this, parentAccess, this ).asCollectionInitializer()
|
||||
creationState.resolveInitializer( this, parent, this ).asCollectionInitializer()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
CollectionFetch resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.sql.results.graph.DomainResult;
|
|||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
|
@ -42,20 +43,27 @@ public class DelayedCollectionFetch extends CollectionFetch {
|
|||
public DomainResultAssembler<?> createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> createAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
// lazy attribute
|
||||
if ( unfetched ) {
|
||||
return new UnfetchedCollectionAssembler( getFetchedMapping() );
|
||||
}
|
||||
else {
|
||||
return super.createAssembler( parentAccess, creationState );
|
||||
return super.createAssembler( parent, creationState );
|
||||
}
|
||||
}
|
||||
|
||||
public CollectionInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public CollectionInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new DelayedCollectionInitializer(
|
||||
getNavigablePath(),
|
||||
getFetchedMapping(),
|
||||
parentAccess,
|
||||
parent,
|
||||
collectionKeyResult,
|
||||
creationState
|
||||
);
|
||||
|
|
|
@ -12,13 +12,17 @@ import org.hibernate.spi.NavigablePath;
|
|||
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.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class DelayedCollectionInitializer extends AbstractCollectionInitializer {
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #DelayedCollectionInitializer(NavigablePath, PluralAttributeMapping, InitializerParent, DomainResult, AssemblerCreationState)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public DelayedCollectionInitializer(
|
||||
NavigablePath fetchedPath,
|
||||
PluralAttributeMapping fetchedMapping,
|
||||
|
@ -28,22 +32,23 @@ public class DelayedCollectionInitializer extends AbstractCollectionInitializer
|
|||
super( fetchedPath, fetchedMapping, parentAccess, collectionKeyResult, false, creationState );
|
||||
}
|
||||
|
||||
public DelayedCollectionInitializer(
|
||||
NavigablePath fetchedPath,
|
||||
PluralAttributeMapping fetchedMapping,
|
||||
InitializerParent parent,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
AssemblerCreationState creationState) {
|
||||
super( fetchedPath, fetchedMapping, parent, collectionKeyResult, false, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
public void resolveInstance() {
|
||||
resolveInstance( rowProcessingState, false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
super.finishUpRow( rowProcessingState );
|
||||
// Chances are pretty low that we can reuse the collection key,
|
||||
// so set it to null in order to avoid an additional equals collection key comparison
|
||||
collectionKey = null;
|
||||
collectionInstance = null;
|
||||
public void resolveInstance(Object instance) {
|
||||
resolveInstance( instance, rowProcessingState, false );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,14 +20,11 @@ import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
|||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
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.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -156,11 +153,11 @@ public class EagerCollectionFetch extends CollectionFetch {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CollectionInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public CollectionInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return initializerProducer.produceInitializer(
|
||||
getNavigablePath(),
|
||||
getFetchedMapping(),
|
||||
parentAccess,
|
||||
parent,
|
||||
null,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
|
@ -227,8 +224,8 @@ public class EagerCollectionFetch extends CollectionFetch {
|
|||
if ( collectionKeyResult != null ) {
|
||||
collectionKeyResult.collectValueIndexesToCache( valueIndexes );
|
||||
}
|
||||
collectionValueKeyResult.collectValueIndexesToCache( valueIndexes );
|
||||
if ( !getFetchedMapping().getCollectionDescriptor().useShallowQueryCacheLayout() ) {
|
||||
collectionValueKeyResult.collectValueIndexesToCache( valueIndexes );
|
||||
for ( Fetch fetch : fetches ) {
|
||||
fetch.collectValueIndexesToCache( valueIndexes );
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
package org.hibernate.sql.results.graph.collection.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
|
@ -22,6 +22,7 @@ import org.hibernate.sql.results.graph.DomainResultAssembler;
|
|||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -39,6 +40,10 @@ public class ListInitializer extends AbstractImmediateCollectionInitializer {
|
|||
|
||||
private final int listIndexBase;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #ListInitializer(NavigablePath, PluralAttributeMapping, InitializerParent, LockMode, DomainResult, DomainResult, boolean, AssemblerCreationState, Fetch, Fetch)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public ListInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping attributeMapping,
|
||||
|
@ -50,10 +55,33 @@ public class ListInitializer extends AbstractImmediateCollectionInitializer {
|
|||
Fetch elementFetch,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
this(
|
||||
navigablePath,
|
||||
attributeMapping,
|
||||
(InitializerParent) parentAccess,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
isResultInitializer, creationState, listIndexFetch,
|
||||
elementFetch
|
||||
);
|
||||
}
|
||||
|
||||
public ListInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping attributeMapping,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState,
|
||||
Fetch listIndexFetch,
|
||||
Fetch elementFetch) {
|
||||
super(
|
||||
navigablePath,
|
||||
attributeMapping,
|
||||
parentAccess,
|
||||
parent,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
|
@ -61,8 +89,8 @@ public class ListInitializer extends AbstractImmediateCollectionInitializer {
|
|||
creationState
|
||||
);
|
||||
//noinspection unchecked
|
||||
this.listIndexAssembler = (DomainResultAssembler<Integer>) listIndexFetch.createAssembler( this, creationState );
|
||||
this.elementAssembler = elementFetch.createAssembler( this, creationState );
|
||||
this.listIndexAssembler = (DomainResultAssembler<Integer>) listIndexFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
this.elementAssembler = elementFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
this.listIndexBase = attributeMapping.getIndexMetadata().getListIndexBase();
|
||||
}
|
||||
|
||||
|
@ -72,9 +100,12 @@ public class ListInitializer extends AbstractImmediateCollectionInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void forEachAssembler(Consumer<DomainResultAssembler<?>> consumer) {
|
||||
consumer.accept( listIndexAssembler );
|
||||
consumer.accept( elementAssembler );
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
super.forEachSubInitializer( consumer, arg );
|
||||
final Initializer initializer = elementAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -117,11 +148,33 @@ public class ListInitializer extends AbstractImmediateCollectionInitializer {
|
|||
final PersistentList<?> list = getCollectionInstance();
|
||||
assert list != null;
|
||||
for ( Object element : list ) {
|
||||
initializer.initializeInstanceFromParent( element, rowProcessingState );
|
||||
initializer.initializeInstanceFromParent( element );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resolveInstanceSubInitializers(RowProcessingState rowProcessingState) {
|
||||
final Initializer initializer = elementAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
final PersistentList<?> list = getCollectionInstance();
|
||||
assert list != null;
|
||||
for ( Object element : list ) {
|
||||
initializer.resolveInstance( element );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> getIndexAssembler() {
|
||||
return listIndexAssembler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> getElementAssembler() {
|
||||
return elementAssembler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ListInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.hibernate.sql.results.graph.DomainResult;
|
|||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
|
||||
/**
|
||||
|
@ -50,7 +51,7 @@ public class ListInitializerProducer implements CollectionInitializerProducer {
|
|||
public CollectionInitializer produceInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping attribute,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
|
@ -59,14 +60,14 @@ public class ListInitializerProducer implements CollectionInitializerProducer {
|
|||
return new ListInitializer(
|
||||
navigablePath,
|
||||
attributeMapping,
|
||||
parentAccess,
|
||||
parent,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
listIndexFetch,
|
||||
elementFetch,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
creationState,
|
||||
listIndexFetch,
|
||||
elementFetch
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ package org.hibernate.sql.results.graph.collection.internal;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.collection.spi.PersistentMap;
|
||||
|
@ -22,6 +22,7 @@ import org.hibernate.sql.results.graph.DomainResultAssembler;
|
|||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -40,6 +41,10 @@ public class MapInitializer extends AbstractImmediateCollectionInitializer {
|
|||
private final DomainResultAssembler<?> mapKeyAssembler;
|
||||
private final DomainResultAssembler<?> mapValueAssembler;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #MapInitializer(NavigablePath, PluralAttributeMapping, InitializerParent, LockMode, DomainResult, DomainResult, boolean, AssemblerCreationState, Fetch, Fetch)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public MapInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping attributeMapping,
|
||||
|
@ -51,18 +56,43 @@ public class MapInitializer extends AbstractImmediateCollectionInitializer {
|
|||
Fetch mapValueFetch,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
this(
|
||||
navigablePath,
|
||||
attributeMapping,
|
||||
(InitializerParent) parentAccess,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
isResultInitializer,
|
||||
creationState,
|
||||
mapKeyFetch,
|
||||
mapValueFetch
|
||||
);
|
||||
}
|
||||
|
||||
public MapInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping attributeMapping,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState,
|
||||
Fetch mapKeyFetch,
|
||||
Fetch mapValueFetch) {
|
||||
super(
|
||||
navigablePath,
|
||||
attributeMapping,
|
||||
parentAccess,
|
||||
parent,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
);
|
||||
this.mapKeyAssembler = mapKeyFetch.createAssembler( this, creationState );
|
||||
this.mapValueAssembler = mapValueFetch.createAssembler( this, creationState );
|
||||
this.mapKeyAssembler = mapKeyFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
this.mapValueAssembler = mapValueFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -71,9 +101,16 @@ public class MapInitializer extends AbstractImmediateCollectionInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void forEachAssembler(Consumer<DomainResultAssembler<?>> consumer) {
|
||||
consumer.accept( mapKeyAssembler );
|
||||
consumer.accept( mapValueAssembler );
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
super.forEachSubInitializer( consumer, arg );
|
||||
final Initializer keyInitializer = mapKeyAssembler.getInitializer();
|
||||
if ( keyInitializer != null ) {
|
||||
consumer.accept( keyInitializer, arg );
|
||||
}
|
||||
final Initializer valueInitializer = mapValueAssembler.getInitializer();
|
||||
if ( valueInitializer != null ) {
|
||||
consumer.accept( valueInitializer, arg );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -108,15 +145,43 @@ public class MapInitializer extends AbstractImmediateCollectionInitializer {
|
|||
assert map != null;
|
||||
for ( Map.Entry<?, ?> entry : map.entrySet() ) {
|
||||
if ( keyInitializer != null ) {
|
||||
keyInitializer.initializeInstanceFromParent( entry.getKey(), rowProcessingState );
|
||||
keyInitializer.initializeInstanceFromParent( entry.getKey() );
|
||||
}
|
||||
if ( valueInitializer != null ) {
|
||||
valueInitializer.initializeInstanceFromParent( entry.getValue(), rowProcessingState );
|
||||
valueInitializer.initializeInstanceFromParent( entry.getValue() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resolveInstanceSubInitializers(RowProcessingState rowProcessingState) {
|
||||
final Initializer keyInitializer = mapKeyAssembler.getInitializer();
|
||||
final Initializer valueInitializer = mapValueAssembler.getInitializer();
|
||||
if ( keyInitializer != null || valueInitializer != null ) {
|
||||
final PersistentMap<?, ?> map = getCollectionInstance();
|
||||
assert map != null;
|
||||
for ( Map.Entry<?, ?> entry : map.entrySet() ) {
|
||||
if ( keyInitializer != null ) {
|
||||
keyInitializer.resolveInstance( entry.getKey() );
|
||||
}
|
||||
if ( valueInitializer != null ) {
|
||||
valueInitializer.resolveInstance( entry.getValue() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> getIndexAssembler() {
|
||||
return mapKeyAssembler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> getElementAssembler() {
|
||||
return mapValueAssembler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MapInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.hibernate.sql.results.graph.DomainResult;
|
|||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
|
||||
/**
|
||||
|
@ -50,7 +51,7 @@ public class MapInitializerProducer implements CollectionInitializerProducer {
|
|||
public CollectionInitializer produceInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping attribute,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
|
@ -59,14 +60,14 @@ public class MapInitializerProducer implements CollectionInitializerProducer {
|
|||
return new MapInitializer(
|
||||
navigablePath,
|
||||
mapDescriptor,
|
||||
parentAccess,
|
||||
parent,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
mapKeyFetch,
|
||||
mapValueFetch,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
creationState,
|
||||
mapKeyFetch,
|
||||
mapValueFetch
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,8 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
|||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
|
@ -46,11 +45,11 @@ public class SelectEagerCollectionFetch extends CollectionFetch {
|
|||
return false;
|
||||
}
|
||||
|
||||
public CollectionInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public CollectionInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new SelectEagerCollectionInitializer(
|
||||
getNavigablePath(),
|
||||
getFetchedMapping(),
|
||||
parentAccess,
|
||||
parent,
|
||||
collectionKeyDomainResult,
|
||||
creationState
|
||||
);
|
||||
|
|
|
@ -6,13 +6,15 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.collection.internal;
|
||||
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.internal.log.LoggingHelper;
|
||||
import org.hibernate.metamodel.CollectionClassification;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
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.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
|
@ -21,6 +23,10 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
*/
|
||||
public class SelectEagerCollectionInitializer extends AbstractCollectionInitializer {
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #SelectEagerCollectionInitializer(NavigablePath, PluralAttributeMapping, InitializerParent, DomainResult, AssemblerCreationState)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public SelectEagerCollectionInitializer(
|
||||
NavigablePath fetchedPath,
|
||||
PluralAttributeMapping fetchedMapping,
|
||||
|
@ -30,22 +36,39 @@ public class SelectEagerCollectionInitializer extends AbstractCollectionInitiali
|
|||
super( fetchedPath, fetchedMapping, parentAccess, collectionKeyResult, false, creationState );
|
||||
}
|
||||
|
||||
public SelectEagerCollectionInitializer(
|
||||
NavigablePath fetchedPath,
|
||||
PluralAttributeMapping fetchedMapping,
|
||||
InitializerParent parent,
|
||||
@Nullable DomainResult<?> collectionKeyResult,
|
||||
AssemblerCreationState creationState) {
|
||||
super( fetchedPath, fetchedMapping, parent, collectionKeyResult, false, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
public void resolveInstance() {
|
||||
resolveInstance( rowProcessingState, true );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
public void resolveInstance(@Nullable Object instance) {
|
||||
resolveInstance( instance, rowProcessingState, true );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
super.finishUpRow( rowProcessingState );
|
||||
// Chances are pretty low that we can reuse the collection key,
|
||||
// so set it to null in order to avoid an additional equals collection key comparison
|
||||
collectionKey = null;
|
||||
collectionInstance = null;
|
||||
public void initializeInstanceFromParent(Object parentInstance) {
|
||||
final Object instance = getInitializedPart().getValue( parentInstance );
|
||||
if ( collectionAttributeMapping.getCollectionDescriptor()
|
||||
.getCollectionSemantics()
|
||||
.getCollectionClassification() == CollectionClassification.ARRAY ) {
|
||||
collectionInstance = rowProcessingState.getSession().getPersistenceContextInternal()
|
||||
.getCollectionHolder( instance );
|
||||
}
|
||||
else {
|
||||
collectionInstance = (PersistentCollection<?>) instance;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
collectionInstance.forceInitialization();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
package org.hibernate.sql.results.graph.collection.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.collection.spi.PersistentSet;
|
||||
|
@ -21,6 +21,7 @@ import org.hibernate.sql.results.graph.DomainResultAssembler;
|
|||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -33,6 +34,10 @@ public class SetInitializer extends AbstractImmediateCollectionInitializer {
|
|||
|
||||
private final DomainResultAssembler<?> elementAssembler;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #SetInitializer(NavigablePath, PluralAttributeMapping, InitializerParent, LockMode, DomainResult, DomainResult, boolean, AssemblerCreationState, Fetch)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public SetInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping setDescriptor,
|
||||
|
@ -43,17 +48,40 @@ public class SetInitializer extends AbstractImmediateCollectionInitializer {
|
|||
Fetch elementFetch,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
this(
|
||||
navigablePath,
|
||||
setDescriptor,
|
||||
(InitializerParent) parentAccess,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
isResultInitializer,
|
||||
creationState,
|
||||
elementFetch
|
||||
);
|
||||
}
|
||||
|
||||
public SetInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping setDescriptor,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
boolean isResultInitializer,
|
||||
AssemblerCreationState creationState,
|
||||
Fetch elementFetch) {
|
||||
super(
|
||||
navigablePath,
|
||||
setDescriptor,
|
||||
parentAccess,
|
||||
parent,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
);
|
||||
this.elementAssembler = elementFetch.createAssembler( this, creationState );
|
||||
this.elementAssembler = elementFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,8 +90,12 @@ public class SetInitializer extends AbstractImmediateCollectionInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void forEachAssembler(Consumer<DomainResultAssembler<?>> consumer) {
|
||||
consumer.accept( elementAssembler );
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
super.forEachSubInitializer( consumer, arg );
|
||||
final Initializer initializer = elementAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -91,11 +123,33 @@ public class SetInitializer extends AbstractImmediateCollectionInitializer {
|
|||
final PersistentSet<?> set = getCollectionInstance();
|
||||
assert set != null;
|
||||
for ( Object element : set ) {
|
||||
initializer.initializeInstanceFromParent( element, rowProcessingState );
|
||||
initializer.initializeInstanceFromParent( element );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resolveInstanceSubInitializers(RowProcessingState rowProcessingState) {
|
||||
final Initializer initializer = elementAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
final PersistentSet<?> set = getCollectionInstance();
|
||||
assert set != null;
|
||||
for ( Object element : set ) {
|
||||
initializer.resolveInstance( element );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> getIndexAssembler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> getElementAssembler() {
|
||||
return elementAssembler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SetInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.hibernate.sql.results.graph.DomainResult;
|
|||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
|
||||
/**
|
||||
|
@ -47,7 +48,7 @@ public class SetInitializerProducer implements CollectionInitializerProducer {
|
|||
public CollectionInitializer produceInitializer(
|
||||
NavigablePath navigablePath,
|
||||
PluralAttributeMapping attribute,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
LockMode lockMode,
|
||||
DomainResult<?> collectionKeyResult,
|
||||
DomainResult<?> collectionValueKeyResult,
|
||||
|
@ -56,13 +57,13 @@ public class SetInitializerProducer implements CollectionInitializerProducer {
|
|||
return new SetInitializer(
|
||||
navigablePath,
|
||||
setDescriptor,
|
||||
parentAccess,
|
||||
parent,
|
||||
lockMode,
|
||||
collectionKeyResult,
|
||||
collectionValueKeyResult,
|
||||
elementFetch,
|
||||
isResultInitializer,
|
||||
creationState
|
||||
creationState,
|
||||
elementFetch
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,510 +0,0 @@
|
|||
/*
|
||||
* 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.embeddable;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.VirtualModelPart;
|
||||
import org.hibernate.metamodel.spi.ValueAccess;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.property.access.spi.Setter;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
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.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.internal.NullValueAssembler;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentAccess
|
||||
implements EmbeddableInitializer, ValueAccess {
|
||||
|
||||
private final NavigablePath navigablePath;
|
||||
private final EmbeddableValuedModelPart embedded;
|
||||
private final FetchParentAccess fetchParentAccess;
|
||||
private final FetchParentAccess owningParent;
|
||||
private final EntityMappingType ownedModelPartDeclaringType;
|
||||
private final boolean isPartOfKey;
|
||||
private final boolean createEmptyCompositesEnabled;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
protected final DomainResultAssembler<?>[] assemblers;
|
||||
private final BasicResultAssembler<?> discriminatorAssembler;
|
||||
|
||||
// per-row state
|
||||
private final Object[] rowState;
|
||||
private State state = State.INITIAL;
|
||||
protected Object compositeInstance;
|
||||
private Object discriminatorValue;
|
||||
private RowProcessingState wrappedProcessingState;
|
||||
|
||||
public AbstractEmbeddableInitializer(
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
FetchParentAccess parentAccess,
|
||||
BasicFetch<?> discriminatorFetch,
|
||||
AssemblerCreationState creationState) {
|
||||
this.navigablePath = resultDescriptor.getNavigablePath();
|
||||
this.embedded = resultDescriptor.getReferencedMappingContainer();
|
||||
this.fetchParentAccess = parentAccess;
|
||||
|
||||
final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
|
||||
final int size = embeddableTypeDescriptor.getNumberOfFetchables();
|
||||
this.rowState = new Object[ size ];
|
||||
|
||||
this.isPartOfKey = embedded.isEntityIdentifierMapping() || Initializer.isPartOfKey( navigablePath, parentAccess );
|
||||
this.owningParent = FetchParentAccess.determineOwningParent( parentAccess );
|
||||
this.ownedModelPartDeclaringType = FetchParentAccess.determineOwnedModelPartDeclaringType( embedded, parentAccess, owningParent );
|
||||
// We never want to create empty composites for the FK target or PK, otherwise collections would break
|
||||
this.createEmptyCompositesEnabled = !isPartOfKey && embeddableTypeDescriptor.isCreateEmptyCompositesEnabled();
|
||||
this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
|
||||
this.assemblers = createAssemblers( resultDescriptor, creationState, embeddableTypeDescriptor );
|
||||
discriminatorAssembler = discriminatorFetch != null ?
|
||||
(BasicResultAssembler<?>) discriminatorFetch.createAssembler( parentAccess, creationState ) :
|
||||
null;
|
||||
}
|
||||
|
||||
protected DomainResultAssembler<?>[] createAssemblers(
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
AssemblerCreationState creationState,
|
||||
EmbeddableMappingType embeddableTypeDescriptor) {
|
||||
final int size = embeddableTypeDescriptor.getNumberOfFetchables();
|
||||
final DomainResultAssembler<?>[] assemblers = new DomainResultAssembler[size];
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
final Fetchable stateArrayContributor = embeddableTypeDescriptor.getFetchable( i );
|
||||
final Fetch fetch = resultDescriptor.findFetch( stateArrayContributor );
|
||||
|
||||
final DomainResultAssembler<?> stateAssembler = fetch == null
|
||||
? new NullValueAssembler<>( stateArrayContributor.getJavaType() )
|
||||
: fetch.createAssembler( this, creationState );
|
||||
|
||||
assemblers[i] = stateAssembler;
|
||||
}
|
||||
return assemblers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableValuedModelPart getInitializedPart() {
|
||||
return embedded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParentAccess getFetchParentAccess() {
|
||||
return fetchParentAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FetchParentAccess getOwningParent() {
|
||||
return owningParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable EntityMappingType getOwnedModelPartDeclaringType() {
|
||||
return ownedModelPartDeclaringType;
|
||||
}
|
||||
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompositeInstance() {
|
||||
return state == State.NULL ? null : compositeInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartOfKey() {
|
||||
return isPartOfKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParentAccess findFirstEntityDescriptorAccess() {
|
||||
if ( fetchParentAccess == null || embedded instanceof CollectionPart ) {
|
||||
return null;
|
||||
}
|
||||
return fetchParentAccess.findFirstEntityDescriptorAccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityInitializer findFirstEntityInitializer() {
|
||||
final FetchParentAccess firstEntityDescriptorAccess = findFirstEntityDescriptorAccess();
|
||||
if ( firstEntityDescriptorAccess == null ) {
|
||||
return null;
|
||||
}
|
||||
return firstEntityDescriptorAccess.findFirstEntityInitializer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState processingState) {
|
||||
// We need to possibly wrap the processing state if the embeddable is within an aggregate
|
||||
if ( wrappedProcessingState == null ) {
|
||||
wrappedProcessingState = wrapProcessingState( processingState );
|
||||
}
|
||||
if ( discriminatorAssembler != null ) {
|
||||
final EmbeddableDiscriminatorMapping discriminatorMapping = embedded.getEmbeddableTypeDescriptor()
|
||||
.getDiscriminatorMapping();
|
||||
assert discriminatorMapping != null;
|
||||
discriminatorValue = discriminatorAssembler.extractRawValue( wrappedProcessingState );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState processingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState processingState) {
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Initializing composite instance [%s]", navigablePath );
|
||||
|
||||
// IMPORTANT: This method might be called multiple times for the same role for a single row.
|
||||
// EmbeddableAssembler calls it as part of its `#assemble` and the RowReader calls it
|
||||
// as part of its normal Initializer handling
|
||||
//
|
||||
// Unfortunately, as currently structured, we need this double call mainly to handle
|
||||
// the case composite keys, especially those with key-m-1 refs.
|
||||
//
|
||||
// When we are processing a non-key embeddable, all initialization happens in
|
||||
// the first call, and we can safely ignore the second call.
|
||||
//
|
||||
// When we are processing a composite key, we really need to react to both calls.
|
||||
//
|
||||
// Unfortunately, atm, because we reuse `EmbeddedAttributeMapping` in a few of these
|
||||
// foreign-key scenarios, we cannot easily tell when one models a key or not.
|
||||
//
|
||||
// While this is "important" to be able to avoid extra handling, it is really only
|
||||
// critical in the case we have custom constructor injection. Luckily, custom instantiation
|
||||
// is only allowed for non-key usage atm, so we leverage that distinction here
|
||||
|
||||
switch ( state ) {
|
||||
case NULL:
|
||||
return;
|
||||
case INITIAL:
|
||||
state = determinInitialState( processingState );
|
||||
if ( state != State.INITIAL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
extractRowState( wrappedProcessingState );
|
||||
prepareCompositeInstance( wrappedProcessingState );
|
||||
if ( state == State.NULL ) {
|
||||
return;
|
||||
}
|
||||
notifyResolutionListeners( compositeInstance );
|
||||
case EXTRACTED:
|
||||
if ( embedded.getParentInjectionAttributePropertyAccess() != null || embedded instanceof VirtualModelPart ) {
|
||||
handleParentInjection();
|
||||
|
||||
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 ( fetchParentAccess != null ) {
|
||||
fetchParentAccess.registerResolutionListener( entity -> {
|
||||
embedded.getEmbeddableTypeDescriptor().setValues( entity, rowState );
|
||||
state = State.INJECTED;
|
||||
} );
|
||||
}
|
||||
else {
|
||||
// At this point, createEmptyCompositesEnabled is always true, so we generate
|
||||
// the composite instance.
|
||||
//
|
||||
// NOTE: `valuesAccess` is set to null to indicate that all values are null,
|
||||
// as opposed to returning the all-null value array. the instantiator
|
||||
// interprets that as the values are not known or were all null.
|
||||
final Object target = embedded.getEmbeddableTypeDescriptor().getRepresentationStrategy()
|
||||
.getInstantiator()
|
||||
.instantiate( this, sessionFactory );
|
||||
state = State.INJECTED;
|
||||
lazyInitializer.setImplementation( target );
|
||||
}
|
||||
}
|
||||
else {
|
||||
embedded.getEmbeddableTypeDescriptor().setValues( compositeInstance, rowState );
|
||||
state = State.INJECTED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
state = State.INJECTED;
|
||||
}
|
||||
case INJECTED:
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstanceFromParent(Object parentInstance, RowProcessingState rowProcessingState) {
|
||||
final AttributeMapping attributeMapping = getInitializedPart().asAttributeMapping();
|
||||
final Object instance = attributeMapping != null
|
||||
? attributeMapping.getValue( parentInstance )
|
||||
: parentInstance;
|
||||
compositeInstance = instance;
|
||||
state = State.INJECTED;
|
||||
if ( instance != null ) {
|
||||
for ( DomainResultAssembler<?> assembler : assemblers ) {
|
||||
final Initializer initializer = assembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializer.initializeInstanceFromParent( instance, rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareCompositeInstance(RowProcessingState processingState) {
|
||||
// Virtual model parts use the owning entity as container which the fetch parent access provides.
|
||||
// For an identifier or foreign key this is called during the resolveKey phase of the fetch parent,
|
||||
// so we can't use the fetch parent access in that case.
|
||||
if ( fetchParentAccess != null && embedded instanceof VirtualModelPart && !isPartOfKey ) {
|
||||
fetchParentAccess.resolveInstance( processingState );
|
||||
compositeInstance = fetchParentAccess.getInitializedInstance();
|
||||
EntityInitializer entityInitializer = fetchParentAccess.asEntityInitializer();
|
||||
if ( entityInitializer != null && entityInitializer.isEntityInitialized() ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( compositeInstance == null ) {
|
||||
compositeInstance = createCompositeInstance(
|
||||
discriminatorValue,
|
||||
navigablePath,
|
||||
sessionFactory
|
||||
);
|
||||
}
|
||||
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf(
|
||||
"Created composite instance [%s]",
|
||||
navigablePath
|
||||
);
|
||||
}
|
||||
|
||||
private State determinInitialState(RowProcessingState rowProcessingState) {
|
||||
if ( isPartOfKey || isResultInitializer() ) {
|
||||
assert !isParentShallowCached();
|
||||
return State.INITIAL;
|
||||
}
|
||||
if ( isParentShallowCached() || shouldSkipInitializer( rowProcessingState ) ) {
|
||||
return State.NULL;
|
||||
}
|
||||
return State.INITIAL;
|
||||
}
|
||||
|
||||
private void extractRowState(RowProcessingState processingState) {
|
||||
boolean stateAllNull = true;
|
||||
for ( int i = 0; i < assemblers.length; i++ ) {
|
||||
final DomainResultAssembler<?> assembler = assemblers[i];
|
||||
final Object contributorValue = assembler.assemble(
|
||||
processingState,
|
||||
processingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
|
||||
);
|
||||
|
||||
if ( contributorValue == BATCH_PROPERTY ) {
|
||||
rowState[i] = null;
|
||||
}
|
||||
else {
|
||||
rowState[i] = contributorValue;
|
||||
}
|
||||
if ( contributorValue != null ) {
|
||||
stateAllNull = false;
|
||||
}
|
||||
else if ( isPartOfKey ) {
|
||||
// If this is a foreign key and there is a null part, the whole thing has to be turned into null
|
||||
stateAllNull = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state = stateAllNull ? State.NULL : State.EXTRACTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveState(RowProcessingState rowProcessingState) {
|
||||
if ( determinInitialState( rowProcessingState ) == State.INITIAL ) {
|
||||
for ( final DomainResultAssembler<?> assembler : assemblers ) {
|
||||
assembler.resolveState( rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object createCompositeInstance(
|
||||
Object discriminatorValue,
|
||||
NavigablePath navigablePath,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
if ( state == State.NULL ) {
|
||||
// todo (6.0) : should we initialize the composite instance if it has a parent attribute?
|
||||
// if ( !createEmptyCompositesEnabled && embedded.getParentInjectionAttributePropertyAccess() == null ) {
|
||||
if ( !createEmptyCompositesEnabled ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final Object instance = embedded.getEmbeddableTypeDescriptor()
|
||||
.getRepresentationStrategy()
|
||||
.getInstantiatorForDiscriminator( discriminatorValue )
|
||||
.instantiate( this, sessionFactory );
|
||||
state = State.EXTRACTED;
|
||||
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Created composite instance [%s] : %s", navigablePath, instance );
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getValues() {
|
||||
return state == State.NULL ? null : rowState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getValue(int i, Class<T> clazz) {
|
||||
return state == State.NULL ? null : clazz.cast( rowState[i] );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOwner() {
|
||||
return fetchParentAccess.getInitializedInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDiscriminatorValue() {
|
||||
return discriminatorValue;
|
||||
}
|
||||
|
||||
private void handleParentInjection() {
|
||||
final PropertyAccess parentInjectionAccess = embedded.getParentInjectionAttributePropertyAccess();
|
||||
if ( parentInjectionAccess == null ) {
|
||||
// embeddable defined no parent injection
|
||||
return;
|
||||
}
|
||||
|
||||
final Initializer parentInitializer = determineParentInjectionInitializer();
|
||||
final Object parent = determineParentInstance( parentInitializer );
|
||||
if ( parent == null ) {
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf(
|
||||
"Unable to determine parent for injection into embeddable [%s]",
|
||||
navigablePath
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf(
|
||||
"Injecting parent into embeddable [%s] : `%s` -> `%s`",
|
||||
navigablePath,
|
||||
parent,
|
||||
compositeInstance
|
||||
);
|
||||
|
||||
|
||||
final Setter setter = parentInjectionAccess.getSetter();
|
||||
assert setter != null;
|
||||
|
||||
final EntityInitializer entityInitializer;
|
||||
if ( ( entityInitializer = findFirstEntityInitializer( parentInitializer ) ) != null ) {
|
||||
entityInitializer.registerResolutionListener(
|
||||
o -> setter.set( compositeInstance, o )
|
||||
);
|
||||
}
|
||||
else {
|
||||
setter.set( compositeInstance, parent );
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable EntityInitializer findFirstEntityInitializer(Initializer initializer) {
|
||||
final EntityInitializer entityInitializer = initializer.asEntityInitializer();
|
||||
if ( entityInitializer != null ) {
|
||||
return entityInitializer;
|
||||
}
|
||||
assert initializer.isCollectionInitializer();
|
||||
return ( (CollectionInitializer) initializer ).findFirstEntityInitializer();
|
||||
}
|
||||
|
||||
private Initializer determineParentInjectionInitializer() {
|
||||
// Try to find the first non-embeddable fetch parent access
|
||||
// todo (6.x) - allow injection of containing composite as parent if
|
||||
// it is the direct parent
|
||||
FetchParentAccess parentAccess = fetchParentAccess;
|
||||
while ( parentAccess != null ) {
|
||||
if ( !parentAccess.isEmbeddableInitializer() ) {
|
||||
return parentAccess;
|
||||
}
|
||||
parentAccess = parentAccess.getFetchParentAccess();
|
||||
}
|
||||
throw new UnsupportedOperationException( "Injection of parent instance into embeddable result is not possible" );
|
||||
}
|
||||
|
||||
private Object determineParentInstance(Initializer parentInitializer) {
|
||||
if ( parentInitializer == null ) {
|
||||
throw new UnsupportedOperationException( "Cannot determine Embeddable: " + navigablePath + " parent instance, parent initializer is null" );
|
||||
}
|
||||
|
||||
if ( parentInitializer.isCollectionInitializer() ) {
|
||||
return ( (CollectionInitializer) parentInitializer ).getCollectionInstance().getOwner();
|
||||
}
|
||||
|
||||
final EntityInitializer parentEntityInitializer = parentInitializer.asEntityInitializer();
|
||||
if ( parentEntityInitializer != null ) {
|
||||
return parentEntityInitializer.getInitializedInstance();
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException( "The Embeddable: " + navigablePath + " parent initializer is neither an instance of an EntityInitializer nor of a CollectionInitializer" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markShallowCached() {
|
||||
assert !isPartOfKey;
|
||||
super.markShallowCached();
|
||||
markSubInitializersAsShallowCached();
|
||||
}
|
||||
|
||||
private void markSubInitializersAsShallowCached() {
|
||||
for ( DomainResultAssembler<?> assembler : assemblers ) {
|
||||
final Initializer initializer = assembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializer.markShallowCached();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
compositeInstance = null;
|
||||
discriminatorValue = null;
|
||||
state = State.INITIAL;
|
||||
wrappedProcessingState = null;
|
||||
|
||||
clearResolutionListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "(" + navigablePath + ") : `" + getInitializedPart().getJavaType().getJavaTypeClass() + "`";
|
||||
}
|
||||
enum State {
|
||||
INITIAL,
|
||||
EXTRACTED,
|
||||
NULL,
|
||||
INJECTED
|
||||
}
|
||||
}
|
|
@ -9,8 +9,11 @@ package org.hibernate.sql.results.graph.embeddable;
|
|||
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Special initializer contract for embeddables
|
||||
*
|
||||
|
@ -22,17 +25,15 @@ public interface EmbeddableInitializer extends FetchParentAccess {
|
|||
|
||||
Object getCompositeInstance();
|
||||
|
||||
FetchParentAccess getFetchParentAccess();
|
||||
/**
|
||||
* @deprecated Use {@link #getParent()} instead
|
||||
*/
|
||||
@Override
|
||||
@Deprecated(forRemoval = true)
|
||||
@Nullable FetchParentAccess getFetchParentAccess();
|
||||
|
||||
default RowProcessingState wrapProcessingState(RowProcessingState processingState) {
|
||||
final FetchParentAccess fetchParentAccess = getFetchParentAccess();
|
||||
if ( fetchParentAccess != null ) {
|
||||
if ( fetchParentAccess.isEmbeddableInitializer() ) {
|
||||
return ( fetchParentAccess.asEmbeddableInitializer() ).wrapProcessingState( processingState );
|
||||
}
|
||||
}
|
||||
return processingState;
|
||||
}
|
||||
@Override
|
||||
@Nullable InitializerParent getParent();
|
||||
|
||||
@Override
|
||||
default Object getInitializedInstance() {
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.hibernate.sql.results.graph.Fetch;
|
|||
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.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
||||
|
@ -160,24 +161,32 @@ public class AggregateEmbeddableFetchImpl extends AbstractFetchParent
|
|||
public DomainResultAssembler createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parentAccess, this ).asEmbeddableInitializer() );
|
||||
return createAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler createAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parent, this ).asEmbeddableInitializer() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableInitializer createInitializer(
|
||||
AggregateEmbeddableFetchImpl resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
return new AggregateEmbeddableFetchInitializer(
|
||||
parentAccess,
|
||||
public EmbeddableInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new AggregateEmbeddableInitializerImpl(
|
||||
this,
|
||||
discriminatorFetch,
|
||||
parent,
|
||||
creationState,
|
||||
false,
|
||||
aggregateSelection
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.sql.results.graph.embeddable.internal;
|
|||
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
|
@ -29,10 +30,10 @@ public interface AggregateEmbeddableInitializer extends EmbeddableInitializer {
|
|||
}
|
||||
|
||||
static int[] determineAggregateValuesArrayPositions(
|
||||
FetchParentAccess fetchParentAccess,
|
||||
InitializerParent parent,
|
||||
SqlSelection structSelection) {
|
||||
if ( fetchParentAccess instanceof AggregateEmbeddableInitializer ) {
|
||||
final int[] parentAggregateValuesArrayPositions = ( (AggregateEmbeddableInitializer) fetchParentAccess ).getAggregateValuesArrayPositions();
|
||||
if ( parent instanceof AggregateEmbeddableInitializer ) {
|
||||
final int[] parentAggregateValuesArrayPositions = ( (AggregateEmbeddableInitializer) parent ).getAggregateValuesArrayPositions();
|
||||
final int[] aggregateValuesArrayPositions = new int[parentAggregateValuesArrayPositions.length + 1];
|
||||
System.arraycopy(
|
||||
parentAggregateValuesArrayPositions,
|
||||
|
@ -44,9 +45,8 @@ public interface AggregateEmbeddableInitializer extends EmbeddableInitializer {
|
|||
aggregateValuesArrayPositions[aggregateValuesArrayPositions.length - 1] = structSelection.getValuesArrayPosition();
|
||||
return aggregateValuesArrayPositions;
|
||||
}
|
||||
else if ( fetchParentAccess instanceof EmbeddableInitializer ) {
|
||||
final FetchParentAccess parentFetchParentAccess = fetchParentAccess.getFetchParentAccess();
|
||||
return determineAggregateValuesArrayPositions( parentFetchParentAccess, structSelection );
|
||||
else if ( parent instanceof EmbeddableInitializer ) {
|
||||
return determineAggregateValuesArrayPositions( parent.getParent(), structSelection );
|
||||
}
|
||||
return new int[] { structSelection.getValuesArrayPosition() };
|
||||
}
|
||||
|
|
|
@ -10,49 +10,56 @@ import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
|||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
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.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
/**
|
||||
* An initializer for an embeddable that is mapped as aggregate e.g. STRUCT, JSON or XML.
|
||||
* The aggregate selection reads an Object[] from JDBC which serves as data for the nested {@link org.hibernate.sql.results.graph.DomainResultAssembler}.
|
||||
* The aggregate selection reads an Object[] from JDBC which serves as data for the nested {@link DomainResultAssembler}.
|
||||
* This class exposes the Object[] of the aggregate to the nested assemblers through a wrapping {@link RowProcessingState}.
|
||||
*/
|
||||
public class AggregateEmbeddableFetchInitializer extends AbstractEmbeddableInitializer implements AggregateEmbeddableInitializer {
|
||||
public class AggregateEmbeddableInitializerImpl extends EmbeddableInitializerImpl implements AggregateEmbeddableInitializer {
|
||||
|
||||
private final int[] aggregateValuesArrayPositions;
|
||||
private NestedRowProcessingState nestedRowProcessingState;
|
||||
|
||||
public AggregateEmbeddableFetchInitializer(
|
||||
FetchParentAccess fetchParentAccess,
|
||||
public AggregateEmbeddableInitializerImpl(
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
BasicFetch<?> discriminatorFetch,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState,
|
||||
boolean isResultInitializer,
|
||||
SqlSelection structSelection) {
|
||||
super( resultDescriptor, fetchParentAccess, discriminatorFetch, creationState );
|
||||
super( resultDescriptor, discriminatorFetch, parent, creationState, isResultInitializer );
|
||||
this.aggregateValuesArrayPositions = AggregateEmbeddableInitializer.determineAggregateValuesArrayPositions(
|
||||
fetchParentAccess,
|
||||
parent,
|
||||
structSelection
|
||||
);
|
||||
final DomainResultAssembler<?>[] assemblers = super.createAssemblers(
|
||||
final DomainResultAssembler<?>[][] assemblers = super.createAssemblers(
|
||||
resultDescriptor,
|
||||
creationState,
|
||||
resultDescriptor.getReferencedMappingType()
|
||||
);
|
||||
System.arraycopy( assemblers, 0, this.assemblers, 0, assemblers.length );
|
||||
final Initializer[][] initializers = createInitializers( assemblers );
|
||||
System.arraycopy( initializers, 0, this.subInitializers, 0, initializers.length );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DomainResultAssembler<?>[] createAssemblers(
|
||||
public void startLoading(RowProcessingState rowProcessingState) {
|
||||
super.startLoading( NestedRowProcessingState.wrap( this, rowProcessingState ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DomainResultAssembler<?>[][] createAssemblers(
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
AssemblerCreationState creationState,
|
||||
EmbeddableMappingType embeddableTypeDescriptor) {
|
||||
// Return just the assemblers array here without elements,
|
||||
// as we initialize the array in the constructor after the aggregateValuesArrayPositions is set
|
||||
return new DomainResultAssembler[embeddableTypeDescriptor.getNumberOfFetchables()];
|
||||
return new DomainResultAssembler[embeddableTypeDescriptor.isPolymorphic() ? embeddableTypeDescriptor.getConcreteEmbeddableTypes().size() : 1][];
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,22 +67,4 @@ public class AggregateEmbeddableFetchInitializer extends AbstractEmbeddableIniti
|
|||
return aggregateValuesArrayPositions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
return findFirstEntityDescriptorAccess().getParentKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultInitializer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NestedRowProcessingState wrapProcessingState(RowProcessingState processingState) {
|
||||
if ( nestedRowProcessingState != null ) {
|
||||
return nestedRowProcessingState;
|
||||
}
|
||||
return nestedRowProcessingState = NestedRowProcessingState.wrap( this, processingState );
|
||||
}
|
||||
|
||||
}
|
|
@ -29,6 +29,7 @@ import org.hibernate.sql.results.graph.Fetch;
|
|||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResult;
|
||||
|
@ -155,25 +156,33 @@ public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implem
|
|||
public DomainResultAssembler<T> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<T> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
//noinspection unchecked
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parentAccess, this ).asEmbeddableInitializer() );
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parent, this ).asEmbeddableInitializer() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
AggregateEmbeddableResultImpl<T> resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
return new AggregateEmbeddableResultInitializer(
|
||||
parentAccess,
|
||||
public Initializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new AggregateEmbeddableInitializerImpl(
|
||||
this,
|
||||
discriminatorFetch,
|
||||
parent,
|
||||
creationState,
|
||||
true,
|
||||
aggregateSelection
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* 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.embeddable.internal;
|
||||
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
/**
|
||||
* An initializer for an embeddable that is mapped as aggregate e.g. STRUCT, JSON or XML.
|
||||
* The aggregate selection reads an Object[] from JDBC which serves as data for the nested {@link org.hibernate.sql.results.graph.DomainResultAssembler}.
|
||||
* This class exposes the Object[] of the aggregate to the nested assemblers through a wrapping {@link RowProcessingState}.
|
||||
*/
|
||||
public class AggregateEmbeddableResultInitializer extends AbstractEmbeddableInitializer implements AggregateEmbeddableInitializer {
|
||||
|
||||
private final int[] aggregateValuesArrayPositions;
|
||||
private NestedRowProcessingState nestedRowProcessingState;
|
||||
|
||||
public AggregateEmbeddableResultInitializer(
|
||||
FetchParentAccess fetchParentAccess,
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
BasicFetch<?> discriminatorFetch,
|
||||
AssemblerCreationState creationState,
|
||||
SqlSelection structSelection) {
|
||||
super( resultDescriptor, fetchParentAccess, discriminatorFetch, creationState );
|
||||
this.aggregateValuesArrayPositions = AggregateEmbeddableInitializer.determineAggregateValuesArrayPositions(
|
||||
fetchParentAccess,
|
||||
structSelection
|
||||
);
|
||||
final DomainResultAssembler<?>[] assemblers = super.createAssemblers(
|
||||
resultDescriptor,
|
||||
creationState,
|
||||
resultDescriptor.getReferencedMappingType()
|
||||
);
|
||||
System.arraycopy( assemblers, 0, this.assemblers, 0, assemblers.length );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DomainResultAssembler<?>[] createAssemblers(
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
AssemblerCreationState creationState,
|
||||
EmbeddableMappingType embeddableTypeDescriptor) {
|
||||
// Return just the assemblers array here without elements,
|
||||
// as we initialize the array in the constructor after the aggregateValuesArrayPositions is set
|
||||
return new DomainResultAssembler[embeddableTypeDescriptor.getNumberOfFetchables()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getAggregateValuesArrayPositions() {
|
||||
return aggregateValuesArrayPositions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
return findFirstEntityDescriptorAccess().getParentKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultInitializer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NestedRowProcessingState wrapProcessingState(RowProcessingState processingState) {
|
||||
if ( nestedRowProcessingState != null ) {
|
||||
return nestedRowProcessingState;
|
||||
}
|
||||
return nestedRowProcessingState = NestedRowProcessingState.wrap( this, processingState );
|
||||
}
|
||||
|
||||
}
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.embeddable.internal;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
|
@ -29,9 +31,7 @@ public class EmbeddableAssembler implements DomainResultAssembler {
|
|||
|
||||
@Override
|
||||
public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
|
||||
initializer.resolveKey( rowProcessingState );
|
||||
initializer.resolveInstance( rowProcessingState );
|
||||
initializer.initializeInstance( rowProcessingState );
|
||||
initializer.resolveInstance();
|
||||
return initializer.getCompositeInstance();
|
||||
}
|
||||
|
||||
|
@ -46,4 +46,11 @@ public class EmbeddableAssembler implements DomainResultAssembler {
|
|||
public EmbeddableInitializer getInitializer() {
|
||||
return initializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachResultAssembler(BiConsumer consumer, Object arg) {
|
||||
if ( initializer.isResultInitializer() ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.hibernate.sql.results.graph.DomainResultCreationState;
|
|||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResult;
|
||||
|
@ -126,20 +127,27 @@ public class EmbeddableExpressionResultImpl<T> extends AbstractFetchParent imple
|
|||
public DomainResultAssembler<T> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<T> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
//noinspection unchecked
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parentAccess, this ).asEmbeddableInitializer() );
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parent, this ).asEmbeddableInitializer() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
EmbeddableExpressionResultImpl<T> resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
return new EmbeddableResultInitializer( this, parentAccess, null, creationState );
|
||||
public Initializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new EmbeddableInitializerImpl( this, null, parent, creationState, true );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,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.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
||||
|
@ -153,20 +154,27 @@ public class EmbeddableFetchImpl extends AbstractFetchParent
|
|||
public DomainResultAssembler createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parentAccess, this ).asEmbeddableInitializer() );
|
||||
return createAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> createAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parent, this ).asEmbeddableInitializer() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
EmbeddableFetchImpl resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
return new EmbeddableFetchInitializer( parentAccess, this, discriminatorFetch, creationState );
|
||||
public EmbeddableInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new EmbeddableInitializerImpl( this, discriminatorFetch, parent, creationState, true );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* 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.embeddable.internal;
|
||||
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EmbeddableFetchInitializer
|
||||
extends AbstractEmbeddableInitializer {
|
||||
public EmbeddableFetchInitializer(
|
||||
FetchParentAccess fetchParentAccess,
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
BasicFetch<?> discriminatorFetch,
|
||||
AssemblerCreationState creationState) {
|
||||
super( resultDescriptor, fetchParentAccess, discriminatorFetch, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
return findFirstEntityDescriptorAccess().getParentKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultInitializer() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,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.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||
|
@ -100,23 +101,30 @@ public class EmbeddableForeignKeyResultImpl<T>
|
|||
public DomainResultAssembler<T> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<T> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
//noinspection unchecked
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parentAccess, this ).asEmbeddableInitializer() );
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parent, this ).asEmbeddableInitializer() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
EmbeddableForeignKeyResultImpl<T> resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public EmbeddableInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return getReferencedModePart() instanceof NonAggregatedIdentifierMapping
|
||||
? new NonAggregatedIdentifierMappingResultInitializer( this, null, creationState )
|
||||
: new EmbeddableResultInitializer( this, null, null, creationState );
|
||||
? new NonAggregatedIdentifierMappingInitializer( this, null, creationState, true )
|
||||
: new EmbeddableInitializerImpl( this, null, null, creationState, true );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,518 @@
|
|||
/*
|
||||
* 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.embeddable.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.VirtualModelPart;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.metamodel.spi.ValueAccess;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.property.access.spi.Setter;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableLoadingLogger;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.graph.internal.AbstractInitializer;
|
||||
import org.hibernate.sql.results.internal.NullValueAssembler;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EmbeddableInitializerImpl extends AbstractInitializer
|
||||
implements EmbeddableInitializer, ValueAccess {
|
||||
|
||||
private final NavigablePath navigablePath;
|
||||
private final EmbeddableValuedModelPart embedded;
|
||||
private final InitializerParent parent;
|
||||
private final boolean isResultInitializer;
|
||||
private final boolean isPartOfKey;
|
||||
private final boolean createEmptyCompositesEnabled;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
protected final DomainResultAssembler<?>[][] assemblers;
|
||||
private final BasicResultAssembler<?> discriminatorAssembler;
|
||||
protected final Initializer[][] subInitializers;
|
||||
|
||||
// per-row state
|
||||
private final Object[] rowState;
|
||||
protected Object compositeInstance;
|
||||
protected EmbeddableMappingType.ConcreteEmbeddableType concreteEmbeddableType;
|
||||
|
||||
public EmbeddableInitializerImpl(
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
BasicFetch<?> discriminatorFetch,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState,
|
||||
boolean isResultInitializer) {
|
||||
this.navigablePath = resultDescriptor.getNavigablePath();
|
||||
this.embedded = resultDescriptor.getReferencedMappingContainer();
|
||||
this.parent = parent;
|
||||
this.isResultInitializer = isResultInitializer;
|
||||
|
||||
final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
|
||||
final int size = embeddableTypeDescriptor.getNumberOfFetchables();
|
||||
this.rowState = new Object[ size ];
|
||||
|
||||
this.isPartOfKey = embedded.isEntityIdentifierMapping() || Initializer.isPartOfKey( navigablePath, parent );
|
||||
// We never want to create empty composites for the FK target or PK, otherwise collections would break
|
||||
this.createEmptyCompositesEnabled = !isPartOfKey && embeddableTypeDescriptor.isCreateEmptyCompositesEnabled();
|
||||
this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
|
||||
this.assemblers = createAssemblers(
|
||||
resultDescriptor,
|
||||
creationState,
|
||||
embeddableTypeDescriptor
|
||||
);
|
||||
this.discriminatorAssembler = discriminatorFetch != null ?
|
||||
(BasicResultAssembler<?>) discriminatorFetch.createAssembler( this, creationState ) :
|
||||
null;
|
||||
this.subInitializers = createInitializers( assemblers );
|
||||
}
|
||||
|
||||
protected DomainResultAssembler<?>[][] createAssemblers(
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
AssemblerCreationState creationState,
|
||||
EmbeddableMappingType embeddableTypeDescriptor) {
|
||||
final int size = embeddableTypeDescriptor.getNumberOfFetchables();
|
||||
final DomainResultAssembler<?>[] overallAssemblers = new DomainResultAssembler[size];
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
final Fetchable stateArrayContributor = embeddableTypeDescriptor.getFetchable( i );
|
||||
final Fetch fetch = resultDescriptor.findFetch( stateArrayContributor );
|
||||
|
||||
final DomainResultAssembler<?> stateAssembler = fetch == null
|
||||
? new NullValueAssembler<>( stateArrayContributor.getJavaType() )
|
||||
: fetch.createAssembler( (InitializerParent) this, creationState );
|
||||
|
||||
overallAssemblers[i] = stateAssembler;
|
||||
}
|
||||
if ( embeddableTypeDescriptor.isPolymorphic() ) {
|
||||
final Collection<EmbeddableMappingType.ConcreteEmbeddableType> concreteEmbeddableTypes = embeddableTypeDescriptor.getConcreteEmbeddableTypes();
|
||||
final DomainResultAssembler<?>[][] assemblers = new DomainResultAssembler[concreteEmbeddableTypes.size()][];
|
||||
for ( EmbeddableMappingType.ConcreteEmbeddableType concreteEmbeddableType : concreteEmbeddableTypes ) {
|
||||
final DomainResultAssembler<?>[] subAssemblers = new DomainResultAssembler[overallAssemblers.length];
|
||||
for ( int i = 0; i < overallAssemblers.length; i++ ) {
|
||||
if ( concreteEmbeddableType.declaresAttribute( i ) ) {
|
||||
subAssemblers[i] = overallAssemblers[i];
|
||||
}
|
||||
}
|
||||
assemblers[concreteEmbeddableType.getSubclassId()] = subAssemblers;
|
||||
}
|
||||
return assemblers;
|
||||
}
|
||||
return new DomainResultAssembler[][] { overallAssemblers };
|
||||
}
|
||||
|
||||
protected static Initializer[][] createInitializers(DomainResultAssembler<?>[][] assemblers) {
|
||||
Initializer[][] subInitializers = new Initializer[assemblers.length][];
|
||||
for ( int i = 0; i < assemblers.length; i++ ) {
|
||||
final DomainResultAssembler<?>[] subAssemblers = assemblers[i];
|
||||
if ( subAssemblers != null ) {
|
||||
final ArrayList<Initializer> initializers = new ArrayList<>( subAssemblers.length );
|
||||
for ( DomainResultAssembler<?> assembler : subAssemblers ) {
|
||||
if ( assembler != null ) {
|
||||
final Initializer initializer = assembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializers.add( initializer );
|
||||
}
|
||||
}
|
||||
}
|
||||
subInitializers[i] = initializers.isEmpty()
|
||||
? Initializer.EMPTY_ARRAY
|
||||
: initializers.toArray( EMPTY_ARRAY );
|
||||
}
|
||||
else {
|
||||
subInitializers[i] = Initializer.EMPTY_ARRAY;
|
||||
}
|
||||
}
|
||||
return subInitializers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableValuedModelPart getInitializedPart() {
|
||||
return embedded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FetchParentAccess getFetchParentAccess() {
|
||||
return (FetchParentAccess) parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InitializerParent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultInitializer() {
|
||||
return isResultInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompositeInstance() {
|
||||
return state == State.RESOLVED || state == State.INITIALIZED ? compositeInstance : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartOfKey() {
|
||||
return isPartOfKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey() {
|
||||
if ( state != State.UNINITIALIZED ) {
|
||||
return;
|
||||
}
|
||||
// We need to possibly wrap the processing state if the embeddable is within an aggregate
|
||||
compositeInstance = null;
|
||||
if ( discriminatorAssembler != null ) {
|
||||
final EmbeddableDiscriminatorMapping discriminatorMapping = embedded.getEmbeddableTypeDescriptor()
|
||||
.getDiscriminatorMapping();
|
||||
assert discriminatorMapping != null;
|
||||
// todo: add more info into EmbeddableDiscriminatorConverter to extract this details object directly
|
||||
final Object discriminatorValue = discriminatorAssembler.extractRawValue( rowProcessingState );
|
||||
concreteEmbeddableType = discriminatorValue == null
|
||||
? null
|
||||
: embedded.getEmbeddableTypeDescriptor().findSubtypeByDiscriminator( discriminatorValue );
|
||||
}
|
||||
if ( isPartOfKey ) {
|
||||
state = State.KEY_RESOLVED;
|
||||
if ( subInitializers.length == 0 ) {
|
||||
// Resolve the component early to know if the key is missing or not
|
||||
resolveInstance();
|
||||
}
|
||||
else {
|
||||
resolveKeySubInitializers( rowProcessingState );
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.resolveKey();
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveKeySubInitializers(RowProcessingState rowProcessingState) {
|
||||
for ( Initializer initializer : subInitializers[getSubclassId()] ) {
|
||||
initializer.resolveKey();
|
||||
if ( initializer.getState() == State.MISSING ) {
|
||||
state = State.MISSING;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance() {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = State.RESOLVED;
|
||||
extractRowState();
|
||||
prepareCompositeInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(@Nullable Object instance) {
|
||||
if ( instance == null ) {
|
||||
state = State.MISSING;
|
||||
compositeInstance = null;
|
||||
}
|
||||
else {
|
||||
state = State.INITIALIZED;
|
||||
compositeInstance = instance;
|
||||
for ( Initializer initializer : subInitializers[getSubclassId()] ) {
|
||||
final Object subInstance = initializer.getInitializedPart()
|
||||
.asAttributeMapping()
|
||||
.getValue( instance );
|
||||
if ( subInstance == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
|
||||
// Go through the normal initializer process
|
||||
initializer.resolveKey();
|
||||
}
|
||||
else {
|
||||
initializer.resolveInstance( subInstance );
|
||||
}
|
||||
}
|
||||
if ( !rowProcessingState.isQueryCacheHit() && rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE ) {
|
||||
// Resolve the state of the assemblers if result caching is enabled and this is not a query cache hit
|
||||
for ( DomainResultAssembler<?> assembler : assemblers[getSubclassId()] ) {
|
||||
assembler.resolveState( rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance() {
|
||||
if ( state != State.RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Initializing composite instance [%s]", navigablePath );
|
||||
|
||||
if ( embedded.getParentInjectionAttributePropertyAccess() != null || embedded instanceof VirtualModelPart ) {
|
||||
handleParentInjection();
|
||||
|
||||
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 ( parent != null ) {
|
||||
embedded.getEmbeddableTypeDescriptor().setValues(
|
||||
lazyInitializer.getImplementation(),
|
||||
rowState
|
||||
);
|
||||
}
|
||||
else {
|
||||
// At this point, createEmptyCompositesEnabled is always true, so we generate
|
||||
// the composite instance.
|
||||
//
|
||||
// NOTE: `valuesAccess` is set to null to indicate that all values are null,
|
||||
// as opposed to returning the all-null value array. the instantiator
|
||||
// interprets that as the values are not known or were all null.
|
||||
final Object target = embedded.getEmbeddableTypeDescriptor().getRepresentationStrategy()
|
||||
.getInstantiator()
|
||||
.instantiate( this, sessionFactory );
|
||||
lazyInitializer.setImplementation( target );
|
||||
}
|
||||
}
|
||||
else {
|
||||
embedded.getEmbeddableTypeDescriptor().setValues( compositeInstance, rowState );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
if ( concreteEmbeddableType == null ) {
|
||||
for ( Initializer[] initializers : subInitializers ) {
|
||||
for ( Initializer initializer : initializers ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( Initializer initializer : subInitializers[getSubclassId()] ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstanceFromParent(Object parentInstance) {
|
||||
final AttributeMapping attributeMapping = getInitializedPart().asAttributeMapping();
|
||||
final Object instance = attributeMapping != null
|
||||
? attributeMapping.getValue( parentInstance )
|
||||
: parentInstance;
|
||||
compositeInstance = instance;
|
||||
if ( instance == null ) {
|
||||
state = State.MISSING;
|
||||
}
|
||||
else {
|
||||
state = State.INITIALIZED;
|
||||
for ( Initializer initializer : subInitializers[getSubclassId()] ) {
|
||||
initializer.initializeInstanceFromParent( instance );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareCompositeInstance() {
|
||||
// Virtual model parts use the owning entity as container which the fetch parent access provides.
|
||||
// For an identifier or foreign key this is called during the resolveKey phase of the fetch parent,
|
||||
// so we can't use the fetch parent access in that case.
|
||||
if ( parent != null && embedded instanceof VirtualModelPart && !isPartOfKey ) {
|
||||
parent.resolveInstance();
|
||||
compositeInstance = parent.getInitializedInstance();
|
||||
EntityInitializer entityInitializer = parent.asEntityInitializer();
|
||||
if ( entityInitializer != null && entityInitializer.isEntityInitialized() ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( compositeInstance == null ) {
|
||||
compositeInstance = createCompositeInstance();
|
||||
}
|
||||
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf(
|
||||
"Created composite instance [%s]",
|
||||
navigablePath
|
||||
);
|
||||
}
|
||||
|
||||
private void extractRowState() {
|
||||
boolean stateAllNull = true;
|
||||
final DomainResultAssembler<?>[] subAssemblers = assemblers[getSubclassId()];
|
||||
for ( int i = 0; i < subAssemblers.length; i++ ) {
|
||||
final DomainResultAssembler<?> assembler = subAssemblers[i];
|
||||
final Object contributorValue = assembler == null ? null : assembler.assemble(
|
||||
rowProcessingState,
|
||||
rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
|
||||
);
|
||||
|
||||
if ( contributorValue == BATCH_PROPERTY ) {
|
||||
rowState[i] = null;
|
||||
}
|
||||
else {
|
||||
rowState[i] = contributorValue;
|
||||
}
|
||||
if ( contributorValue != null ) {
|
||||
stateAllNull = false;
|
||||
}
|
||||
else if ( isPartOfKey ) {
|
||||
// If this is a foreign key and there is a null part, the whole thing has to be turned into null
|
||||
stateAllNull = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( stateAllNull ) {
|
||||
state = State.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveState(RowProcessingState rowProcessingState) {
|
||||
for ( final DomainResultAssembler<?> assembler : assemblers[getSubclassId()] ) {
|
||||
assembler.resolveState( rowProcessingState );
|
||||
}
|
||||
}
|
||||
|
||||
private Object createCompositeInstance() {
|
||||
if ( state == State.MISSING ) {
|
||||
// todo (6.0) : should we initialize the composite instance if it has a parent attribute?
|
||||
// if ( !createEmptyCompositesEnabled && embedded.getParentInjectionAttributePropertyAccess() == null ) {
|
||||
if ( !createEmptyCompositesEnabled ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final EmbeddableInstantiator instantiator = concreteEmbeddableType == null
|
||||
? embedded.getEmbeddableTypeDescriptor().getRepresentationStrategy().getInstantiator()
|
||||
: concreteEmbeddableType.getInstantiator();
|
||||
final Object instance = instantiator.instantiate( this, sessionFactory );
|
||||
state = State.RESOLVED;
|
||||
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Created composite instance [%s] : %s", navigablePath, instance );
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getValues() {
|
||||
return state == State.MISSING ? null : rowState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getValue(int i, Class<T> clazz) {
|
||||
return state == State.MISSING ? null : clazz.cast( rowState[i] );
|
||||
}
|
||||
|
||||
public int getSubclassId() {
|
||||
return concreteEmbeddableType == null ? 0 : concreteEmbeddableType.getSubclassId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOwner() {
|
||||
return parent.getInitializedInstance();
|
||||
}
|
||||
|
||||
private void handleParentInjection() {
|
||||
final PropertyAccess parentInjectionAccess = embedded.getParentInjectionAttributePropertyAccess();
|
||||
if ( parentInjectionAccess == null ) {
|
||||
// embeddable defined no parent injection
|
||||
return;
|
||||
}
|
||||
|
||||
final Initializer owningInitializer = determineOwningInitializer();
|
||||
final Object parent = determineParentInstance( owningInitializer );
|
||||
if ( parent == null ) {
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf(
|
||||
"Unable to determine parent for injection into embeddable [%s]",
|
||||
navigablePath
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf(
|
||||
"Injecting parent into embeddable [%s] : `%s` -> `%s`",
|
||||
navigablePath,
|
||||
parent,
|
||||
compositeInstance
|
||||
);
|
||||
|
||||
|
||||
final Setter setter = parentInjectionAccess.getSetter();
|
||||
assert setter != null;
|
||||
|
||||
setter.set( compositeInstance, parent );
|
||||
}
|
||||
|
||||
private Initializer determineOwningInitializer() {
|
||||
// Try to find the first non-embeddable fetch parent access
|
||||
// todo (6.x) - allow injection of containing composite as parent if
|
||||
// it is the direct parent
|
||||
InitializerParent parent = this.parent;
|
||||
while ( parent != null ) {
|
||||
if ( !parent.isEmbeddableInitializer() ) {
|
||||
return parent;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
throw new UnsupportedOperationException( "Injection of parent instance into embeddable result is not possible" );
|
||||
}
|
||||
|
||||
private Object determineParentInstance(Initializer parentInitializer) {
|
||||
if ( parentInitializer == null ) {
|
||||
throw new UnsupportedOperationException( "Cannot determine Embeddable: " + navigablePath + " parent instance, parent initializer is null" );
|
||||
}
|
||||
|
||||
final CollectionInitializer collectionInitializer = parentInitializer.asCollectionInitializer();
|
||||
if ( collectionInitializer != null ) {
|
||||
return collectionInitializer.getCollectionInstance().getOwner();
|
||||
}
|
||||
|
||||
final EntityInitializer parentEntityInitializer = parentInitializer.asEntityInitializer();
|
||||
if ( parentEntityInitializer != null ) {
|
||||
return parentEntityInitializer.getTargetInstance();
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException( "The Embeddable: " + navigablePath + " parent initializer is neither an instance of an EntityInitializer nor of a CollectionInitializer" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "(" + navigablePath + ") : `" + getInitializedPart().getJavaType().getJavaTypeClass() + "`";
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import org.hibernate.sql.results.graph.DomainResultCreationState;
|
|||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResult;
|
||||
|
@ -129,20 +130,27 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
|
|||
public DomainResultAssembler<T> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<T> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
//noinspection unchecked
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parentAccess, this ).asEmbeddableInitializer() );
|
||||
return new EmbeddableAssembler( creationState.resolveInitializer( this, parent, this ).asEmbeddableInitializer() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
EmbeddableResultImpl<T> resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
return new EmbeddableResultInitializer( this, parentAccess, discriminatorFetch, creationState );
|
||||
public Initializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new EmbeddableInitializerImpl( this, discriminatorFetch, parent, creationState, true );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* 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.embeddable.internal;
|
||||
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EmbeddableResultInitializer extends AbstractEmbeddableInitializer {
|
||||
public EmbeddableResultInitializer(
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
FetchParentAccess parentAccess,
|
||||
BasicFetch<?> discriminatorFetch,
|
||||
AssemblerCreationState creationState) {
|
||||
super( resultDescriptor, parentAccess, discriminatorFetch, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
return findFirstEntityDescriptorAccess().getParentKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultInitializer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EmbeddableResultInitializer(" + getNavigablePath() + ")";
|
||||
}
|
||||
}
|
|
@ -12,17 +12,13 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
|
|||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.exec.internal.BaseExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.Callback;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityFetch;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.sql.results.spi.RowReader;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class NestedRowProcessingState extends BaseExecutionContext implements RowProcessingState {
|
||||
private final AggregateEmbeddableInitializer aggregateEmbeddableInitializer;
|
||||
final RowProcessingState processingState;
|
||||
|
@ -85,11 +81,6 @@ public class NestedRowProcessingState extends BaseExecutionContext implements Ro
|
|||
processingState.finishRowProcessing( wasAdded );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer resolveInitializer(@Nullable NavigablePath path) {
|
||||
return processingState.resolveInitializer( path );
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions getQueryOptions() {
|
||||
return processingState.getQueryOptions();
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.hibernate.spi.NavigablePath;
|
|||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
||||
|
||||
public class NonAggregatedIdentifierMappingFetch extends EmbeddableFetchImpl {
|
||||
|
@ -31,9 +31,7 @@ public class NonAggregatedIdentifierMappingFetch extends EmbeddableFetchImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableInitializer createInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return new NonAggregatedIdentifierMappingFetchInitializer( parentAccess, this, creationState );
|
||||
public EmbeddableInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new NonAggregatedIdentifierMappingInitializer( this, parent, creationState, false );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* 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.embeddable.internal;
|
||||
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class NonAggregatedIdentifierMappingFetchInitializer
|
||||
extends AbstractNonAggregatedIdentifierMappingInitializer {
|
||||
public NonAggregatedIdentifierMappingFetchInitializer(
|
||||
FetchParentAccess fetchParentAccess,
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
AssemblerCreationState creationState) {
|
||||
super( resultDescriptor, fetchParentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultInitializer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
return findFirstEntityDescriptorAccess().getParentKey();
|
||||
}
|
||||
|
||||
}
|
|
@ -6,11 +6,14 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.embeddable.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
|
@ -20,17 +23,17 @@ import org.hibernate.proxy.HibernateProxy;
|
|||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||
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.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableLoadingLogger;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.graph.internal.AbstractInitializer;
|
||||
import org.hibernate.sql.results.internal.NullValueAssembler;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
|
@ -41,33 +44,36 @@ import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideE
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends AbstractFetchParentAccess
|
||||
public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializer
|
||||
implements EmbeddableInitializer, ValueAccess {
|
||||
|
||||
private final NavigablePath navigablePath;
|
||||
private final NonAggregatedIdentifierMapping embedded;
|
||||
private final EmbeddableMappingType representationEmbeddable;
|
||||
private final EmbeddableRepresentationStrategy representationStrategy;
|
||||
private final FetchParentAccess fetchParentAccess;
|
||||
private final @Nullable InitializerParent parent;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
private final boolean isResultInitializer;
|
||||
|
||||
private final DomainResultAssembler<?>[] assemblers;
|
||||
private final Initializer[] initializers;
|
||||
private final boolean hasIdClass;
|
||||
|
||||
|
||||
// per-row state
|
||||
private final Object[] virtualIdState;
|
||||
private final Object[] idClassState;
|
||||
private State state = State.INITIAL;
|
||||
protected Object compositeInstance;
|
||||
|
||||
public AbstractNonAggregatedIdentifierMappingInitializer(
|
||||
public NonAggregatedIdentifierMappingInitializer(
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
FetchParentAccess fetchParentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState,
|
||||
boolean isResultInitializer) {
|
||||
this.navigablePath = resultDescriptor.getNavigablePath();
|
||||
this.embedded = (NonAggregatedIdentifierMapping) resultDescriptor.getReferencedMappingContainer();
|
||||
this.fetchParentAccess = fetchParentAccess;
|
||||
this.parent = parent;
|
||||
this.isResultInitializer = isResultInitializer;
|
||||
|
||||
final EmbeddableMappingType virtualIdEmbeddable = embedded.getEmbeddableTypeDescriptor();
|
||||
this.representationEmbeddable = embedded.getMappedIdEmbeddableTypeDescriptor();
|
||||
|
@ -80,11 +86,19 @@ public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends
|
|||
|
||||
this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
|
||||
this.assemblers = createAssemblers( this, resultDescriptor, creationState, virtualIdEmbeddable );
|
||||
final ArrayList<Initializer> initializers = new ArrayList<>( assemblers.length );
|
||||
for ( DomainResultAssembler<?> assembler : assemblers ) {
|
||||
final Initializer initializer = assembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializers.add( initializer );
|
||||
}
|
||||
}
|
||||
this.initializers = initializers.isEmpty() ? Initializer.EMPTY_ARRAY : initializers.toArray( EMPTY_ARRAY );
|
||||
}
|
||||
|
||||
|
||||
protected static DomainResultAssembler<?>[] createAssemblers(
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
AssemblerCreationState creationState,
|
||||
EmbeddableMappingType embeddableTypeDescriptor) {
|
||||
|
@ -96,7 +110,7 @@ public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends
|
|||
|
||||
final DomainResultAssembler<?> stateAssembler = fetch == null
|
||||
? new NullValueAssembler<>( stateArrayContributor.getJavaType() )
|
||||
: fetch.createAssembler( parentAccess, creationState );
|
||||
: fetch.createAssembler( parent, creationState );
|
||||
|
||||
assemblers[i] = stateAssembler;
|
||||
}
|
||||
|
@ -109,128 +123,157 @@ public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends
|
|||
}
|
||||
|
||||
@Override
|
||||
public FetchParentAccess getFetchParentAccess() {
|
||||
return fetchParentAccess;
|
||||
public @Nullable FetchParentAccess getFetchParentAccess() {
|
||||
return (FetchParentAccess) parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InitializerParent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultInitializer() {
|
||||
return isResultInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompositeInstance() {
|
||||
return compositeInstance;
|
||||
return state == State.RESOLVED || state == State.INITIALIZED ? compositeInstance : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParentAccess findFirstEntityDescriptorAccess() {
|
||||
if ( fetchParentAccess == null ) {
|
||||
return null;
|
||||
public void resolveKey() {
|
||||
if ( state != State.UNINITIALIZED ) {
|
||||
return;
|
||||
}
|
||||
return fetchParentAccess.findFirstEntityDescriptorAccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityInitializer findFirstEntityInitializer() {
|
||||
final FetchParentAccess firstEntityDescriptorAccess = findFirstEntityDescriptorAccess();
|
||||
if ( firstEntityDescriptorAccess == null ) {
|
||||
return null;
|
||||
// We need to possibly wrap the processing state if the embeddable is within an aggregate
|
||||
compositeInstance = null;
|
||||
state = State.KEY_RESOLVED;
|
||||
if ( initializers.length == 0 ) {
|
||||
// Resolve the component early to know if the key is missing or not
|
||||
resolveInstance();
|
||||
}
|
||||
return firstEntityDescriptorAccess.findFirstEntityInitializer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState processingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState processingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState processingState) {
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Initializing composite instance [%s]", navigablePath );
|
||||
|
||||
switch ( state ) {
|
||||
case NULL:
|
||||
return;
|
||||
case INITIAL:
|
||||
// If we don't have an id class and this is a find by id lookup, we just use that instance
|
||||
if ( isFindByIdLookup( processingState ) ) {
|
||||
compositeInstance = processingState.getEntityId();
|
||||
state = State.INJECTED;
|
||||
else {
|
||||
for ( Initializer initializer : initializers ) {
|
||||
initializer.resolveKey();
|
||||
if ( initializer.getState() == State.MISSING ) {
|
||||
state = State.MISSING;
|
||||
return;
|
||||
}
|
||||
// We need to possibly wrap the processing state if the embeddable is within an aggregate
|
||||
processingState = wrapProcessingState( processingState );
|
||||
extractRowState( processingState );
|
||||
if ( state == State.NULL ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance() {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't have an id class and this is a find by id lookup, we just use that instance
|
||||
if ( isFindByIdLookup() ) {
|
||||
compositeInstance = rowProcessingState.getEntityId();
|
||||
state = State.INITIALIZED;
|
||||
return;
|
||||
}
|
||||
state = State.RESOLVED;
|
||||
// We need to possibly wrap the processing state if the embeddable is within an aggregate
|
||||
extractRowState();
|
||||
if ( state == State.MISSING ) {
|
||||
compositeInstance = null;
|
||||
}
|
||||
else {
|
||||
compositeInstance = representationStrategy.getInstantiator().instantiate( this, sessionFactory );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(@Nullable Object instance) {
|
||||
if ( instance == null ) {
|
||||
state = State.MISSING;
|
||||
compositeInstance = null;
|
||||
}
|
||||
else {
|
||||
state = State.INITIALIZED;
|
||||
compositeInstance = instance;
|
||||
for ( Initializer initializer : initializers ) {
|
||||
final Object subInstance = initializer.getInitializedPart()
|
||||
.asAttributeMapping()
|
||||
.getValue( instance );
|
||||
if ( subInstance == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
|
||||
// Go through the normal initializer process
|
||||
initializer.resolveKey();
|
||||
}
|
||||
else {
|
||||
compositeInstance = representationStrategy.getInstantiator().instantiate( this, sessionFactory );
|
||||
initializer.resolveInstance( subInstance );
|
||||
}
|
||||
case EXTRACTED:
|
||||
final Object parentInstance;
|
||||
if ( fetchParentAccess != null && ( parentInstance = fetchParentAccess.getInitializedInstance() ) != null ) {
|
||||
notifyResolutionListeners( compositeInstance );
|
||||
|
||||
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( parentInstance );
|
||||
// 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 ) {
|
||||
final Initializer parentInitializer = processingState.resolveInitializer( navigablePath.getParent() );
|
||||
if ( parentInitializer != this ) {
|
||||
( (FetchParentAccess) parentInitializer ).registerResolutionListener( (entity) -> {
|
||||
embedded.getVirtualIdEmbeddable().setValues( entity, virtualIdState );
|
||||
state = State.INJECTED;
|
||||
} );
|
||||
}
|
||||
else {
|
||||
assert false;
|
||||
// At this point, createEmptyCompositesEnabled is always true, so we generate
|
||||
// the composite instance.
|
||||
//
|
||||
// NOTE: `valuesAccess` is set to null to indicate that all values are null,
|
||||
// as opposed to returning the all-null value array. the instantiator
|
||||
// interprets that as the values are not known or were all null.
|
||||
final Object target = representationStrategy
|
||||
.getInstantiator()
|
||||
.instantiate( this, sessionFactory);
|
||||
|
||||
state = State.INJECTED;
|
||||
lazyInitializer.setImplementation( target );
|
||||
}
|
||||
}
|
||||
else {
|
||||
embedded.getVirtualIdEmbeddable().setValues( parentInstance, virtualIdState );
|
||||
state = State.INJECTED;
|
||||
}
|
||||
}
|
||||
if ( !rowProcessingState.isQueryCacheHit() && rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE ) {
|
||||
// Resolve the state of the assemblers if result caching is enabled and this is not a query cache hit
|
||||
for ( DomainResultAssembler<?> assembler : assemblers ) {
|
||||
assembler.resolveState( rowProcessingState );
|
||||
}
|
||||
case INJECTED:
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFindByIdLookup(RowProcessingState processingState) {
|
||||
return !hasIdClass && processingState.getEntityId() != null
|
||||
@Override
|
||||
public void initializeInstance() {
|
||||
if ( state != State.RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Initializing composite instance [%s]", navigablePath );
|
||||
|
||||
if ( parent != null ) {
|
||||
assert parent.isEntityInitializer();
|
||||
final Object parentInstance = parent.getInitializedInstance();
|
||||
assert parentInstance != null;
|
||||
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( parentInstance );
|
||||
// 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 ) {
|
||||
embedded.getVirtualIdEmbeddable().setValues(
|
||||
lazyInitializer.getImplementation(),
|
||||
virtualIdState
|
||||
);
|
||||
}
|
||||
else {
|
||||
embedded.getVirtualIdEmbeddable().setValues( parentInstance, virtualIdState );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
for ( Initializer initializer : initializers ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFindByIdLookup() {
|
||||
return !hasIdClass && rowProcessingState.getEntityId() != null
|
||||
&& navigablePath.getParent().getParent() == null
|
||||
&& navigablePath instanceof EntityIdentifierNavigablePath;
|
||||
}
|
||||
|
||||
private void extractRowState(RowProcessingState processingState) {
|
||||
state = State.NULL;
|
||||
private void extractRowState() {
|
||||
for ( int i = 0; i < assemblers.length; i++ ) {
|
||||
final DomainResultAssembler<?> assembler = assemblers[i];
|
||||
final Object contributorValue = assembler.assemble(
|
||||
processingState,
|
||||
processingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
|
||||
rowProcessingState,
|
||||
rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
|
||||
);
|
||||
|
||||
if ( contributorValue == null ) {
|
||||
// This is a key and there is a null part, the whole thing has to be turned into null
|
||||
state = State.MISSING;
|
||||
return;
|
||||
}
|
||||
if ( contributorValue == BATCH_PROPERTY ) {
|
||||
|
@ -250,19 +293,18 @@ public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends
|
|||
final Object associationKey = fkDescriptor.getAssociationKeyFromSide(
|
||||
virtualIdState[i],
|
||||
toOneAttributeMapping.getSideNature().inverse(),
|
||||
processingState.getSession()
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
idClassState[i] = associationKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
state = State.EXTRACTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveState(RowProcessingState rowProcessingState) {
|
||||
if ( !isFindByIdLookup( rowProcessingState ) ) {
|
||||
if ( !isFindByIdLookup() ) {
|
||||
for ( final DomainResultAssembler<?> assembler : assemblers ) {
|
||||
assembler.resolveState( rowProcessingState );
|
||||
}
|
||||
|
@ -271,25 +313,19 @@ public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends
|
|||
|
||||
@Override
|
||||
public Object[] getValues() {
|
||||
return state == State.NULL ? null : idClassState;
|
||||
assert state == State.RESOLVED;
|
||||
return idClassState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getValue(int i, Class<T> clazz) {
|
||||
return state == State.NULL ? null : clazz.cast( idClassState[i] );
|
||||
assert state == State.RESOLVED;
|
||||
return clazz.cast( idClassState[i] );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOwner() {
|
||||
return fetchParentAccess.getInitializedInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
compositeInstance = null;
|
||||
state = State.INITIAL;
|
||||
|
||||
clearResolutionListeners();
|
||||
return parent == null ? null : parent.getInitializedInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -297,25 +333,8 @@ public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FetchParentAccess getOwningParent() {
|
||||
return fetchParentAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable EntityMappingType getOwnedModelPartDeclaringType() {
|
||||
return embedded.findContainingEntityMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "(" + navigablePath + ") : `" + getInitializedPart().getJavaType().getJavaTypeClass() + "`";
|
||||
}
|
||||
|
||||
enum State {
|
||||
INITIAL,
|
||||
EXTRACTED,
|
||||
NULL,
|
||||
INJECTED
|
||||
return "NonAggregatedIdentifierMappingInitializer(" + navigablePath + ") : `" + getInitializedPart().getJavaType().getJavaTypeClass() + "`";
|
||||
}
|
||||
}
|
|
@ -10,8 +10,8 @@ import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
|||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
|
||||
public class NonAggregatedIdentifierMappingResult<T> extends EmbeddableResultImpl<T> {
|
||||
public NonAggregatedIdentifierMappingResult(
|
||||
|
@ -25,12 +25,8 @@ public class NonAggregatedIdentifierMappingResult<T> extends EmbeddableResultImp
|
|||
@Override
|
||||
public Initializer createInitializer(
|
||||
EmbeddableResultImpl<T> resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return new NonAggregatedIdentifierMappingResultInitializer(
|
||||
resultGraphNode,
|
||||
parentAccess,
|
||||
creationState
|
||||
);
|
||||
return new NonAggregatedIdentifierMappingInitializer( resultGraphNode, parent, creationState, true );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* 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.embeddable.internal;
|
||||
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class NonAggregatedIdentifierMappingResultInitializer extends AbstractNonAggregatedIdentifierMappingInitializer {
|
||||
public NonAggregatedIdentifierMappingResultInitializer(
|
||||
EmbeddableResultGraphNode resultDescriptor,
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
super( resultDescriptor, parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
return findFirstEntityDescriptorAccess().getParentKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultInitializer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EmbeddableResultInitializer(" + getNavigablePath() + ")";
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.results.graph.AbstractFetchParent;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.FetchableContainer;
|
||||
import org.hibernate.sql.results.graph.entity.internal.EntityAssembler;
|
||||
|
||||
/**
|
||||
* Support for non-lazy EntityFetch implementations - both joined and subsequent-select
|
||||
*
|
||||
* @author Andrea Boriero
|
||||
* @deprecated Abstraction was not useful, so it was inlined into {@link org.hibernate.sql.results.graph.entity.internal.EntityFetchJoinedImpl} directly
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public abstract class AbstractNonLazyEntityFetch extends AbstractFetchParent implements EntityFetch {
|
||||
private final FetchParent fetchParent;
|
||||
private final EntityValuedFetchable fetchContainer;
|
||||
|
||||
public AbstractNonLazyEntityFetch(
|
||||
FetchParent fetchParent,
|
||||
EntityValuedFetchable fetchedPart,
|
||||
NavigablePath navigablePath) {
|
||||
super( navigablePath );
|
||||
this.fetchContainer = fetchedPart;
|
||||
this.fetchParent = fetchParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityValuedFetchable getEntityValuedModelPart() {
|
||||
return fetchContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchableContainer getFetchContainer() {
|
||||
return fetchContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityValuedFetchable getReferencedModePart() {
|
||||
return getEntityValuedModelPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityValuedFetchable getReferencedMappingType() {
|
||||
return getEntityValuedModelPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType getReferencedMappingContainer() {
|
||||
return getEntityValuedModelPart().getEntityMappingType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityValuedFetchable getFetchedMapping() {
|
||||
return getEntityValuedModelPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParent getFetchParent() {
|
||||
return fetchParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
final EntityInitializer entityInitializer = getEntityInitializer( parentAccess, creationState );
|
||||
return buildEntityAssembler( entityInitializer );
|
||||
}
|
||||
|
||||
protected EntityAssembler buildEntityAssembler(EntityInitializer entityInitializer) {
|
||||
return new EntityAssembler( getFetchedMapping().getJavaType(), entityInitializer );
|
||||
}
|
||||
|
||||
protected abstract EntityInitializer getEntityInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState);
|
||||
}
|
|
@ -11,6 +11,8 @@ import org.hibernate.persister.entity.EntityPersister;
|
|||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Initializer implementation for initializing entity references.
|
||||
*
|
||||
|
@ -26,7 +28,8 @@ public interface EntityInitializer extends FetchParentAccess {
|
|||
EntityPersister getConcreteDescriptor();
|
||||
|
||||
@Override
|
||||
default FetchParentAccess findFirstEntityDescriptorAccess() {
|
||||
default @Nullable EntityInitializer findFirstEntityDescriptorAccess() {
|
||||
// Keep this method around for binary backwards compatibility
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -44,12 +47,36 @@ public interface EntityInitializer extends FetchParentAccess {
|
|||
*/
|
||||
Object getEntityInstance();
|
||||
|
||||
default Object getManagedInstance() {
|
||||
return getEntityInstance();
|
||||
}
|
||||
|
||||
default Object getTargetInstance() {
|
||||
return getEntityInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
default Object getInitializedInstance() {
|
||||
return getEntityInstance();
|
||||
}
|
||||
|
||||
EntityKey getEntityKey();
|
||||
default @Nullable EntityKey resolveEntityKeyOnly(RowProcessingState rowProcessingState) {
|
||||
resolveKey();
|
||||
final EntityKey entityKey = getEntityKey();
|
||||
finishUpRow();
|
||||
return entityKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #resolveEntityKeyOnly(RowProcessingState)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
@Nullable EntityKey getEntityKey();
|
||||
|
||||
default @Nullable Object getEntityIdentifier() {
|
||||
final EntityKey entityKey = getEntityKey();
|
||||
return entityKey == null ? null : entityKey.getIdentifier();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isEntityInitializer() {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.entity.internal;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.engine.spi.EntityHolder;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
|
@ -20,52 +20,68 @@ import org.hibernate.persister.entity.EntityPersister;
|
|||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
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.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.graph.internal.AbstractInitializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public abstract class AbstractBatchEntitySelectFetchInitializer implements EntityInitializer {
|
||||
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
|
||||
|
||||
protected final FetchParentAccess parentAccess;
|
||||
public abstract class AbstractBatchEntitySelectFetchInitializer extends AbstractInitializer implements EntityInitializer {
|
||||
|
||||
protected final InitializerParent parent;
|
||||
private final NavigablePath navigablePath;
|
||||
private final FetchParentAccess owningParent;
|
||||
private final EntityMappingType ownedModelPartDeclaringType;
|
||||
private final boolean isPartOfKey;
|
||||
|
||||
protected final EntityPersister concreteDescriptor;
|
||||
protected final DomainResultAssembler<?> identifierAssembler;
|
||||
protected final ToOneAttributeMapping referencedModelPart;
|
||||
protected final EntityInitializer firstEntityInitializer;
|
||||
|
||||
protected boolean parentShallowCached;
|
||||
protected final EntityInitializer owningEntityInitializer;
|
||||
|
||||
// per-row state
|
||||
protected Object initializedEntityInstance;
|
||||
protected EntityKey entityKey;
|
||||
|
||||
protected State state = State.UNINITIALIZED;
|
||||
protected @Nullable Object initializedEntityInstance;
|
||||
protected @Nullable Object entityIdentifier;
|
||||
protected @Nullable EntityKey entityKey;
|
||||
|
||||
/**
|
||||
*
|
||||
* @deprecated Use {@link #AbstractBatchEntitySelectFetchInitializer(InitializerParent, ToOneAttributeMapping, NavigablePath, EntityPersister, DomainResultAssembler)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public AbstractBatchEntitySelectFetchInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
FetchParentAccess parent,
|
||||
ToOneAttributeMapping referencedModelPart,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> identifierAssembler) {
|
||||
this.parentAccess = parentAccess;
|
||||
this(
|
||||
(InitializerParent) parent,
|
||||
referencedModelPart,
|
||||
fetchedNavigable,
|
||||
concreteDescriptor,
|
||||
identifierAssembler
|
||||
);
|
||||
}
|
||||
|
||||
public AbstractBatchEntitySelectFetchInitializer(
|
||||
InitializerParent parent,
|
||||
ToOneAttributeMapping referencedModelPart,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> identifierAssembler) {
|
||||
this.parent = parent;
|
||||
this.referencedModelPart = referencedModelPart;
|
||||
this.navigablePath = fetchedNavigable;
|
||||
this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parentAccess );
|
||||
this.owningParent = FetchParentAccess.determineOwningParent( parentAccess );
|
||||
this.ownedModelPartDeclaringType = FetchParentAccess.determineOwnedModelPartDeclaringType( referencedModelPart, parentAccess, owningParent );
|
||||
this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parent );
|
||||
this.concreteDescriptor = concreteDescriptor;
|
||||
this.identifierAssembler = identifierAssembler;
|
||||
this.firstEntityInitializer = parentAccess.findFirstEntityInitializer();
|
||||
assert firstEntityInitializer != null : "This initializer requires parentAccess.findFirstEntityInitializer() to not be null";
|
||||
this.owningEntityInitializer = Initializer.findOwningEntityInitializer( parent );
|
||||
assert owningEntityInitializer != null : "This initializer requires an owning parent entity initializer";
|
||||
}
|
||||
|
||||
public ModelPart getInitializedPart() {
|
||||
|
@ -87,34 +103,106 @@ public abstract class AbstractBatchEntitySelectFetchInitializer implements Entit
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
}
|
||||
|
||||
protected abstract void registerResolutionListener();
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
public void resolveKey() {
|
||||
if ( state != State.UNINITIALIZED ) {
|
||||
return;
|
||||
}
|
||||
if ( parentShallowCached || shouldSkipInitializer( rowProcessingState ) ) {
|
||||
state = State.MISSING;
|
||||
|
||||
entityKey = null;
|
||||
initializedEntityInstance = null;
|
||||
final Initializer initializer = identifierAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializer.resolveKey();
|
||||
entityIdentifier = null;
|
||||
state = initializer.getState() == State.MISSING
|
||||
? State.MISSING
|
||||
: State.KEY_RESOLVED;
|
||||
}
|
||||
else {
|
||||
entityIdentifier = identifierAssembler.assemble( rowProcessingState );
|
||||
state = entityIdentifier == null
|
||||
? State.MISSING
|
||||
: State.KEY_RESOLVED;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance() {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Object entityIdentifier = identifierAssembler.assemble( rowProcessingState );
|
||||
state = State.RESOLVED;
|
||||
if ( entityIdentifier == null ) {
|
||||
// entityIdentifier can be null if the identifier is based on an initializer
|
||||
entityIdentifier = identifierAssembler.assemble( rowProcessingState );
|
||||
if ( entityIdentifier == null ) {
|
||||
entityKey = null;
|
||||
initializedEntityInstance = null;
|
||||
state = State.MISSING;
|
||||
return;
|
||||
}
|
||||
}
|
||||
entityKey = new EntityKey( entityIdentifier, concreteDescriptor );
|
||||
initializedEntityInstance = getExistingInitializedInstance( rowProcessingState );
|
||||
if ( initializedEntityInstance == null ) {
|
||||
// need to add the key to the batch queue only when the entity has not been already loaded or
|
||||
// there isn't another initializer that is loading it
|
||||
registerToBatchFetchQueue( rowProcessingState );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(Object instance) {
|
||||
if ( instance == null ) {
|
||||
state = State.MISSING;
|
||||
entityKey = null;
|
||||
initializedEntityInstance = null;
|
||||
return;
|
||||
}
|
||||
final Initializer initializer = identifierAssembler.getInitializer();
|
||||
// Only need to extract the identifier if the identifier has a many to one
|
||||
final boolean hasKeyManyToOne = initializer != null;
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
final LazyInitializer lazyInitializer = extractLazyInitializer( instance );
|
||||
entityKey = null;
|
||||
if ( lazyInitializer == null ) {
|
||||
// Entity is initialized
|
||||
state = State.INITIALIZED;
|
||||
if ( hasKeyManyToOne ) {
|
||||
entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
|
||||
}
|
||||
initializedEntityInstance = instance;
|
||||
}
|
||||
else if ( lazyInitializer.isUninitialized() ) {
|
||||
state = State.RESOLVED;
|
||||
if ( hasKeyManyToOne ) {
|
||||
entityIdentifier = lazyInitializer.getIdentifier();
|
||||
}
|
||||
// Resolve and potentially create the entity instance
|
||||
registerToBatchFetchQueue( rowProcessingState );
|
||||
}
|
||||
else {
|
||||
entityKey = new EntityKey( entityIdentifier, concreteDescriptor );
|
||||
state = State.KEY_RESOLVED;
|
||||
// Entity is initialized
|
||||
state = State.INITIALIZED;
|
||||
if ( hasKeyManyToOne ) {
|
||||
entityIdentifier = lazyInitializer.getIdentifier();
|
||||
}
|
||||
initializedEntityInstance = lazyInitializer.getImplementation();
|
||||
}
|
||||
if ( hasKeyManyToOne ) {
|
||||
initializer.resolveInstance( entityIdentifier );
|
||||
}
|
||||
else if ( !rowProcessingState.isQueryCacheHit() && rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE ) {
|
||||
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
|
||||
identifierAssembler.resolveState( rowProcessingState );
|
||||
}
|
||||
}
|
||||
|
||||
protected Object getExistingInitializedInstance(RowProcessingState rowProcessingState) {
|
||||
assert entityKey != null;
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
|
||||
|
@ -134,34 +222,33 @@ public abstract class AbstractBatchEntitySelectFetchInitializer implements Entit
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstanceFromParent(Object parentInstance, RowProcessingState rowProcessingState) {
|
||||
public void initializeInstanceFromParent(Object parentInstance) {
|
||||
final AttributeMapping attributeMapping = getInitializedPart().asAttributeMapping();
|
||||
final Object instance = attributeMapping != null
|
||||
? attributeMapping.getValue( parentInstance )
|
||||
: parentInstance;
|
||||
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( instance );
|
||||
if ( lazyInitializer != null && lazyInitializer.isUninitialized() ) {
|
||||
entityKey = new EntityKey( lazyInitializer.getIdentifier(), concreteDescriptor );
|
||||
registerToBatchFetchQueue( rowProcessingState );
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
initializedEntityInstance = null;
|
||||
// No need to initialize these fields
|
||||
entityKey = null;
|
||||
state = State.UNINITIALIZED;
|
||||
initializedEntityInstance = null;
|
||||
if ( instance == null ) {
|
||||
state = State.MISSING;
|
||||
}
|
||||
else {
|
||||
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( instance );
|
||||
if ( lazyInitializer != null && lazyInitializer.isUninitialized() ) {
|
||||
entityKey = new EntityKey( lazyInitializer.getIdentifier(), concreteDescriptor );
|
||||
registerToBatchFetchQueue( rowProcessingState );
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markShallowCached() {
|
||||
parentShallowCached = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
parentShallowCached = false;
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
final Initializer initializer = identifierAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -171,7 +258,7 @@ public abstract class AbstractBatchEntitySelectFetchInitializer implements Entit
|
|||
|
||||
@Override
|
||||
public Object getEntityInstance() {
|
||||
return initializedEntityInstance;
|
||||
return state == State.RESOLVED || state == State.INITIALIZED ? initializedEntityInstance : null;
|
||||
}
|
||||
|
||||
protected static Object loadInstance(
|
||||
|
@ -186,9 +273,33 @@ public abstract class AbstractBatchEntitySelectFetchInitializer implements Entit
|
|||
);
|
||||
}
|
||||
|
||||
protected AttributeMapping getParentEntityAttribute(String attributeName) {
|
||||
final AttributeMapping parentAttribute = firstEntityInitializer.getConcreteDescriptor()
|
||||
.findAttributeMapping( attributeName );
|
||||
protected AttributeMapping[] getParentEntityAttributes(String attributeName) {
|
||||
final EntityPersister entityDescriptor = owningEntityInitializer.getEntityDescriptor();
|
||||
final AttributeMapping[] parentEntityAttributes = new AttributeMapping[
|
||||
entityDescriptor.getRootEntityDescriptor()
|
||||
.getSubclassEntityNames()
|
||||
.size()
|
||||
];
|
||||
parentEntityAttributes[entityDescriptor.getSubclassId()] = getParentEntityAttribute(
|
||||
entityDescriptor,
|
||||
referencedModelPart,
|
||||
attributeName
|
||||
);
|
||||
for ( EntityMappingType subMappingType : entityDescriptor.getSubMappingTypes() ) {
|
||||
parentEntityAttributes[subMappingType.getSubclassId()] = getParentEntityAttribute(
|
||||
subMappingType,
|
||||
referencedModelPart,
|
||||
attributeName
|
||||
);
|
||||
}
|
||||
return parentEntityAttributes;
|
||||
}
|
||||
|
||||
protected static AttributeMapping getParentEntityAttribute(
|
||||
EntityMappingType subMappingType,
|
||||
ToOneAttributeMapping referencedModelPart,
|
||||
String attributeName) {
|
||||
final AttributeMapping parentAttribute = subMappingType.findAttributeMapping( attributeName );
|
||||
if ( parentAttribute != null && parentAttribute.getDeclaringType() == referencedModelPart.getDeclaringType()
|
||||
.findContainingEntityMapping() ) {
|
||||
// These checks are needed to avoid setting the instance using the wrong (child's) model part or
|
||||
|
@ -200,17 +311,12 @@ public abstract class AbstractBatchEntitySelectFetchInitializer implements Entit
|
|||
|
||||
@Override
|
||||
public FetchParentAccess getFetchParentAccess() {
|
||||
return parentAccess;
|
||||
return (FetchParentAccess) parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FetchParentAccess getOwningParent() {
|
||||
return owningParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable EntityMappingType getOwnedModelPartDeclaringType() {
|
||||
return ownedModelPartDeclaringType;
|
||||
public InitializerParent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -218,13 +324,6 @@ public abstract class AbstractBatchEntitySelectFetchInitializer implements Entit
|
|||
return concreteDescriptor;
|
||||
}
|
||||
|
||||
protected enum State {
|
||||
UNINITIALIZED,
|
||||
MISSING,
|
||||
KEY_RESOLVED,
|
||||
INITIALIZED
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityKey getEntityKey() {
|
||||
throw new UnsupportedOperationException(
|
||||
|
@ -237,10 +336,4 @@ public abstract class AbstractBatchEntitySelectFetchInitializer implements Entit
|
|||
"This should never happen, because this initializer has not child initializers" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> listener) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This should never happen, because this initializer has not child initializers" );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.hibernate.sql.results.graph.Fetch;
|
|||
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.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.entity.EntityFetch;
|
||||
|
@ -144,7 +145,14 @@ public abstract class AbstractNonJoinedEntityFetch implements EntityFetch,
|
|||
public DomainResultAssembler<?> createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
final EntityInitializer entityInitializer = creationState.resolveInitializer( this, parentAccess, this )
|
||||
return createAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> createAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
final EntityInitializer entityInitializer = creationState.resolveInitializer( this, parent, this )
|
||||
.asEntityInitializer();
|
||||
assert entityInitializer != null;
|
||||
return buildEntityAssembler( entityInitializer );
|
||||
|
@ -153,13 +161,13 @@ public abstract class AbstractNonJoinedEntityFetch implements EntityFetch,
|
|||
@Override
|
||||
public EntityInitializer createInitializer(
|
||||
AbstractNonJoinedEntityFetch resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract EntityInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState);
|
||||
public abstract EntityInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState);
|
||||
|
||||
protected EntityAssembler buildEntityAssembler(EntityInitializer entityInitializer) {
|
||||
return new EntityAssembler( getFetchedMapping().getJavaType(), entityInitializer );
|
||||
|
|
|
@ -13,24 +13,31 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityHolder;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.log.LoggingHelper;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.property.access.spi.Getter;
|
||||
import org.hibernate.property.access.spi.Setter;
|
||||
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.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer {
|
||||
private Map<EntityKey, List<ParentInfo>> toBatchLoad;
|
||||
private final String rootEmbeddablePropertyName;
|
||||
protected final Setter referencedModelPartSetter;
|
||||
protected final AttributeMapping[] rootEmbeddableAttributes;
|
||||
protected final Getter[] rootEmbeddableGetters;
|
||||
protected final Type[] rootEmbeddablePropertyTypes;
|
||||
|
||||
/**
|
||||
* Marker value for batch properties, needed by the EmbeddableInitializer to instantiate the
|
||||
|
@ -47,50 +54,96 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #BatchEntityInsideEmbeddableSelectFetchInitializer(InitializerParent, ToOneAttributeMapping, NavigablePath, EntityPersister, DomainResultAssembler)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public BatchEntityInsideEmbeddableSelectFetchInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
ToOneAttributeMapping referencedModelPart,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> identifierAssembler) {
|
||||
super( parentAccess, referencedModelPart, fetchedNavigable, concreteDescriptor, identifierAssembler );
|
||||
|
||||
rootEmbeddablePropertyName = getRootEmbeddablePropertyName(
|
||||
firstEntityInitializer,
|
||||
parentAccess,
|
||||
referencedModelPart
|
||||
this(
|
||||
(InitializerParent) parentAccess,
|
||||
referencedModelPart,
|
||||
fetchedNavigable,
|
||||
concreteDescriptor,
|
||||
identifierAssembler
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
return;
|
||||
public BatchEntityInsideEmbeddableSelectFetchInitializer(
|
||||
InitializerParent parentAccess,
|
||||
ToOneAttributeMapping referencedModelPart,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> identifierAssembler) {
|
||||
super( parentAccess, referencedModelPart, fetchedNavigable, concreteDescriptor, identifierAssembler );
|
||||
|
||||
this.referencedModelPartSetter = referencedModelPart.getAttributeMetadata().getPropertyAccess().getSetter();
|
||||
final String rootEmbeddablePropertyName = getRootEmbeddablePropertyName(
|
||||
owningEntityInitializer,
|
||||
parentAccess,
|
||||
referencedModelPart
|
||||
);
|
||||
this.rootEmbeddableAttributes = getParentEntityAttributes( rootEmbeddablePropertyName );
|
||||
final Getter[] getters = new Getter[rootEmbeddableAttributes.length];
|
||||
for ( int i = 0; i < rootEmbeddableAttributes.length; i++ ) {
|
||||
if ( rootEmbeddableAttributes[i] != null ) {
|
||||
getters[i] = rootEmbeddableAttributes[i].getAttributeMetadata().getPropertyAccess().getGetter();
|
||||
}
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
initializedEntityInstance = getExistingInitializedInstance( rowProcessingState );
|
||||
if ( initializedEntityInstance == null ) {
|
||||
this.rootEmbeddableGetters = getters;
|
||||
this.rootEmbeddablePropertyTypes = getParentEntityAttributeTypes( rootEmbeddablePropertyName );
|
||||
}
|
||||
|
||||
// need to add the key to the batch queue only when the entity has not been already loaded or
|
||||
// there isn't another initializer that is loading it
|
||||
registerToBatchFetchQueue( rowProcessingState );
|
||||
protected Type[] getParentEntityAttributeTypes(String attributeName) {
|
||||
final EntityPersister entityDescriptor = owningEntityInitializer.getEntityDescriptor();
|
||||
final Type[] attributeTypes = new Type[
|
||||
entityDescriptor.getRootEntityDescriptor()
|
||||
.getSubclassEntityNames()
|
||||
.size()
|
||||
];
|
||||
initializeAttributeType( attributeTypes, entityDescriptor, attributeName );
|
||||
for ( EntityMappingType subMappingType : entityDescriptor.getSubMappingTypes() ) {
|
||||
initializeAttributeType( attributeTypes, subMappingType.getEntityPersister(), attributeName );
|
||||
}
|
||||
return attributeTypes;
|
||||
}
|
||||
|
||||
initializedEntityInstance = BATCH_PROPERTY;
|
||||
protected void initializeAttributeType(Type[] attributeTypes, EntityPersister entityDescriptor, String attributeName) {
|
||||
if ( rootEmbeddableAttributes[entityDescriptor.getSubclassId()] != null ) {
|
||||
attributeTypes[entityDescriptor.getSubclassId()] = entityDescriptor.getPropertyType( attributeName );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerToBatchFetchQueue(RowProcessingState rowProcessingState) {
|
||||
super.registerToBatchFetchQueue( rowProcessingState );
|
||||
initializedEntityInstance = BATCH_PROPERTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance() {
|
||||
super.initializeInstance();
|
||||
// todo: check why this can't be moved to #registerToBatchFetchQueue
|
||||
if ( initializedEntityInstance == BATCH_PROPERTY ) {
|
||||
final int owningEntitySubclassId = owningEntityInitializer.getConcreteDescriptor().getSubclassId();
|
||||
final AttributeMapping rootEmbeddableAttribute = rootEmbeddableAttributes[owningEntitySubclassId];
|
||||
if ( rootEmbeddableAttribute != null ) {
|
||||
getParentInfos().add( new ParentInfo(
|
||||
owningEntityInitializer.getTargetInstance(),
|
||||
parent.getInitializedInstance(),
|
||||
rootEmbeddableAttribute.getStateArrayPosition(),
|
||||
owningEntitySubclassId
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerResolutionListener() {
|
||||
parentAccess.registerResolutionListener( parentInstance -> {
|
||||
final AttributeMapping parentAttribute = getParentEntityAttribute( rootEmbeddablePropertyName );
|
||||
if ( parentAttribute != null ) {
|
||||
getParentInfos().add( new ParentInfo(
|
||||
firstEntityInitializer.getEntityKey(),
|
||||
parentInstance,
|
||||
parentAttribute.getStateArrayPosition()
|
||||
) );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
private List<ParentInfo> getParentInfos() {
|
||||
|
@ -106,39 +159,50 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
|
|||
}
|
||||
|
||||
private static class ParentInfo {
|
||||
private final EntityKey initializerEntityKey;
|
||||
private final Object parentEntityInstance;
|
||||
private final Object parentInstance;
|
||||
private final int propertyIndex;
|
||||
private final int parentEntitySubclassId;
|
||||
|
||||
public ParentInfo(EntityKey initializerEntityKey, Object parentInstance, int propertyIndex) {
|
||||
this.initializerEntityKey = initializerEntityKey;
|
||||
public ParentInfo(
|
||||
Object parentEntityInstance,
|
||||
Object parentInstance,
|
||||
int propertyIndex,
|
||||
int parentEntitySubclassId) {
|
||||
this.parentEntityInstance = parentEntityInstance;
|
||||
this.parentInstance = parentInstance;
|
||||
this.propertyIndex = propertyIndex;
|
||||
this.parentEntitySubclassId = parentEntitySubclassId;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext context) {
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
super.endLoading( executionContext );
|
||||
if ( toBatchLoad != null ) {
|
||||
toBatchLoad.forEach(
|
||||
(entityKey, parentInfos) -> {
|
||||
final SharedSessionContractImplementor session = context.getSession();
|
||||
final SharedSessionContractImplementor session = executionContext.getSession();
|
||||
final Object loadedInstance = loadInstance( entityKey, referencedModelPart, session );
|
||||
for ( ParentInfo parentInfo : parentInfos ) {
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
final EntityHolder holder = persistenceContext.getEntityHolder( parentInfo.initializerEntityKey );
|
||||
final Object entity = holder.getEntity();
|
||||
setInstance(
|
||||
firstEntityInitializer,
|
||||
referencedModelPart,
|
||||
rootEmbeddablePropertyName,
|
||||
parentInfo.propertyIndex,
|
||||
loadedInstance,
|
||||
parentInfo.parentInstance,
|
||||
entity,
|
||||
persistenceContext.getEntry( entity ),
|
||||
session
|
||||
);
|
||||
final EntityEntry parentEntityEntry = persistenceContext.getEntry( parentInfo.parentEntityInstance );
|
||||
referencedModelPartSetter.set( parentInfo.parentInstance, loadedInstance );
|
||||
final 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 Object rootEmbeddable = rootEmbeddableGetters[parentInfo.parentEntitySubclassId].get( parentInfo.parentEntityInstance );
|
||||
loadedState[parentInfo.propertyIndex] = rootEmbeddablePropertyTypes[parentInfo.parentEntitySubclassId].deepCopy(
|
||||
rootEmbeddable,
|
||||
session.getFactory()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -193,12 +257,27 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getRootEmbeddablePropertyName(EntityInitializer, InitializerParent, ToOneAttributeMapping)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
protected static String getRootEmbeddablePropertyName(
|
||||
EntityInitializer firstEntityInitializer,
|
||||
FetchParentAccess parentAccess,
|
||||
ToOneAttributeMapping referencedModelPart) {
|
||||
return getRootEmbeddablePropertyName(
|
||||
firstEntityInitializer,
|
||||
(InitializerParent) parentAccess,
|
||||
referencedModelPart
|
||||
);
|
||||
}
|
||||
|
||||
protected static String getRootEmbeddablePropertyName(
|
||||
EntityInitializer firstEntityInitializer,
|
||||
InitializerParent parent,
|
||||
ToOneAttributeMapping referencedModelPart) {
|
||||
final NavigablePath entityPath = firstEntityInitializer.getNavigablePath();
|
||||
NavigablePath navigablePath = parentAccess.getNavigablePath();
|
||||
NavigablePath navigablePath = parent.getNavigablePath();
|
||||
if ( navigablePath == entityPath ) {
|
||||
return referencedModelPart.getPartName();
|
||||
}
|
||||
|
|
|
@ -18,48 +18,58 @@ import org.hibernate.internal.log.LoggingHelper;
|
|||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.property.access.spi.Setter;
|
||||
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;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer {
|
||||
protected final AttributeMapping[] parentAttributes;
|
||||
protected final Setter referencedModelPartSetter;
|
||||
protected final Type referencedModelPartType;
|
||||
|
||||
private Map<EntityKey, List<ParentInfo>> toBatchLoad;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #BatchEntitySelectFetchInitializer(InitializerParent, ToOneAttributeMapping, NavigablePath, EntityPersister, DomainResultAssembler)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public BatchEntitySelectFetchInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
ToOneAttributeMapping referencedModelPart,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> identifierAssembler) {
|
||||
super( parentAccess, referencedModelPart, fetchedNavigable, concreteDescriptor, identifierAssembler );
|
||||
this(
|
||||
(InitializerParent) parentAccess,
|
||||
referencedModelPart,
|
||||
fetchedNavigable,
|
||||
concreteDescriptor,
|
||||
identifierAssembler
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
|
||||
initializedEntityInstance = getExistingInitializedInstance( rowProcessingState );
|
||||
if ( initializedEntityInstance == null ) {
|
||||
// need to add the key to the batch queue only when the entity has not been already loaded or
|
||||
// there isn't another initializer that is loading it
|
||||
registerToBatchFetchQueue( rowProcessingState );
|
||||
}
|
||||
public BatchEntitySelectFetchInitializer(
|
||||
InitializerParent parentAccess,
|
||||
ToOneAttributeMapping referencedModelPart,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> identifierAssembler) {
|
||||
super( parentAccess, referencedModelPart, fetchedNavigable, concreteDescriptor, identifierAssembler );
|
||||
this.parentAttributes = getParentEntityAttributes( referencedModelPart.getAttributeName() );
|
||||
this.referencedModelPartSetter = referencedModelPart.getPropertyAccess().getSetter();
|
||||
this.referencedModelPartType = owningEntityInitializer.getEntityDescriptor().getPropertyType( referencedModelPart.getAttributeName() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerResolutionListener() {
|
||||
parentAccess.registerResolutionListener( parentInstance -> {
|
||||
final AttributeMapping parentAttribute = getParentEntityAttribute( referencedModelPart.getAttributeName() );
|
||||
if ( parentAttribute != null && !firstEntityInitializer.isEntityInitialized() ) {
|
||||
getParentInfos().add( new ParentInfo( parentInstance, parentAttribute.getStateArrayPosition() ) );
|
||||
}
|
||||
} );
|
||||
final AttributeMapping parentAttribute;
|
||||
if ( !owningEntityInitializer.isEntityInitialized() && ( parentAttribute = parentAttributes[owningEntityInitializer.getConcreteDescriptor().getSubclassId()] ) != null ) {
|
||||
getParentInfos().add( new ParentInfo( owningEntityInitializer.getTargetInstance(), parentAttribute.getStateArrayPosition() ) );
|
||||
}
|
||||
}
|
||||
|
||||
private List<ParentInfo> getParentInfos() {
|
||||
|
@ -85,24 +95,24 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect
|
|||
}
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext context) {
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
super.endLoading( executionContext );
|
||||
if ( toBatchLoad != null ) {
|
||||
toBatchLoad.forEach(
|
||||
(entityKey, parentInfos) -> {
|
||||
final SharedSessionContractImplementor session = context.getSession();
|
||||
final SharedSessionContractImplementor session = executionContext.getSession();
|
||||
final Object instance = loadInstance( entityKey, referencedModelPart, session );
|
||||
for ( ParentInfo parentInfo : parentInfos ) {
|
||||
final Object parentInstance = parentInfo.parentInstance;
|
||||
setInstance(
|
||||
firstEntityInitializer,
|
||||
referencedModelPart,
|
||||
referencedModelPart.getPartName(),
|
||||
parentInfo.propertyIndex,
|
||||
session,
|
||||
instance,
|
||||
parentInstance,
|
||||
session.getPersistenceContext().getEntry( parentInstance )
|
||||
);
|
||||
final EntityEntry entry = session.getPersistenceContext().getEntry( parentInstance );
|
||||
referencedModelPartSetter.set( parentInstance, instance );
|
||||
final Object[] loadedState = entry.getLoadedState();
|
||||
if ( loadedState != null ) {
|
||||
loadedState[parentInfo.propertyIndex] = referencedModelPartType.deepCopy(
|
||||
instance,
|
||||
session.getFactory()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -110,33 +120,6 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect
|
|||
}
|
||||
}
|
||||
|
||||
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() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BatchEntitySelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||
|
|
|
@ -18,6 +18,7 @@ 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.InitializerParent;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
/**
|
||||
|
@ -28,14 +29,32 @@ public class BatchInitializeEntitySelectFetchInitializer extends AbstractBatchEn
|
|||
|
||||
private final Set<EntityKey> toBatchLoad = new HashSet<>();
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #BatchInitializeEntitySelectFetchInitializer(InitializerParent, ToOneAttributeMapping, NavigablePath, EntityPersister, DomainResultAssembler)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public BatchInitializeEntitySelectFetchInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
ToOneAttributeMapping referencedModelPart,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> identifierAssembler) {
|
||||
super( parentAccess, referencedModelPart, fetchedNavigable, concreteDescriptor, identifierAssembler );
|
||||
this(
|
||||
(InitializerParent) parentAccess,
|
||||
referencedModelPart,
|
||||
fetchedNavigable,
|
||||
concreteDescriptor,
|
||||
identifierAssembler
|
||||
);
|
||||
}
|
||||
|
||||
public BatchInitializeEntitySelectFetchInitializer(
|
||||
InitializerParent parent,
|
||||
ToOneAttributeMapping referencedModelPart,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> identifierAssembler) {
|
||||
super( parent, referencedModelPart, fetchedNavigable, concreteDescriptor, identifierAssembler );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -44,25 +63,16 @@ public class BatchInitializeEntitySelectFetchInitializer extends AbstractBatchEn
|
|||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
initializedEntityInstance = getExistingInitializedInstance( rowProcessingState );
|
||||
if ( initializedEntityInstance == null ) {
|
||||
// need to add the key to the batch queue only when the entity has not been already loaded or
|
||||
// there isn't another initializer that is loading it
|
||||
registerToBatchFetchQueue( rowProcessingState );
|
||||
// Force creating a proxy
|
||||
initializedEntityInstance = rowProcessingState.getSession().internalLoad(
|
||||
entityKey.getEntityName(),
|
||||
entityKey.getIdentifier(),
|
||||
false,
|
||||
false
|
||||
);
|
||||
toBatchLoad.add( entityKey );
|
||||
}
|
||||
protected void registerToBatchFetchQueue(RowProcessingState rowProcessingState) {
|
||||
super.registerToBatchFetchQueue( rowProcessingState );
|
||||
// Force creating a proxy
|
||||
initializedEntityInstance = rowProcessingState.getSession().internalLoad(
|
||||
entityKey.getEntityName(),
|
||||
entityKey.getIdentifier(),
|
||||
false,
|
||||
false
|
||||
);
|
||||
toBatchLoad.add( entityKey );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -71,8 +81,9 @@ public class BatchInitializeEntitySelectFetchInitializer extends AbstractBatchEn
|
|||
}
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext context) {
|
||||
final SharedSessionContractImplementor session = context.getSession();
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
super.endLoading( executionContext );
|
||||
final SharedSessionContractImplementor session = executionContext.getSession();
|
||||
for ( EntityKey key : toBatchLoad ) {
|
||||
loadInstance( key, referencedModelPart, session );
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.sql.results.graph.Fetch;
|
|||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.entity.AbstractDiscriminatedEntityResultGraphNode;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -63,24 +64,31 @@ public class DiscriminatedEntityFetch extends AbstractDiscriminatedEntityResultG
|
|||
public DomainResultAssembler<?> createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> createAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return new EntityAssembler(
|
||||
getReferencedMappingContainer().getJavaType(),
|
||||
creationState.resolveInitializer( this, parentAccess, this ).asEntityInitializer()
|
||||
creationState.resolveInitializer( this, parent, this ).asEntityInitializer()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
DiscriminatedEntityFetch resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public Initializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new DiscriminatedEntityInitializer(
|
||||
parentAccess,
|
||||
parent,
|
||||
getReferencedMappingType(),
|
||||
getNavigablePath(),
|
||||
getDiscriminatorValueFetch(),
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.entity.internal;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.engine.spi.EntityHolder;
|
||||
|
@ -17,36 +17,33 @@ import org.hibernate.internal.log.LoggingHelper;
|
|||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityLoadingLogging;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.sql.results.graph.internal.AbstractInitializer;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import static org.hibernate.internal.log.LoggingHelper.toLoggableString;
|
||||
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
|
||||
|
||||
/**
|
||||
* Initializer for discriminated mappings.
|
||||
*/
|
||||
public class DiscriminatedEntityInitializer implements EntityInitializer {
|
||||
public class DiscriminatedEntityInitializer extends AbstractInitializer implements EntityInitializer {
|
||||
private static final String CONCRETE_NAME = DiscriminatedEntityInitializer.class.getSimpleName();
|
||||
|
||||
protected final FetchParentAccess parentAccess;
|
||||
protected final InitializerParent parent;
|
||||
private final NavigablePath navigablePath;
|
||||
private final FetchParentAccess owningParent;
|
||||
private final EntityMappingType ownedModelPartDeclaringType;
|
||||
private final boolean isPartOfKey;
|
||||
|
||||
private final DomainResultAssembler<?> discriminatorValueAssembler;
|
||||
|
@ -55,16 +52,17 @@ public class DiscriminatedEntityInitializer implements EntityInitializer {
|
|||
private final boolean eager;
|
||||
private final boolean resultInitializer;
|
||||
|
||||
protected boolean parentShallowCached;
|
||||
|
||||
// per-row state
|
||||
protected State state = State.UNINITIALIZED;
|
||||
protected EntityPersister concreteDescriptor;
|
||||
protected Object entityIdentifier;
|
||||
protected Object entityInstance;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #DiscriminatedEntityInitializer(InitializerParent, DiscriminatedAssociationModelPart, NavigablePath, Fetch, Fetch, boolean, boolean, AssemblerCreationState)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public DiscriminatedEntityInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
FetchParentAccess parent,
|
||||
DiscriminatedAssociationModelPart fetchedPart,
|
||||
NavigablePath fetchedNavigable,
|
||||
Fetch discriminatorFetch,
|
||||
|
@ -72,31 +70,45 @@ public class DiscriminatedEntityInitializer implements EntityInitializer {
|
|||
boolean eager,
|
||||
boolean resultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
this.parentAccess = parentAccess;
|
||||
this(
|
||||
(InitializerParent) parent,
|
||||
fetchedPart,
|
||||
fetchedNavigable,
|
||||
discriminatorFetch,
|
||||
keyFetch,
|
||||
eager,
|
||||
resultInitializer,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
public DiscriminatedEntityInitializer(
|
||||
InitializerParent parent,
|
||||
DiscriminatedAssociationModelPart fetchedPart,
|
||||
NavigablePath fetchedNavigable,
|
||||
Fetch discriminatorFetch,
|
||||
Fetch keyFetch,
|
||||
boolean eager,
|
||||
boolean resultInitializer,
|
||||
AssemblerCreationState creationState) {
|
||||
this.parent = parent;
|
||||
this.fetchedPart = fetchedPart;
|
||||
this.navigablePath = fetchedNavigable;
|
||||
this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parentAccess );
|
||||
this.owningParent = FetchParentAccess.determineOwningParent( parentAccess );
|
||||
this.ownedModelPartDeclaringType = FetchParentAccess.determineOwnedModelPartDeclaringType( fetchedPart, parentAccess, owningParent );
|
||||
this.discriminatorValueAssembler = discriminatorFetch.createAssembler( this, creationState );
|
||||
this.keyValueAssembler = keyFetch.createAssembler( this, creationState );
|
||||
this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parent );
|
||||
this.discriminatorValueAssembler = discriminatorFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
this.keyValueAssembler = keyFetch.createAssembler( (InitializerParent) this, creationState );
|
||||
this.eager = eager;
|
||||
this.resultInitializer = resultInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParentAccess getFetchParentAccess() {
|
||||
return parentAccess;
|
||||
return (FetchParentAccess) parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FetchParentAccess getOwningParent() {
|
||||
return owningParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable EntityMappingType getOwnedModelPartDeclaringType() {
|
||||
return ownedModelPartDeclaringType;
|
||||
public @Nullable InitializerParent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public ModelPart getInitializedPart(){
|
||||
|
@ -109,7 +121,7 @@ public class DiscriminatedEntityInitializer implements EntityInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
public void resolveKey() {
|
||||
if ( state != State.UNINITIALIZED ) {
|
||||
return;
|
||||
}
|
||||
|
@ -119,7 +131,10 @@ public class DiscriminatedEntityInitializer implements EntityInitializer {
|
|||
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState );
|
||||
|
||||
if ( discriminatorValue == null ) {
|
||||
state = State.INITIALIZED;
|
||||
state = State.MISSING;
|
||||
concreteDescriptor = null;
|
||||
entityIdentifier = null;
|
||||
entityInstance = null;
|
||||
// null association
|
||||
assert keyValueAssembler.assemble( rowProcessingState ) == null;
|
||||
}
|
||||
|
@ -131,25 +146,12 @@ public class DiscriminatedEntityInitializer implements EntityInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
public void resolveInstance() {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
state = State.RESOLVED;
|
||||
|
||||
// We can avoid processing further if the parent is already initialized or missing,
|
||||
// as the value produced by this initializer will never be used anyway.
|
||||
if ( parentShallowCached || shouldSkipInitializer( rowProcessingState ) ) {
|
||||
state = State.INITIALIZED;
|
||||
return;
|
||||
}
|
||||
|
||||
entityIdentifier = keyValueAssembler.assemble( rowProcessingState );
|
||||
|
||||
if ( entityIdentifier == null ) {
|
||||
state = State.INITIALIZED;
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
|
||||
if ( EntityLoadingLogging.ENTITY_LOADING_LOGGER.isTraceEnabled() ) {
|
||||
EntityLoadingLogging.ENTITY_LOADING_LOGGER.tracef(
|
||||
|
@ -178,7 +180,6 @@ public class DiscriminatedEntityInitializer implements EntityInitializer {
|
|||
entityInstance = holder.getEntity();
|
||||
if ( holder.getEntityInitializer() == null ) {
|
||||
if ( entityInstance != null && Hibernate.isInitialized( entityInstance ) ) {
|
||||
state = State.INITIALIZED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -192,38 +193,15 @@ public class DiscriminatedEntityInitializer implements EntityInitializer {
|
|||
holder.getEntityInitializer()
|
||||
);
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
return;
|
||||
}
|
||||
else if ( entityInstance == null ) {
|
||||
state = State.INITIALIZED;
|
||||
// todo: maybe mark this as resolved instead?
|
||||
assert holder.getProxy() == null : "How to handle this case?";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Defer the select by default to the initialize phase
|
||||
// We only need to select in this phase if this is part of an identifier or foreign key
|
||||
NavigablePath np = navigablePath.getParent();
|
||||
while ( np != null ) {
|
||||
if ( np instanceof EntityIdentifierNavigablePath
|
||||
|| ForeignKeyDescriptor.PART_NAME.equals( np.getLocalName() )
|
||||
|| ForeignKeyDescriptor.TARGET_PART_NAME.equals( np.getLocalName() )) {
|
||||
|
||||
initializeInstance( rowProcessingState );
|
||||
return;
|
||||
}
|
||||
np = np.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
if ( state != State.RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = State.INITIALIZED;
|
||||
|
||||
entityInstance = rowProcessingState.getSession().internalLoad(
|
||||
concreteDescriptor.getEntityName(),
|
||||
entityIdentifier,
|
||||
|
@ -234,34 +212,91 @@ public class DiscriminatedEntityInitializer implements EntityInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstanceFromParent(Object parentInstance, RowProcessingState rowProcessingState) {
|
||||
public void resolveInstance(Object instance) {
|
||||
if ( instance == null ) {
|
||||
state = State.MISSING;
|
||||
entityIdentifier = null;
|
||||
concreteDescriptor = null;
|
||||
entityInstance = null;
|
||||
}
|
||||
else {
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
final LazyInitializer lazyInitializer = extractLazyInitializer( entityInstance );
|
||||
if ( lazyInitializer == null ) {
|
||||
state = State.INITIALIZED;
|
||||
concreteDescriptor = session.getEntityPersister( null, instance );
|
||||
entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
|
||||
}
|
||||
else if ( lazyInitializer.isUninitialized() ) {
|
||||
state = eager ? State.RESOLVED : State.INITIALIZED;
|
||||
// Read the discriminator from the result set if necessary
|
||||
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState );
|
||||
concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister();
|
||||
entityIdentifier = lazyInitializer.getIdentifier();
|
||||
}
|
||||
else {
|
||||
state = State.INITIALIZED;
|
||||
concreteDescriptor = session.getEntityPersister( null, lazyInitializer.getImplementation() );
|
||||
entityIdentifier = lazyInitializer.getIdentifier();
|
||||
}
|
||||
entityInstance = instance;
|
||||
final Initializer initializer = keyValueAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializer.resolveInstance( entityIdentifier );
|
||||
}
|
||||
else if ( !rowProcessingState.isQueryCacheHit() && rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE ) {
|
||||
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
|
||||
discriminatorValueAssembler.resolveState( rowProcessingState );
|
||||
keyValueAssembler.resolveState( rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance() {
|
||||
if ( state != State.RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
entityInstance = rowProcessingState.getSession().internalLoad(
|
||||
concreteDescriptor.getEntityName(),
|
||||
entityIdentifier,
|
||||
eager,
|
||||
// should not be null since we checked already. null would indicate bad data (ala, not-found handling)
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstanceFromParent(Object parentInstance) {
|
||||
final AttributeMapping attributeMapping = getInitializedPart().asAttributeMapping();
|
||||
final Object instance = attributeMapping != null
|
||||
? attributeMapping.getValue( parentInstance )
|
||||
: parentInstance;
|
||||
if ( eager ) {
|
||||
Hibernate.initialize( instance );
|
||||
if ( instance == null ) {
|
||||
state = State.MISSING;
|
||||
entityInstance = null;
|
||||
entityIdentifier = null;
|
||||
concreteDescriptor = null;
|
||||
}
|
||||
else {
|
||||
state = State.INITIALIZED;
|
||||
entityInstance = instance;
|
||||
// No need to initialize this
|
||||
entityIdentifier = null;
|
||||
concreteDescriptor = null;
|
||||
if ( eager ) {
|
||||
Hibernate.initialize( instance );
|
||||
}
|
||||
}
|
||||
entityInstance = instance;
|
||||
state = State.INITIALIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
entityInstance = null;
|
||||
entityIdentifier = null;
|
||||
concreteDescriptor = null;
|
||||
state = State.UNINITIALIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markShallowCached() {
|
||||
parentShallowCached = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
parentShallowCached = false;
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
final Initializer initializer = keyValueAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -299,13 +334,6 @@ public class DiscriminatedEntityInitializer implements EntityInitializer {
|
|||
return "DiscriminatedEntityInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||
}
|
||||
|
||||
protected enum State {
|
||||
UNINITIALIZED,
|
||||
KEY_RESOLVED,
|
||||
RESOLVED,
|
||||
INITIALIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityKey getEntityKey() {
|
||||
throw new UnsupportedOperationException(
|
||||
|
@ -317,10 +345,4 @@ public class DiscriminatedEntityInitializer implements EntityInitializer {
|
|||
throw new UnsupportedOperationException(
|
||||
"This should never happen, because this initializer has not child initializers" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> listener) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This should never happen, because this initializer has not child initializers" );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.hibernate.sql.results.graph.DomainResultAssembler;
|
|||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.entity.AbstractDiscriminatedEntityResultGraphNode;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -43,25 +44,32 @@ public class DiscriminatedEntityResult<T> extends AbstractDiscriminatedEntityRes
|
|||
public DomainResultAssembler<T> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<T> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
//noinspection unchecked
|
||||
return new EntityAssembler(
|
||||
getReferencedMappingContainer().getJavaType(),
|
||||
creationState.resolveInitializer( this, parentAccess, this ).asEntityInitializer()
|
||||
creationState.resolveInitializer( this, parent, this ).asEntityInitializer()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
DiscriminatedEntityResult<T> resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public Initializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new DiscriminatedEntityInitializer(
|
||||
parentAccess,
|
||||
parent,
|
||||
getReferencedMappingType(),
|
||||
getNavigablePath(),
|
||||
getDiscriminatorValueFetch(),
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.entity.internal;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
|
@ -36,7 +38,7 @@ public class EntityAssembler implements DomainResultAssembler {
|
|||
// Ensure that the instance really is initialized
|
||||
// This is important for key-many-to-ones that are part of a collection key fk,
|
||||
// as the instance is needed for resolveKey before initializing the instance in RowReader
|
||||
initializer.resolveInstance( rowProcessingState );
|
||||
initializer.resolveInstance();
|
||||
return initializer.getEntityInstance();
|
||||
}
|
||||
|
||||
|
@ -44,4 +46,11 @@ public class EntityAssembler implements DomainResultAssembler {
|
|||
public EntityInitializer getInitializer() {
|
||||
return initializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachResultAssembler(BiConsumer consumer, Object arg) {
|
||||
if ( initializer.isResultInitializer() ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.hibernate.sql.results.graph.AssemblerCreationState;
|
|||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
|
||||
|
@ -46,15 +46,15 @@ public class EntityDelayedFetchImpl extends AbstractNonJoinedEntityFetch {
|
|||
}
|
||||
|
||||
@Override
|
||||
public EntityInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public EntityInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new EntityDelayedFetchInitializer(
|
||||
parentAccess,
|
||||
parent,
|
||||
getNavigablePath(),
|
||||
getEntityValuedModelPart(),
|
||||
isSelectByUniqueKey(),
|
||||
getKeyResult().createResultAssembler( parentAccess, creationState ),
|
||||
getKeyResult().createResultAssembler( parent, creationState ),
|
||||
getDiscriminatorFetch() != null
|
||||
? (BasicResultAssembler<?>) getDiscriminatorFetch().createResultAssembler( parentAccess, creationState )
|
||||
? (BasicResultAssembler<?>) getDiscriminatorFetch().createResultAssembler( parent, creationState )
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.entity.internal;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.FetchNotFoundException;
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
|
@ -16,49 +16,47 @@ import org.hibernate.engine.spi.EntityUniqueKey;
|
|||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.log.LoggingHelper;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
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.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.sql.results.graph.internal.AbstractInitializer;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import static org.hibernate.sql.results.graph.entity.AbstractEntityInitializer.determineConcreteEntityDescriptor;
|
||||
import static org.hibernate.sql.results.graph.entity.internal.EntityInitializerImpl.determineConcreteEntityDescriptor;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EntityDelayedFetchInitializer implements EntityInitializer {
|
||||
public class EntityDelayedFetchInitializer extends AbstractInitializer implements EntityInitializer {
|
||||
|
||||
private final FetchParentAccess parentAccess;
|
||||
private final InitializerParent parent;
|
||||
private final NavigablePath navigablePath;
|
||||
private final FetchParentAccess owningParent;
|
||||
private final EntityMappingType ownedModelPartDeclaringType;
|
||||
private final boolean isPartOfKey;
|
||||
private final ToOneAttributeMapping referencedModelPart;
|
||||
private final boolean selectByUniqueKey;
|
||||
private final DomainResultAssembler<?> identifierAssembler;
|
||||
private final BasicResultAssembler<?> discriminatorAssembler;
|
||||
|
||||
protected boolean parentShallowCached;
|
||||
|
||||
// per-row state
|
||||
private boolean processed;
|
||||
private Object entityInstance;
|
||||
private Object identifier;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EntityDelayedFetchInitializer(InitializerParent, NavigablePath, ToOneAttributeMapping, boolean, DomainResultAssembler, BasicResultAssembler)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public EntityDelayedFetchInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
NavigablePath fetchedNavigable,
|
||||
|
@ -66,14 +64,29 @@ public class EntityDelayedFetchInitializer implements EntityInitializer {
|
|||
boolean selectByUniqueKey,
|
||||
DomainResultAssembler<?> identifierAssembler,
|
||||
BasicResultAssembler<?> discriminatorAssembler) {
|
||||
this(
|
||||
(InitializerParent) parentAccess,
|
||||
fetchedNavigable,
|
||||
referencedModelPart,
|
||||
selectByUniqueKey,
|
||||
identifierAssembler,
|
||||
discriminatorAssembler
|
||||
);
|
||||
}
|
||||
|
||||
public EntityDelayedFetchInitializer(
|
||||
InitializerParent parent,
|
||||
NavigablePath fetchedNavigable,
|
||||
ToOneAttributeMapping referencedModelPart,
|
||||
boolean selectByUniqueKey,
|
||||
DomainResultAssembler<?> identifierAssembler,
|
||||
BasicResultAssembler<?> discriminatorAssembler) {
|
||||
// associations marked with `@NotFound` are ALWAYS eagerly fetched, unless we're resolving the concrete type
|
||||
assert !referencedModelPart.hasNotFoundAction() || referencedModelPart.getEntityMappingType().isConcreteProxy();
|
||||
|
||||
this.parentAccess = parentAccess;
|
||||
this.parent = parent;
|
||||
this.navigablePath = fetchedNavigable;
|
||||
this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parentAccess );
|
||||
this.owningParent = FetchParentAccess.determineOwningParent( parentAccess );
|
||||
this.ownedModelPartDeclaringType = FetchParentAccess.determineOwnedModelPartDeclaringType( referencedModelPart, parentAccess, owningParent );
|
||||
this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parent );
|
||||
this.referencedModelPart = referencedModelPart;
|
||||
this.selectByUniqueKey = selectByUniqueKey;
|
||||
this.identifierAssembler = identifierAssembler;
|
||||
|
@ -85,34 +98,24 @@ public class EntityDelayedFetchInitializer implements EntityInitializer {
|
|||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart getInitializedPart() {
|
||||
return referencedModelPart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
if ( processed ) {
|
||||
public void resolveInstance() {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
processed = true;
|
||||
|
||||
// We can avoid processing further if the parent is already initialized or missing,
|
||||
// as the value produced by this initializer will never be used anyway.
|
||||
if ( parentShallowCached || shouldSkipInitializer( rowProcessingState ) ) {
|
||||
return;
|
||||
}
|
||||
state = State.RESOLVED;
|
||||
|
||||
identifier = identifierAssembler.assemble( rowProcessingState );
|
||||
|
||||
if ( identifier == null ) {
|
||||
entityInstance = null;
|
||||
state = State.MISSING;
|
||||
}
|
||||
else {
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
|
@ -131,6 +134,7 @@ public class EntityDelayedFetchInitializer implements EntityInitializer {
|
|||
throw new FetchNotFoundException( entityPersister.getEntityName(), identifier );
|
||||
}
|
||||
entityInstance = null;
|
||||
state = State.MISSING;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -210,25 +214,35 @@ public class EntityDelayedFetchInitializer implements EntityInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
// nothing to do
|
||||
public void resolveInstance(Object instance) {
|
||||
if ( instance == null ) {
|
||||
state = State.MISSING;
|
||||
identifier = null;
|
||||
entityInstance = null;
|
||||
}
|
||||
else {
|
||||
state = State.RESOLVED;
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
final EntityPersister concreteDescriptor = referencedModelPart.getEntityMappingType().getEntityPersister();
|
||||
identifier = concreteDescriptor.getIdentifier( instance, session );
|
||||
entityInstance = instance;
|
||||
final Initializer initializer = identifierAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializer.resolveInstance( identifier );
|
||||
}
|
||||
else if ( !rowProcessingState.isQueryCacheHit() && rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE ) {
|
||||
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
|
||||
identifierAssembler.resolveState( rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
entityInstance = null;
|
||||
identifier = null;
|
||||
processed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markShallowCached() {
|
||||
parentShallowCached = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
parentShallowCached = false;
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
final Initializer initializer = identifierAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -248,17 +262,12 @@ public class EntityDelayedFetchInitializer implements EntityInitializer {
|
|||
|
||||
@Override
|
||||
public FetchParentAccess getFetchParentAccess() {
|
||||
return parentAccess;
|
||||
return (FetchParentAccess) parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FetchParentAccess getOwningParent() {
|
||||
return owningParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable EntityMappingType getOwnedModelPartDeclaringType() {
|
||||
return ownedModelPartDeclaringType;
|
||||
public @Nullable InitializerParent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -288,14 +297,6 @@ public class EntityDelayedFetchInitializer implements EntityInitializer {
|
|||
this.entityInstance = entityInstance;
|
||||
}
|
||||
|
||||
protected void setProcessed(boolean processed) {
|
||||
this.processed = processed;
|
||||
}
|
||||
|
||||
protected boolean isProcessed() {
|
||||
return processed;
|
||||
}
|
||||
|
||||
protected Object getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
@ -323,10 +324,4 @@ public class EntityDelayedFetchInitializer implements EntityInitializer {
|
|||
throw new UnsupportedOperationException(
|
||||
"This should never happen, because this initializer has not child initializers" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> listener) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This should never happen, because this initializer has not child initializers" );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,31 +10,31 @@ import java.util.BitSet;
|
|||
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.graph.AbstractFetchParent;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.FetchableContainer;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityFetch;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
|
||||
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EntityFetchJoinedImpl extends AbstractFetchParent implements EntityFetch,
|
||||
InitializerProducer<EntityFetchJoinedImpl> {
|
||||
public class EntityFetchJoinedImpl implements EntityFetch, FetchParent, InitializerProducer<EntityFetchJoinedImpl> {
|
||||
private final FetchParent fetchParent;
|
||||
private final EntityValuedFetchable fetchContainer;
|
||||
private final EntityResultImpl entityResult;
|
||||
|
@ -50,7 +50,6 @@ public class EntityFetchJoinedImpl extends AbstractFetchParent implements Entity
|
|||
DomainResult<?> keyResult,
|
||||
NavigablePath navigablePath,
|
||||
DomainResultCreationState creationState) {
|
||||
super( navigablePath );
|
||||
this.fetchContainer = toOneMapping;
|
||||
this.fetchParent = fetchParent;
|
||||
this.keyResult = keyResult;
|
||||
|
@ -73,7 +72,6 @@ public class EntityFetchJoinedImpl extends AbstractFetchParent implements Entity
|
|||
TableGroup tableGroup,
|
||||
NavigablePath navigablePath,
|
||||
DomainResultCreationState creationState) {
|
||||
super( navigablePath );
|
||||
this.fetchContainer = collectionPart;
|
||||
this.fetchParent = fetchParent;
|
||||
this.notFoundAction = collectionPart.getNotFoundAction();
|
||||
|
@ -94,7 +92,6 @@ public class EntityFetchJoinedImpl extends AbstractFetchParent implements Entity
|
|||
* For Hibernate Reactive
|
||||
*/
|
||||
protected EntityFetchJoinedImpl(EntityFetchJoinedImpl original) {
|
||||
super( original.getNavigablePath() );
|
||||
this.fetchContainer = original.fetchContainer;
|
||||
this.fetchParent = original.fetchParent;
|
||||
this.entityResult = original.entityResult;
|
||||
|
@ -108,11 +105,6 @@ public class EntityFetchJoinedImpl extends AbstractFetchParent implements Entity
|
|||
return fetchContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchableContainer getFetchContainer() {
|
||||
return fetchContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityValuedFetchable getReferencedModePart() {
|
||||
return getEntityValuedModelPart();
|
||||
|
@ -123,11 +115,6 @@ public class EntityFetchJoinedImpl extends AbstractFetchParent implements Entity
|
|||
return getEntityValuedModelPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType getReferencedMappingContainer() {
|
||||
return getEntityValuedModelPart().getEntityMappingType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityValuedFetchable getFetchedMapping() {
|
||||
return getEntityValuedModelPart();
|
||||
|
@ -142,7 +129,14 @@ public class EntityFetchJoinedImpl extends AbstractFetchParent implements Entity
|
|||
public DomainResultAssembler<?> createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return buildEntityAssembler( creationState.resolveInitializer( this, parentAccess, this ).asEntityInitializer() );
|
||||
return createAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<?> createAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return buildEntityAssembler( creationState.resolveInitializer( this, parent, this ).asEntityInitializer() );
|
||||
}
|
||||
|
||||
protected EntityAssembler buildEntityAssembler(EntityInitializer entityInitializer) {
|
||||
|
@ -152,26 +146,50 @@ public class EntityFetchJoinedImpl extends AbstractFetchParent implements Entity
|
|||
@Override
|
||||
public Initializer createInitializer(
|
||||
EntityFetchJoinedImpl resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
return new EntityJoinedFetchInitializer(
|
||||
entityResult,
|
||||
getReferencedModePart(),
|
||||
getNavigablePath(),
|
||||
public EntityInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new EntityInitializerImpl(
|
||||
this,
|
||||
creationState.determineEffectiveLockMode( sourceAlias ),
|
||||
notFoundAction,
|
||||
keyResult,
|
||||
entityResult.getRowIdResult(),
|
||||
entityResult.getIdentifierFetch(),
|
||||
entityResult.getDiscriminatorFetch(),
|
||||
parentAccess,
|
||||
keyResult,
|
||||
entityResult.getRowIdResult(),
|
||||
notFoundAction,
|
||||
parent,
|
||||
false,
|
||||
creationState
|
||||
);
|
||||
// return new EntityJoinedFetchInitializer(
|
||||
// entityResult,
|
||||
// getReferencedModePart(),
|
||||
// getNavigablePath(),
|
||||
// creationState.determineEffectiveLockMode( sourceAlias ),
|
||||
// notFoundAction,
|
||||
// keyResult,
|
||||
// entityResult.getRowIdResult(),
|
||||
// entityResult.getIdentifierFetch(),
|
||||
// entityResult.getDiscriminatorFetch(),
|
||||
// parentAccess,
|
||||
// creationState
|
||||
// );
|
||||
// return new EntityInitializerImpl(
|
||||
// this,
|
||||
// creationState.determineEffectiveLockMode( sourceAlias ),
|
||||
// entityResult.getIdentifierFetch(),
|
||||
// entityResult.getDiscriminatorFetch(),
|
||||
// keyResult,
|
||||
// entityResult.getRowIdResult(),
|
||||
// notFoundAction,
|
||||
// parentAccess,
|
||||
// false,
|
||||
// creationState
|
||||
// );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -184,15 +202,35 @@ public class EntityFetchJoinedImpl extends AbstractFetchParent implements Entity
|
|||
return true;
|
||||
}
|
||||
|
||||
public EntityResultImpl getEntityResult() {
|
||||
return entityResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return entityResult.getNavigablePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableFetchList getFetches() {
|
||||
return entityResult.getFetches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch findFetch(Fetchable fetchable) {
|
||||
return entityResult.findFetch( fetchable );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasJoinFetches() {
|
||||
return entityResult.hasJoinFetches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsCollectionFetches() {
|
||||
return entityResult.containsCollectionFetches();
|
||||
}
|
||||
|
||||
public EntityResultImpl getEntityResult() {
|
||||
return entityResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectValueIndexesToCache(BitSet valueIndexes) {
|
||||
entityResult.collectValueIndexesToCache( valueIndexes );
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.hibernate.sql.results.graph.AssemblerCreationState;
|
|||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
|
||||
/**
|
||||
|
@ -53,9 +53,9 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
|
|||
}
|
||||
|
||||
@Override
|
||||
public EntityInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
public EntityInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return EntitySelectFetchInitializerBuilder.createInitializer(
|
||||
parentAccess,
|
||||
parent,
|
||||
getFetchedMapping(),
|
||||
getReferencedMappingContainer().getEntityPersister(),
|
||||
getKeyResult(),
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* 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.FetchNotFoundException;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
import org.hibernate.internal.log.LoggingHelper;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityLoadingLogging;
|
||||
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
|
||||
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class EntityJoinedFetchInitializer extends AbstractEntityInitializer {
|
||||
private static final String CONCRETE_NAME = EntityJoinedFetchInitializer.class.getSimpleName();
|
||||
|
||||
private final DomainResultAssembler<?> keyAssembler;
|
||||
private final NotFoundAction notFoundAction;
|
||||
|
||||
public EntityJoinedFetchInitializer(
|
||||
EntityResultGraphNode resultDescriptor,
|
||||
EntityValuedFetchable referencedFetchable,
|
||||
NavigablePath navigablePath,
|
||||
LockMode lockMode,
|
||||
NotFoundAction notFoundAction,
|
||||
DomainResult<?> keyResult,
|
||||
DomainResult<Object> rowIdResult,
|
||||
Fetch identifierFetch,
|
||||
Fetch discriminatorFetch,
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
super(
|
||||
resultDescriptor,
|
||||
navigablePath,
|
||||
lockMode,
|
||||
identifierFetch,
|
||||
discriminatorFetch,
|
||||
rowIdResult,
|
||||
parentAccess,
|
||||
creationState
|
||||
);
|
||||
assert getInitializedPart() == referencedFetchable;
|
||||
this.notFoundAction = notFoundAction;
|
||||
|
||||
this.keyAssembler = keyResult == null ? null : keyResult.createResultAssembler( this, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
if ( isParentShallowCached() ) {
|
||||
state = State.MISSING;
|
||||
}
|
||||
else if ( state == State.UNINITIALIZED ) {
|
||||
if ( shouldSkipInitializer( rowProcessingState ) ) {
|
||||
state = State.MISSING;
|
||||
return;
|
||||
}
|
||||
|
||||
super.resolveKey( rowProcessingState );
|
||||
|
||||
// super processes the foreign-key target column. here we
|
||||
// need to also look at the foreign-key value column to check
|
||||
// for a dangling foreign-key
|
||||
|
||||
if ( keyAssembler != null ) {
|
||||
final Object fkKeyValue = keyAssembler.assemble( rowProcessingState );
|
||||
if ( fkKeyValue != null ) {
|
||||
if ( state == State.MISSING ) {
|
||||
if ( notFoundAction != NotFoundAction.IGNORE ) {
|
||||
throw new FetchNotFoundException(
|
||||
getEntityDescriptor().getEntityName(),
|
||||
fkKeyValue
|
||||
);
|
||||
}
|
||||
else {
|
||||
EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
|
||||
"Ignoring dangling foreign-key due to `@NotFound(IGNORE); association will be null - %s",
|
||||
getNavigablePath()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstanceFromParent(Object parentInstance, RowProcessingState rowProcessingState) {
|
||||
final AttributeMapping attributeMapping = getInitializedPart().asAttributeMapping();
|
||||
final Object instance = attributeMapping != null
|
||||
? attributeMapping.getValue( parentInstance )
|
||||
: parentInstance;
|
||||
setEntityInstance( instance );
|
||||
setEntityInstanceForNotify( Hibernate.unproxy( instance ) );
|
||||
state = State.INITIALIZED;
|
||||
initializeSubInstancesFromParent( rowProcessingState );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSimpleConcreteImplName() {
|
||||
return CONCRETE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultInitializer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EntityJoinedFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.sql.results.graph.entity.internal;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
|
@ -18,6 +19,7 @@ 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.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.InitializerProducer;
|
||||
import org.hibernate.sql.results.graph.entity.AbstractEntityResultGraphNode;
|
||||
import org.hibernate.sql.results.graph.entity.EntityResult;
|
||||
|
@ -81,29 +83,39 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode
|
|||
public DomainResultAssembler createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return new EntityAssembler(
|
||||
this.getResultJavaType(),
|
||||
creationState.resolveInitializer( this, parentAccess, this ).asEntityInitializer()
|
||||
creationState.resolveInitializer( this, parent, this ).asEntityInitializer()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(
|
||||
EntityResultImpl resultGraphNode,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return resultGraphNode.createInitializer( parentAccess, creationState );
|
||||
return resultGraphNode.createInitializer( parent, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
|
||||
return new EntityResultInitializer(
|
||||
public Initializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new EntityInitializerImpl(
|
||||
this,
|
||||
getNavigablePath(),
|
||||
getLockMode( creationState ),
|
||||
getIdentifierFetch(),
|
||||
getDiscriminatorFetch(),
|
||||
null,
|
||||
getRowIdResult(),
|
||||
NotFoundAction.EXCEPTION,
|
||||
null,
|
||||
true,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* 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.LockMode;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
|
||||
|
||||
/**
|
||||
* Initializer for cases where the entity is a root domain selection
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EntityResultInitializer extends AbstractEntityInitializer {
|
||||
private static final String CONCRETE_NAME = EntityResultInitializer.class.getSimpleName();
|
||||
|
||||
public EntityResultInitializer(
|
||||
EntityResultGraphNode resultDescriptor,
|
||||
NavigablePath navigablePath,
|
||||
LockMode lockMode,
|
||||
Fetch identifierFetch,
|
||||
BasicFetch<?> discriminatorFetch,
|
||||
DomainResult<Object> rowIdResult,
|
||||
AssemblerCreationState creationState) {
|
||||
super(
|
||||
resultDescriptor,
|
||||
navigablePath,
|
||||
lockMode,
|
||||
identifierFetch,
|
||||
discriminatorFetch,
|
||||
rowIdResult,
|
||||
null,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSimpleConcreteImplName() {
|
||||
return CONCRETE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return CONCRETE_NAME + "(" + getNavigablePath() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartOfKey() {
|
||||
// The entity result itself can never be part of the key
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultInitializer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,8 @@ import org.hibernate.spi.EntityIdentifierNavigablePath;
|
|||
import org.hibernate.spi.NavigablePath;
|
||||
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.InitializerParent;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
/**
|
||||
|
@ -25,59 +27,31 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
|||
public class EntitySelectFetchByUniqueKeyInitializer extends EntitySelectFetchInitializer {
|
||||
private final ToOneAttributeMapping fetchedAttribute;
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EntitySelectFetchByUniqueKeyInitializer(InitializerParent, ToOneAttributeMapping, NavigablePath, EntityPersister, DomainResultAssembler)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public EntitySelectFetchByUniqueKeyInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
ToOneAttributeMapping fetchedAttribute,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> keyAssembler) {
|
||||
super( parentAccess, fetchedAttribute, fetchedNavigable, concreteDescriptor, keyAssembler );
|
||||
this( (InitializerParent) parentAccess, fetchedAttribute, fetchedNavigable, concreteDescriptor, keyAssembler );
|
||||
}
|
||||
|
||||
public EntitySelectFetchByUniqueKeyInitializer(
|
||||
InitializerParent parent,
|
||||
ToOneAttributeMapping fetchedAttribute,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> keyAssembler) {
|
||||
super( parent, fetchedAttribute, fetchedNavigable, concreteDescriptor, keyAssembler );
|
||||
this.fetchedAttribute = fetchedAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
if ( state != State.UNINITIALIZED ) {
|
||||
return;
|
||||
}
|
||||
state = State.RESOLVED;
|
||||
|
||||
// We can avoid processing further if the parent is already initialized or missing,
|
||||
// as the value produced by this initializer will never be used anyway.
|
||||
if ( parentShallowCached || shouldSkipInitializer( rowProcessingState ) ) {
|
||||
state = State.INITIALIZED;
|
||||
return;
|
||||
}
|
||||
|
||||
entityIdentifier = keyAssembler.assemble( rowProcessingState );
|
||||
if ( entityIdentifier == null ) {
|
||||
state = State.INITIALIZED;
|
||||
return;
|
||||
}
|
||||
|
||||
// Defer the select by default to the initialize phase
|
||||
// We only need to select in this phase if this is part of an identifier or foreign key
|
||||
NavigablePath np = getNavigablePath().getParent();
|
||||
while ( np != null ) {
|
||||
if ( np instanceof EntityIdentifierNavigablePath
|
||||
|| ForeignKeyDescriptor.PART_NAME.equals( np.getLocalName() )
|
||||
|| ForeignKeyDescriptor.TARGET_PART_NAME.equals( np.getLocalName() )) {
|
||||
|
||||
initializeInstance( rowProcessingState );
|
||||
return;
|
||||
}
|
||||
np = np.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
if ( state != State.RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
|
||||
protected void initialize(RowProcessingState rowProcessingState) {
|
||||
final String entityName = concreteDescriptor.getEntityName();
|
||||
final String uniqueKeyPropertyName = fetchedAttribute.getReferencedPropertyName();
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.entity.internal;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.FetchNotFoundException;
|
||||
import org.hibernate.Hibernate;
|
||||
|
@ -16,39 +16,35 @@ 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.internal.util.StringHelper;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||
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.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityLoadingLogging;
|
||||
import org.hibernate.sql.results.graph.internal.AbstractInitializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import static org.hibernate.internal.log.LoggingHelper.toLoggableString;
|
||||
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class EntitySelectFetchInitializer implements EntityInitializer {
|
||||
public class EntitySelectFetchInitializer extends AbstractInitializer implements EntityInitializer {
|
||||
private static final String CONCRETE_NAME = EntitySelectFetchInitializer.class.getSimpleName();
|
||||
|
||||
protected final FetchParentAccess parentAccess;
|
||||
protected final InitializerParent parent;
|
||||
private final NavigablePath navigablePath;
|
||||
private final FetchParentAccess owningParent;
|
||||
private final EntityMappingType ownedModelPartDeclaringType;
|
||||
private final boolean isPartOfKey;
|
||||
private final boolean isEnhancedForLazyLoading;
|
||||
|
||||
|
@ -56,25 +52,33 @@ public class EntitySelectFetchInitializer implements EntityInitializer {
|
|||
protected final DomainResultAssembler<?> keyAssembler;
|
||||
private final ToOneAttributeMapping toOneMapping;
|
||||
|
||||
protected boolean parentShallowCached;
|
||||
|
||||
// per-row state
|
||||
protected State state = State.UNINITIALIZED;
|
||||
protected Object entityIdentifier;
|
||||
protected Object entityInstance;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EntitySelectFetchInitializer(InitializerParent, ToOneAttributeMapping, NavigablePath, EntityPersister, DomainResultAssembler)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public EntitySelectFetchInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
FetchParentAccess parent,
|
||||
ToOneAttributeMapping toOneMapping,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> keyAssembler) {
|
||||
this.parentAccess = parentAccess;
|
||||
this( (InitializerParent) parent, toOneMapping, fetchedNavigable, concreteDescriptor, keyAssembler );
|
||||
}
|
||||
|
||||
public EntitySelectFetchInitializer(
|
||||
InitializerParent parent,
|
||||
ToOneAttributeMapping toOneMapping,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler<?> keyAssembler) {
|
||||
this.parent = parent;
|
||||
this.toOneMapping = toOneMapping;
|
||||
this.navigablePath = fetchedNavigable;
|
||||
this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parentAccess );
|
||||
this.owningParent = FetchParentAccess.determineOwningParent( parentAccess );
|
||||
this.ownedModelPartDeclaringType = FetchParentAccess.determineOwnedModelPartDeclaringType( toOneMapping, parentAccess, owningParent );
|
||||
this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parent );
|
||||
this.concreteDescriptor = concreteDescriptor;
|
||||
this.keyAssembler = keyAssembler;
|
||||
this.isEnhancedForLazyLoading = concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading();
|
||||
|
@ -86,17 +90,12 @@ public class EntitySelectFetchInitializer implements EntityInitializer {
|
|||
|
||||
@Override
|
||||
public FetchParentAccess getFetchParentAccess() {
|
||||
return parentAccess;
|
||||
return (FetchParentAccess) parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FetchParentAccess getOwningParent() {
|
||||
return owningParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable EntityMappingType getOwnedModelPartDeclaringType() {
|
||||
return ownedModelPartDeclaringType;
|
||||
public @Nullable InitializerParent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -105,39 +104,66 @@ public class EntitySelectFetchInitializer implements EntityInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
if ( state != State.UNINITIALIZED ) {
|
||||
return;
|
||||
}
|
||||
state = State.RESOLVED;
|
||||
|
||||
// We can avoid processing further if the parent is already initialized or missing,
|
||||
// as the value produced by this initializer will never be used anyway.
|
||||
if ( parentShallowCached || shouldSkipInitializer( rowProcessingState ) ) {
|
||||
state = State.INITIALIZED;
|
||||
public void resolveInstance() {
|
||||
if ( state != State.KEY_RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
entityIdentifier = keyAssembler.assemble( rowProcessingState );
|
||||
|
||||
if ( entityIdentifier == null ) {
|
||||
state = State.INITIALIZED;
|
||||
state = State.MISSING;
|
||||
entityInstance = null;
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
initialize( rowProcessingState );
|
||||
}
|
||||
|
||||
if ( EntityLoadingLogging.ENTITY_LOADING_LOGGER.isTraceEnabled() ) {
|
||||
EntityLoadingLogging.ENTITY_LOADING_LOGGER.tracef(
|
||||
"(%s) Beginning Initializer#resolveInstance process for entity (%s) : %s",
|
||||
StringHelper.collapse( this.getClass().getName() ),
|
||||
getNavigablePath(),
|
||||
entityIdentifier
|
||||
);
|
||||
@Override
|
||||
public void resolveInstance(Object instance) {
|
||||
if ( instance == null ) {
|
||||
state = State.MISSING;
|
||||
entityIdentifier = null;
|
||||
entityInstance = null;
|
||||
}
|
||||
else {
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
final LazyInitializer lazyInitializer = extractLazyInitializer( entityInstance );
|
||||
if ( lazyInitializer == null ) {
|
||||
state = State.INITIALIZED;
|
||||
entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
|
||||
}
|
||||
else if ( lazyInitializer.isUninitialized() ) {
|
||||
state = State.RESOLVED;
|
||||
entityIdentifier = lazyInitializer.getIdentifier();
|
||||
}
|
||||
else {
|
||||
state = State.INITIALIZED;
|
||||
entityIdentifier = lazyInitializer.getIdentifier();
|
||||
}
|
||||
entityInstance = instance;
|
||||
final Initializer initializer = keyAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
initializer.resolveInstance( entityIdentifier );
|
||||
}
|
||||
else if ( !rowProcessingState.isQueryCacheHit() && rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE ) {
|
||||
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
|
||||
keyAssembler.resolveState( rowProcessingState );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance() {
|
||||
if ( state != State.RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
Hibernate.initialize( entityInstance );
|
||||
}
|
||||
|
||||
protected void initialize(RowProcessingState rowProcessingState) {
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
final EntityKey entityKey = new EntityKey( entityIdentifier, concreteDescriptor );
|
||||
|
||||
|
@ -175,33 +201,13 @@ public class EntitySelectFetchInitializer implements EntityInitializer {
|
|||
return;
|
||||
}
|
||||
else if ( entityInstance == null ) {
|
||||
// todo: maybe mark this as resolved instead?
|
||||
assert holder.getProxy() == null : "How to handle this case?";
|
||||
state = State.INITIALIZED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Defer the select by default to the initialize phase
|
||||
// We only need to select in this phase if this is part of an identifier or foreign key
|
||||
NavigablePath np = navigablePath.getParent();
|
||||
while ( np != null ) {
|
||||
if ( np instanceof EntityIdentifierNavigablePath
|
||||
|| ForeignKeyDescriptor.PART_NAME.equals( np.getLocalName() )
|
||||
|| ForeignKeyDescriptor.TARGET_PART_NAME.equals( np.getLocalName() )) {
|
||||
|
||||
initializeInstance( rowProcessingState );
|
||||
return;
|
||||
}
|
||||
np = np.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
if ( state != State.RESOLVED ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
final String entityName = concreteDescriptor.getEntityName();
|
||||
|
||||
if ( EntityLoadingLogging.ENTITY_LOADING_LOGGER.isDebugEnabled() ) {
|
||||
|
@ -248,30 +254,31 @@ public class EntitySelectFetchInitializer implements EntityInitializer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstanceFromParent(Object parentInstance, RowProcessingState rowProcessingState) {
|
||||
public void initializeInstanceFromParent(Object parentInstance) {
|
||||
final AttributeMapping attributeMapping = getInitializedPart().asAttributeMapping();
|
||||
final Object instance = attributeMapping != null
|
||||
? attributeMapping.getValue( parentInstance )
|
||||
: parentInstance;
|
||||
Hibernate.initialize( instance );
|
||||
entityInstance = instance;
|
||||
state = State.INITIALIZED;
|
||||
if ( instance == null ) {
|
||||
state = State.MISSING;
|
||||
entityIdentifier = null;
|
||||
entityInstance = null;
|
||||
}
|
||||
else {
|
||||
state = State.INITIALIZED;
|
||||
// No need to initialize this
|
||||
entityIdentifier = null;
|
||||
entityInstance = instance;
|
||||
Hibernate.initialize( instance );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
entityInstance = null;
|
||||
state = State.UNINITIALIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markShallowCached() {
|
||||
parentShallowCached = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
parentShallowCached = false;
|
||||
protected <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
final Initializer initializer = keyAssembler.getInitializer();
|
||||
if ( initializer != null ) {
|
||||
consumer.accept( initializer, arg );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -309,12 +316,6 @@ public class EntitySelectFetchInitializer implements EntityInitializer {
|
|||
return "EntitySelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
|
||||
}
|
||||
|
||||
protected enum State {
|
||||
UNINITIALIZED,
|
||||
RESOLVED,
|
||||
INITIALIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityKey getEntityKey() {
|
||||
throw new UnsupportedOperationException(
|
||||
|
@ -326,10 +327,4 @@ public class EntitySelectFetchInitializer implements EntityInitializer {
|
|||
throw new UnsupportedOperationException(
|
||||
"This should never happen, because this initializer has not child initializers" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> listener) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This should never happen, because this initializer has not child initializers" );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ 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.Initializer;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
|
||||
|
@ -34,61 +35,79 @@ public class EntitySelectFetchInitializerBuilder {
|
|||
NavigablePath navigablePath,
|
||||
boolean selectByUniqueKey,
|
||||
AssemblerCreationState creationState) {
|
||||
return createInitializer(
|
||||
(InitializerParent) parentAccess,
|
||||
fetchedAttribute,
|
||||
entityPersister,
|
||||
keyResult,
|
||||
navigablePath,
|
||||
selectByUniqueKey,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
public static EntityInitializer createInitializer(
|
||||
InitializerParent parent,
|
||||
ToOneAttributeMapping fetchedAttribute,
|
||||
EntityPersister entityPersister,
|
||||
DomainResult<?> keyResult,
|
||||
NavigablePath navigablePath,
|
||||
boolean selectByUniqueKey,
|
||||
AssemblerCreationState creationState) {
|
||||
if ( selectByUniqueKey ) {
|
||||
return new EntitySelectFetchByUniqueKeyInitializer(
|
||||
parentAccess,
|
||||
parent,
|
||||
fetchedAttribute,
|
||||
navigablePath,
|
||||
entityPersister,
|
||||
keyResult.createResultAssembler( parentAccess, creationState )
|
||||
keyResult.createResultAssembler( parent, creationState )
|
||||
);
|
||||
}
|
||||
if ( parentAccess.findFirstEntityInitializer() == null ) {
|
||||
// Batch initializers require parentAccess.findFirstEntityInitializer() != null
|
||||
if ( !parent.isEntityInitializer() && parent.findOwningEntityInitializer() == null ) {
|
||||
// Batch initializers require an owning parent initializer
|
||||
return new EntitySelectFetchInitializer(
|
||||
parentAccess,
|
||||
parent,
|
||||
fetchedAttribute,
|
||||
navigablePath,
|
||||
entityPersister,
|
||||
keyResult.createResultAssembler( parentAccess, creationState )
|
||||
keyResult.createResultAssembler( parent, creationState )
|
||||
);
|
||||
}
|
||||
final BatchMode batchMode = determineBatchMode( entityPersister, parentAccess, creationState );
|
||||
final BatchMode batchMode = determineBatchMode( entityPersister, parent, creationState );
|
||||
switch ( batchMode ) {
|
||||
case NONE:
|
||||
return new EntitySelectFetchInitializer(
|
||||
parentAccess,
|
||||
parent,
|
||||
fetchedAttribute,
|
||||
navigablePath,
|
||||
entityPersister,
|
||||
keyResult.createResultAssembler( parentAccess, creationState )
|
||||
keyResult.createResultAssembler( parent, creationState )
|
||||
);
|
||||
case BATCH_LOAD:
|
||||
if ( parentAccess.isEmbeddableInitializer() ) {
|
||||
if ( parent.isEmbeddableInitializer() ) {
|
||||
return new BatchEntityInsideEmbeddableSelectFetchInitializer(
|
||||
parentAccess,
|
||||
parent,
|
||||
fetchedAttribute,
|
||||
navigablePath,
|
||||
entityPersister,
|
||||
keyResult.createResultAssembler( parentAccess, creationState )
|
||||
keyResult.createResultAssembler( parent, creationState )
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new BatchEntitySelectFetchInitializer(
|
||||
parentAccess,
|
||||
parent,
|
||||
fetchedAttribute,
|
||||
navigablePath,
|
||||
entityPersister,
|
||||
keyResult.createResultAssembler( parentAccess, creationState )
|
||||
keyResult.createResultAssembler( parent, creationState )
|
||||
);
|
||||
}
|
||||
case BATCH_INITIALIZE:
|
||||
return new BatchInitializeEntitySelectFetchInitializer(
|
||||
parentAccess,
|
||||
parent,
|
||||
fetchedAttribute,
|
||||
navigablePath,
|
||||
entityPersister,
|
||||
keyResult.createResultAssembler( parentAccess, creationState )
|
||||
keyResult.createResultAssembler( parent, creationState )
|
||||
);
|
||||
}
|
||||
throw new IllegalStateException( "Should be unreachable" );
|
||||
|
@ -96,7 +115,7 @@ public class EntitySelectFetchInitializerBuilder {
|
|||
|
||||
private static BatchMode determineBatchMode(
|
||||
EntityPersister entityPersister,
|
||||
FetchParentAccess parentAccess,
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
if ( creationState.isScrollResult()
|
||||
|| !creationState.getExecutionContext()
|
||||
|
@ -111,8 +130,8 @@ public class EntitySelectFetchInitializerBuilder {
|
|||
}
|
||||
return NONE;
|
||||
}
|
||||
while ( parentAccess.isEmbeddableInitializer() ) {
|
||||
final EmbeddableInitializer embeddableInitializer = parentAccess.asEmbeddableInitializer();
|
||||
while ( parent.isEmbeddableInitializer() ) {
|
||||
final EmbeddableInitializer embeddableInitializer = parent.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,
|
||||
|
@ -126,14 +145,14 @@ public class EntitySelectFetchInitializerBuilder {
|
|||
instanceof StandardEmbeddableInstantiator ) ) {
|
||||
return entityPersister.hasSubclasses() ? NONE : BATCH_INITIALIZE;
|
||||
}
|
||||
parentAccess = parentAccess.getFetchParentAccess();
|
||||
if ( parentAccess == null ) {
|
||||
parent = parent.getParent();
|
||||
if ( parent == null ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( parentAccess != null ) {
|
||||
assert parentAccess.getInitializedPart() instanceof EntityValuedModelPart;
|
||||
final EntityPersister parentPersister = parentAccess.asEntityInitializer().getEntityDescriptor();
|
||||
if ( parent != null ) {
|
||||
assert parent.getInitializedPart() instanceof EntityValuedModelPart;
|
||||
final EntityPersister parentPersister = parent.asEntityInitializer().getEntityDescriptor();
|
||||
final EntityDataAccess cacheAccess = parentPersister.getCacheAccessStrategy();
|
||||
if ( cacheAccess != null ) {
|
||||
// Do batch initialization instead of batch loading if the parent entity is cacheable
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.BitSet;
|
|||
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.InitializerParent;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
/**
|
||||
|
@ -43,8 +44,13 @@ public class ArgumentDomainResult<A> implements DomainResult<A> {
|
|||
public ArgumentReader<A> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgumentReader<A> createResultAssembler(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
return new ArgumentReader<>(
|
||||
realDomainResult.createResultAssembler( parentAccess, creationState ),
|
||||
realDomainResult.createResultAssembler( parent, creationState ),
|
||||
getResultVariable()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,11 +6,16 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.instantiation.internal;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Specialized QueryResultAssembler for use as a "reader" for dynamic-
|
||||
* instantiation arguments.
|
||||
|
@ -39,4 +44,24 @@ public class ArgumentReader<A> implements DomainResultAssembler<A> {
|
|||
public JavaType<A> getAssembledJavaType() {
|
||||
return delegateAssembler.getAssembledJavaType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable A assemble(RowProcessingState rowProcessingState) {
|
||||
return delegateAssembler.assemble( rowProcessingState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveState(RowProcessingState rowProcessingState) {
|
||||
delegateAssembler.resolveState( rowProcessingState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Initializer getInitializer() {
|
||||
return delegateAssembler.getInitializer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> void forEachResultAssembler(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
delegateAssembler.forEachResultAssembler( consumer, arg );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,11 @@ package org.hibernate.sql.results.graph.instantiation.internal;
|
|||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.query.sqm.sql.internal.InstantiationException;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -58,4 +60,11 @@ public class DynamicInstantiationAssemblerConstructorImpl<R> implements DomainRe
|
|||
+ targetConstructor.getDeclaringClass().getName() + "'", e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> void forEachResultAssembler(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
for ( ArgumentReader<?> argumentReader : argumentReaders ) {
|
||||
argumentReader.forEachResultAssembler( consumer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,12 @@ import java.lang.reflect.InvocationTargetException;
|
|||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.internal.util.beans.BeanInfoHelper;
|
||||
import org.hibernate.query.sqm.sql.internal.InstantiationException;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -102,4 +104,11 @@ public class DynamicInstantiationAssemblerInjectionImpl<T> implements DomainResu
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> void forEachResultAssembler(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
for ( BeanInjection beanInjection : beanInjections ) {
|
||||
beanInjection.getValueAssembler().forEachResultAssembler( consumer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,10 @@ package org.hibernate.sql.results.graph.instantiation.internal;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -48,4 +50,11 @@ public class DynamicInstantiationAssemblerListImpl implements DomainResultAssemb
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> void forEachResultAssembler(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
for ( ArgumentReader<?> argumentReader : argumentReaders ) {
|
||||
argumentReader.forEachResultAssembler( consumer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -67,4 +69,11 @@ public class DynamicInstantiationAssemblerMapImpl implements DomainResultAssembl
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> void forEachResultAssembler(BiConsumer<Initializer, X> consumer, X arg) {
|
||||
for ( ArgumentReader<?> argumentReader : argumentReaders ) {
|
||||
argumentReader.forEachResultAssembler( consumer, arg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.hibernate.query.sqm.DynamicInstantiationNature;
|
|||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.instantiation.DynamicInstantiationResult;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
|
@ -85,6 +86,11 @@ public class DynamicInstantiationResultImpl<R> implements DynamicInstantiationRe
|
|||
public DomainResultAssembler<R> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<R> createResultAssembler(InitializerParent parent, AssemblerCreationState creationState) {
|
||||
boolean areAllArgumentsAliased = true;
|
||||
boolean areAnyArgumentsAliased = false;
|
||||
final Set<String> aliases = new HashSet<>();
|
||||
|
@ -108,7 +114,7 @@ public class DynamicInstantiationResultImpl<R> implements DynamicInstantiationRe
|
|||
areAnyArgumentsAliased = true;
|
||||
}
|
||||
|
||||
argumentReaders.add( argumentResult.createResultAssembler( parentAccess, creationState ) );
|
||||
argumentReaders.add( argumentResult.createResultAssembler( parent, creationState ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.internal;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
public abstract class AbstractInitializer implements Initializer {
|
||||
|
||||
protected RowProcessingState rowProcessingState;
|
||||
protected State state = State.UNINITIALIZED;
|
||||
|
||||
@Override
|
||||
public void startLoading(RowProcessingState rowProcessingState) {
|
||||
this.rowProcessingState = rowProcessingState;
|
||||
forEachSubInitializer( Initializer::startLoading, rowProcessingState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endLoading(ExecutionContext executionContext) {
|
||||
rowProcessingState = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey() {
|
||||
state = State.KEY_RESOLVED;
|
||||
forEachSubInitializer( (initializer, processingState) -> initializer.resolveKey(), rowProcessingState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance() {
|
||||
// No-op by default
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow() {
|
||||
state = State.UNINITIALIZED;
|
||||
}
|
||||
|
||||
protected abstract <X> void forEachSubInitializer(BiConsumer<Initializer, X> consumer, X arg);
|
||||
|
||||
}
|
|
@ -14,6 +14,7 @@ import org.hibernate.sql.results.graph.AssemblerCreationState;
|
|||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.InitializerParent;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResultGraphNode;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
|
@ -75,6 +76,13 @@ public class TupleResult<T> implements DomainResult<T>, BasicResultGraphNode<T>
|
|||
public DomainResultAssembler<T> createResultAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
AssemblerCreationState creationState) {
|
||||
return createResultAssembler( (InitializerParent) parentAccess, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<T> createResultAssembler(
|
||||
InitializerParent parent,
|
||||
AssemblerCreationState creationState) {
|
||||
return assembler;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue