HHH-17030 Rework the initializer interactions and polish the interface design

This commit is contained in:
Christian Beikov 2024-05-16 17:38:53 +02:00
parent 3263ac4fd0
commit d02d8d7af6
116 changed files with 4310 additions and 3110 deletions

View File

@ -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(),

View File

@ -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,

View File

@ -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();
}
}

View File

@ -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() );
}
}

View File

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

View File

@ -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() {

View File

@ -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();

View File

@ -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;

View File

@ -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 );
}
}

View File

@ -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;
}

View File

@ -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 )
);
}

View File

@ -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
// );
}
}

View File

@ -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 );
}
};
}

View File

@ -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 ) );

View File

@ -206,7 +206,7 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
jdbcValues
);
rowReader.getInitializersList().startLoading( rowProcessingState );
rowReader.startLoading( rowProcessingState );
final T result = resultsConsumer.consume(
jdbcValues,

View File

@ -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;
}
}

View File

@ -35,7 +35,7 @@ public interface AssemblerCreationState {
<P extends FetchParent> Initializer resolveInitializer(
P resultGraphNode,
FetchParentAccess parentAccess,
InitializerParent parent,
InitializerProducer<P> producer);
SqlAstCreationContext getSqlAstCreationContext();

View File

@ -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 );
}
}

View File

@ -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) {
}
}

View File

@ -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 );
}
}

View File

@ -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 ) {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 {
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -42,6 +42,7 @@ public interface CollectionInitializer extends FetchParentAccess {
return true;
}
@Deprecated(forRemoval = true)
@Nullable CollectionKey resolveCollectionKey(RowProcessingState rowProcessingState);
@Override

View File

@ -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
}
}

View File

@ -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) {

View File

@ -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() ) + ")";

View File

@ -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
);
}
}

View File

@ -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() ) + ")";

View File

@ -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
);
}
}

View File

@ -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 );
}
}
}

View File

@ -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,

View File

@ -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 );
}
}

View File

@ -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
);

View File

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

View File

@ -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 );
}

View File

@ -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() ) + ")";

View File

@ -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
);
}
}

View File

@ -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() ) + ")";

View File

@ -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
);
}
}

View File

@ -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
);

View File

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

View File

@ -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() ) + ")";

View File

@ -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
);
}
}

View File

@ -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
}
}

View File

@ -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() {

View File

@ -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
);
}

View File

@ -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() };
}

View File

@ -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 );
}
}

View File

@ -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
);
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}

View File

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

View File

@ -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;
}
}

View File

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

View File

@ -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() + "`";
}
}

View File

@ -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 );
}
}

View File

@ -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() + ")";
}
}

View File

@ -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();

View File

@ -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 );
}
}

View File

@ -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();
}
}

View File

@ -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() + "`";
}
}

View File

@ -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 );
}
}

View File

@ -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() + ")";
}
}

View File

@ -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);
}

View File

@ -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() {

View File

@ -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" );
}
}

View File

@ -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 );

View File

@ -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();
}

View File

@ -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() ) + ")";

View File

@ -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 );
}

View File

@ -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(),

View File

@ -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" );
}
}

View File

@ -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(),

View File

@ -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 );
}
}
}

View File

@ -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
);
}

View File

@ -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" );
}
}

View File

@ -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 );

View File

@ -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(),

View File

@ -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() ) + ")";
}
}

View File

@ -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
);
}

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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" );
}
}

View File

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

View File

@ -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()
);
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}
}

View File

@ -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 ) );
}
}

View File

@ -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);
}

View File

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