HHH-16468 Don't create fetch for _identifierMapper anymore

This commit is contained in:
Christian Beikov 2023-04-11 13:33:26 +02:00
parent 5a3d60f508
commit 3ee817008a
14 changed files with 642 additions and 164 deletions

View File

@ -689,7 +689,7 @@ private BiConsumer<Fetchable, Boolean> createFetchableBiConsumer(
LoaderSqlAstCreationState creationState, LoaderSqlAstCreationState creationState,
ImmutableFetchList.Builder fetches) { ImmutableFetchList.Builder fetches) {
return (fetchable, isKeyFetchable) -> { return (fetchable, isKeyFetchable) -> {
if ( !fetchable.isSelectable() ) { if ( !fetchable.isSelectable() || fetchable.getPartName().equals( NavigablePath.IDENTIFIER_MAPPER_PROPERTY ) ) {
return; return;
} }

View File

@ -10,6 +10,7 @@
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import org.hibernate.cache.MutableCacheKeyBuilder; import org.hibernate.cache.MutableCacheKeyBuilder;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -35,8 +36,15 @@
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; 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.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
import org.hibernate.sql.results.graph.embeddable.internal.NonAggregatedIdentifierMappingFetch;
import org.hibernate.sql.results.graph.embeddable.internal.NonAggregatedIdentifierMappingResult;
/** /**
* A "non-aggregated" composite identifier. * A "non-aggregated" composite identifier.
@ -316,6 +324,38 @@ public void applySqlSelections(
identifierValueMapper.applySqlSelections( navigablePath, tableGroup, creationState, selectionConsumer ); identifierValueMapper.applySqlSelections( navigablePath, tableGroup, creationState, selectionConsumer );
} }
@Override
public <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
String resultVariable,
DomainResultCreationState creationState) {
return new NonAggregatedIdentifierMappingResult<>(
navigablePath,
this,
resultVariable,
creationState
);
}
@Override
public Fetch generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
String resultVariable,
DomainResultCreationState creationState) {
return new NonAggregatedIdentifierMappingFetch(
fetchablePath,
this,
fetchParent,
fetchTiming,
selected,
creationState
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// EmbeddableValuedFetchable // EmbeddableValuedFetchable

View File

@ -459,7 +459,7 @@ public ImmutableFetchList visitFetches(FetchParent fetchParent) {
private Consumer<Fetchable> createFetchableConsumer(FetchParent fetchParent, ImmutableFetchList.Builder fetches) { private Consumer<Fetchable> createFetchableConsumer(FetchParent fetchParent, ImmutableFetchList.Builder fetches) {
return fetchable -> { return fetchable -> {
if ( !fetchable.isSelectable() ) { if ( !fetchable.isSelectable() || fetchable.getPartName().equals( NavigablePath.IDENTIFIER_MAPPER_PROPERTY ) ) {
return; return;
} }
final String fetchableName = fetchable.getFetchableName(); final String fetchableName = fetchable.getFetchableName();

View File

@ -7144,7 +7144,7 @@ public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) {
} }
private Fetch createFetch(FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) { private Fetch createFetch(FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) {
if ( !fetchable.isSelectable() ) { if ( !fetchable.isSelectable() || fetchable.getPartName().equals( NavigablePath.IDENTIFIER_MAPPER_PROPERTY ) ) {
return null; return null;
} }
final NavigablePath resolvedNavigablePath = fetchParent.resolveNavigablePath( fetchable ); final NavigablePath resolvedNavigablePath = fetchParent.resolveNavigablePath( fetchable );

View File

@ -9,17 +9,12 @@
import java.util.List; import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.internal.StandardEmbeddableInstantiator;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.VirtualModelPart; import org.hibernate.metamodel.mapping.VirtualModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.spi.ValueAccess; import org.hibernate.metamodel.spi.ValueAccess;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
@ -37,41 +32,27 @@
import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.internal.NullValueAssembler;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY; import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentAccess implements EmbeddableInitializer, public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentAccess
ValueAccess { implements EmbeddableInitializer, ValueAccess {
private static final Object NULL_MARKER = new Object() {
@Override
public String toString() {
return "Composite NULL_MARKER";
}
};
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final EmbeddableValuedModelPart embedded; private final EmbeddableValuedModelPart embedded;
private final EmbeddableMappingType representationEmbeddable;
private final EmbeddableRepresentationStrategy representationStrategy;
private final FetchParentAccess fetchParentAccess; private final FetchParentAccess fetchParentAccess;
private final boolean createEmptyCompositesEnabled; private final boolean createEmptyCompositesEnabled;
private final SessionFactoryImplementor sessionFactory; private final SessionFactoryImplementor sessionFactory;
private final List<DomainResultAssembler<?>> assemblers; private final List<DomainResultAssembler<?>> assemblers;
private final boolean usesStandardInstantiation;
// per-row state // per-row state
private final Object[] rowState; private final Object[] rowState;
private Boolean stateAllNull; private State state = State.INITIAL;
private Boolean stateInjected;
protected Object compositeInstance; protected Object compositeInstance;
private boolean isParentInitialized;
private RowProcessingState wrappedProcessingState; private RowProcessingState wrappedProcessingState;
public AbstractEmbeddableInitializer( public AbstractEmbeddableInitializer(
@ -83,16 +64,6 @@ public AbstractEmbeddableInitializer(
this.fetchParentAccess = fetchParentAccess; this.fetchParentAccess = fetchParentAccess;
final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor(); final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
if ( embedded instanceof CompositeIdentifierMapping ) {
representationEmbeddable = ( (CompositeIdentifierMapping) embedded ).getMappedIdEmbeddableTypeDescriptor();
}
else {
representationEmbeddable = embeddableTypeDescriptor;
}
this.representationStrategy = representationEmbeddable.getRepresentationStrategy();
this.usesStandardInstantiation = representationStrategy.getInstantiator() instanceof StandardEmbeddableInstantiator;
final int size = embeddableTypeDescriptor.getNumberOfFetchables(); final int size = embeddableTypeDescriptor.getNumberOfFetchables();
this.rowState = new Object[ size ]; this.rowState = new Object[ size ];
this.assemblers = arrayList( size ); this.assemblers = arrayList( size );
@ -140,7 +111,7 @@ public NavigablePath getNavigablePath() {
@Override @Override
public Object getCompositeInstance() { public Object getCompositeInstance() {
return compositeInstance == NULL_MARKER ? null : compositeInstance; return state == State.NULL ? null : compositeInstance;
} }
@Override @Override
@ -174,16 +145,6 @@ public void resolveInstance(RowProcessingState processingState) {
public void initializeInstance(RowProcessingState processingState) { public void initializeInstance(RowProcessingState processingState) {
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Initializing composite instance [%s]", navigablePath ); EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Initializing composite instance [%s]", navigablePath );
if ( compositeInstance == NULL_MARKER ) {
// we already know it is null
return;
}
if ( isParentInstanceNull() ) {
compositeInstance = NULL_MARKER;
return;
}
// IMPORTANT: This method might be called multiple times for the same role for a single row. // 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 // EmbeddableAssembler calls it as part of its `#assemble` and the RowReader calls it
// as part of its normal Initializer handling // as part of its normal Initializer handling
@ -203,74 +164,66 @@ public void initializeInstance(RowProcessingState processingState) {
// critical in the case we have custom constructor injection. Luckily, custom instantiation // 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 // is only allowed for non-key usage atm, so we leverage that distinction here
if ( !usesStandardInstantiation ) { switch ( state ) {
// we have a custom instantiator case NULL:
if ( compositeInstance != null ) {
return; return;
} case INITIAL:
} if ( isParentInstanceNull() ) {
if ( isParentInitialized ) { state = State.NULL;
return; return;
} }
// We need to possibly wrap the processing state if the embeddable is within an aggregate // We need to possibly wrap the processing state if the embeddable is within an aggregate
if ( wrappedProcessingState == null ) { if ( wrappedProcessingState == null ) {
wrappedProcessingState = wrapProcessingState( processingState ); wrappedProcessingState = wrapProcessingState( processingState );
} }
initializeInstance( ); extractRowState( wrappedProcessingState );
} prepareCompositeInstance( wrappedProcessingState );
if ( state == State.NULL ) {
return;
}
notifyResolutionListeners( compositeInstance );
case EXTRACTED:
if ( embedded.getParentInjectionAttributePropertyAccess() != null || embedded instanceof VirtualModelPart ) {
handleParentInjection( wrappedProcessingState );
private void initializeInstance() { final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( compositeInstance );
stateInjected = false; // If the composite instance has a lazy initializer attached, this means that the embeddable is actually virtual
extractRowState( wrappedProcessingState ); // and the compositeInstance == entity, so we have to inject the row state into the entity when it finishes resolution
prepareCompositeInstance( wrappedProcessingState ); if ( lazyInitializer != null ) {
if ( isParentInitialized ) { final Initializer parentInitializer = wrappedProcessingState.resolveInitializer( navigablePath.getParent() );
return; if ( parentInitializer != this ) {
} ( (FetchParentAccess) parentInitializer ).registerResolutionListener( (entity) -> {
if ( !stateInjected ) { embedded.getEmbeddableTypeDescriptor().setValues( entity, rowState );
handleParentInjection( wrappedProcessingState ); state = State.INJECTED;
} } );
}
if ( compositeInstance != NULL_MARKER ) { else {
notifyResolutionListeners( compositeInstance ); // At this point, createEmptyCompositesEnabled is always true, so we generate
// the composite instance.
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( compositeInstance ); //
// If the composite instance has a lazy initializer attached, this means that the embeddable is actually virtual // NOTE: `valuesAccess` is set to null to indicate that all values are null,
// and the compositeInstance == entity, so we have to inject the row state into the entity when it finishes resolution // as opposed to returning the all-null value array. the instantiator
if ( lazyInitializer != null ) { // interprets that as the values are not known or were all null.
final Initializer parentInitializer = wrappedProcessingState.resolveInitializer( navigablePath.getParent() ); final Object target = embedded.getEmbeddableTypeDescriptor().getRepresentationStrategy()
if ( parentInitializer != this ) { .getInstantiator()
( (FetchParentAccess) parentInitializer ).registerResolutionListener( (entity) -> { .instantiate( this, sessionFactory);
representationEmbeddable.setValues( entity, rowState ); state = State.INJECTED;
stateInjected = true; lazyInitializer.setImplementation( target );
} ); }
}
else {
embedded.getEmbeddableTypeDescriptor().setValues( compositeInstance, rowState );
state = State.INJECTED;
}
} }
else { else {
// At this point, createEmptyCompositesEnabled is always true, so we generate state = State.INJECTED;
// 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);
stateInjected = true;
lazyInitializer.setImplementation( target );
} }
}
else if ( stateAllNull == FALSE && stateInjected != TRUE ) {
representationEmbeddable.setValues( compositeInstance, rowState );
stateInjected = true;
}
} }
} }
private void prepareCompositeInstance(RowProcessingState processingState) { private void prepareCompositeInstance(RowProcessingState processingState) {
if ( compositeInstance != null ) {
return;
}
// Virtual model parts use the owning entity as container which the fetch parent access provides. // 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, // 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. // so we can't use the fetch parent access in that case.
@ -282,7 +235,6 @@ private void prepareCompositeInstance(RowProcessingState processingState) {
compositeInstance = fetchParentAccess.getInitializedInstance(); compositeInstance = fetchParentAccess.getInitializedInstance();
EntityInitializer entityInitializer = fetchParentAccess.asEntityInitializer(); EntityInitializer entityInitializer = fetchParentAccess.asEntityInitializer();
if ( entityInitializer != null && entityInitializer.isEntityInitialized() ) { if ( entityInitializer != null && entityInitializer.isEntityInitialized() ) {
this.isParentInitialized = true;
return; return;
} }
} }
@ -290,7 +242,6 @@ private void prepareCompositeInstance(RowProcessingState processingState) {
if ( compositeInstance == null ) { if ( compositeInstance == null ) {
compositeInstance = createCompositeInstance( compositeInstance = createCompositeInstance(
navigablePath, navigablePath,
representationStrategy,
sessionFactory sessionFactory
); );
} }
@ -326,7 +277,7 @@ private boolean isParentInstanceNull() {
} }
private void extractRowState(RowProcessingState processingState) { private void extractRowState(RowProcessingState processingState) {
stateAllNull = true; boolean stateAllNull = true;
final boolean isKey = ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() ) final boolean isKey = ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() )
|| ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getLocalName() ) || ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getLocalName() )
|| EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() ); || EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() );
@ -353,51 +304,23 @@ else if ( isKey ) {
} }
} }
applyMapsId( processingState ); state = stateAllNull ? State.NULL : State.EXTRACTED;
} }
private void applyMapsId(RowProcessingState processingState) { private Object createCompositeInstance(NavigablePath navigablePath, SessionFactoryImplementor sessionFactory) {
final SharedSessionContractImplementor session = processingState.getSession(); if ( state == State.NULL ) {
if ( embedded instanceof CompositeIdentifierMapping ) { // todo (6.0) : should we initialize the composite instance if it has a parent attribute?
final CompositeIdentifierMapping cid = (CompositeIdentifierMapping) embedded; // if ( !createEmptyCompositesEnabled && embedded.getParentInjectionAttributePropertyAccess() == null ) {
final EmbeddableMappingType mappedIdEmbeddable = cid.getMappedIdEmbeddableTypeDescriptor(); if ( !createEmptyCompositesEnabled ) {
if ( cid.hasContainingClass() ) { return null;
final EmbeddableMappingType virtualIdEmbeddable = embedded.getEmbeddableTypeDescriptor();
if ( virtualIdEmbeddable == mappedIdEmbeddable ) {
return;
}
virtualIdEmbeddable.forEachAttributeMapping(
(position, virtualIdAttribute) -> {
final AttributeMapping mappedIdAttribute = mappedIdEmbeddable.getAttributeMapping( position );
if ( virtualIdAttribute instanceof ToOneAttributeMapping
&& !( mappedIdAttribute instanceof ToOneAttributeMapping ) ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) virtualIdAttribute;
final ForeignKeyDescriptor fkDescriptor = toOneAttributeMapping.getForeignKeyDescriptor();
final Object associationKey = fkDescriptor.getAssociationKeyFromSide(
rowState[position],
toOneAttributeMapping.getSideNature().inverse(),
session
);
rowState[position] = associationKey;
}
}
);
} }
} }
}
private Object createCompositeInstance( final Object instance = embedded.getEmbeddableTypeDescriptor()
NavigablePath navigablePath, .getRepresentationStrategy()
EmbeddableRepresentationStrategy representationStrategy, .getInstantiator()
SessionFactoryImplementor sessionFactory) { .instantiate( this, sessionFactory );
if ( !createEmptyCompositesEnabled && stateAllNull == TRUE ) { state = State.EXTRACTED;
return NULL_MARKER;
}
final Object instance = representationStrategy.getInstantiator().instantiate( this, sessionFactory );
stateInjected = true;
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Created composite instance [%s] : %s", navigablePath, instance ); EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Created composite instance [%s] : %s", navigablePath, instance );
@ -406,12 +329,12 @@ private Object createCompositeInstance(
@Override @Override
public Object[] getValues() { public Object[] getValues() {
return stateAllNull ? null : rowState; return state == State.NULL ? null : rowState;
} }
@Override @Override
public <T> T getValue(int i, Class<T> clazz) { public <T> T getValue(int i, Class<T> clazz) {
return stateAllNull ? null : clazz.cast( rowState[i] ); return state == State.NULL ? null : clazz.cast( rowState[i] );
} }
@Override @Override
@ -420,17 +343,6 @@ public Object getOwner() {
} }
private void handleParentInjection(RowProcessingState processingState) { private void handleParentInjection(RowProcessingState processingState) {
// todo (6.0) : should we initialize the composite instance if we get here and it is null (not NULL_MARKER)?
// we want to avoid injection for `NULL_MARKER`
if ( compositeInstance == null || compositeInstance == NULL_MARKER ) {
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf(
"Skipping parent injection for null embeddable [%s]",
navigablePath
);
return;
}
final PropertyAccess parentInjectionAccess = embedded.getParentInjectionAttributePropertyAccess(); final PropertyAccess parentInjectionAccess = embedded.getParentInjectionAttributePropertyAccess();
if ( parentInjectionAccess == null ) { if ( parentInjectionAccess == null ) {
// embeddable defined no parent injection // embeddable defined no parent injection
@ -504,9 +416,7 @@ private Object determineParentInstance(RowProcessingState processingState) {
@Override @Override
public void finishUpRow(RowProcessingState rowProcessingState) { public void finishUpRow(RowProcessingState rowProcessingState) {
compositeInstance = null; compositeInstance = null;
stateAllNull = null; state = State.INITIAL;
stateInjected = null;
isParentInitialized = false;
wrappedProcessingState = null; wrappedProcessingState = null;
clearResolutionListeners(); clearResolutionListeners();
@ -516,4 +426,10 @@ public void finishUpRow(RowProcessingState rowProcessingState) {
public String toString() { public String toString() {
return getClass().getSimpleName() + "(" + navigablePath + ") : `" + getInitializedPart().getJavaType().getJavaTypeClass() + "`"; return getClass().getSimpleName() + "(" + navigablePath + ") : `" + getInitializedPart().getJavaType().getJavaTypeClass() + "`";
} }
enum State {
INITIAL,
EXTRACTED,
NULL,
INJECTED
}
} }

View File

@ -0,0 +1,289 @@
/*
* 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.List;
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.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.spi.ValueAccess;
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.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.internal.NullValueAssembler;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY;
/**
* @author Steve Ebersole
*/
public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends AbstractFetchParentAccess
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 SessionFactoryImplementor sessionFactory;
private final List<DomainResultAssembler<?>> assemblers;
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(
EmbeddableResultGraphNode resultDescriptor,
FetchParentAccess fetchParentAccess,
AssemblerCreationState creationState) {
this.navigablePath = resultDescriptor.getNavigablePath();
this.embedded = (NonAggregatedIdentifierMapping) resultDescriptor.getReferencedMappingContainer();
this.fetchParentAccess = fetchParentAccess;
final EmbeddableMappingType virtualIdEmbeddable = embedded.getEmbeddableTypeDescriptor();
this.representationEmbeddable = embedded.getMappedIdEmbeddableTypeDescriptor();
this.representationStrategy = representationEmbeddable.getRepresentationStrategy();
this.hasIdClass = embedded.hasContainingClass() && virtualIdEmbeddable != representationEmbeddable;
final int size = virtualIdEmbeddable.getNumberOfFetchables();
this.virtualIdState = new Object[ size ];
this.idClassState = new Object[ size ];
this.assemblers = arrayList( size );
this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
initializeAssemblers( resultDescriptor, creationState, virtualIdEmbeddable );
}
protected void initializeAssemblers(
EmbeddableResultGraphNode resultDescriptor,
AssemblerCreationState creationState,
EmbeddableMappingType embeddableTypeDescriptor) {
final int size = embeddableTypeDescriptor.getNumberOfFetchables();
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.add( stateAssembler );
}
}
@Override
public EmbeddableValuedModelPart getInitializedPart() {
return embedded;
}
@Override
public FetchParentAccess getFetchParentAccess() {
return fetchParentAccess;
}
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override
public Object getCompositeInstance() {
return compositeInstance;
}
@Override
public FetchParentAccess findFirstEntityDescriptorAccess() {
if ( fetchParentAccess == null ) {
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) {
// 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 ( !hasIdClass && processingState.getEntityId() != null
&& navigablePath.getParent().getParent() == null
&& navigablePath instanceof EntityIdentifierNavigablePath ) {
compositeInstance = processingState.getEntityId();
state = State.INJECTED;
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;
}
else {
compositeInstance = representationStrategy.getInstantiator().instantiate( this, sessionFactory );
}
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;
}
}
}
}
private void extractRowState(RowProcessingState processingState) {
state = State.NULL;
for ( int i = 0; i < assemblers.size(); i++ ) {
final DomainResultAssembler<?> assembler = assemblers.get( i );
final Object contributorValue = assembler.assemble(
processingState,
processingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
);
if ( contributorValue == null ) {
// This is a key and there is a null part, the whole thing has to be turned into null
return;
}
if ( contributorValue == BATCH_PROPERTY ) {
virtualIdState[i] = null;
idClassState[i] = null;
}
else {
virtualIdState[i] = contributorValue;
idClassState[i] = contributorValue;
if ( hasIdClass ) {
final AttributeMapping virtualIdAttribute = embedded.getEmbeddableTypeDescriptor().getAttributeMapping( i );
final AttributeMapping mappedIdAttribute = representationEmbeddable.getAttributeMapping( i );
if ( virtualIdAttribute instanceof ToOneAttributeMapping
&& !( mappedIdAttribute instanceof ToOneAttributeMapping ) ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) virtualIdAttribute;
final ForeignKeyDescriptor fkDescriptor = toOneAttributeMapping.getForeignKeyDescriptor();
final Object associationKey = fkDescriptor.getAssociationKeyFromSide(
virtualIdState[i],
toOneAttributeMapping.getSideNature().inverse(),
processingState.getSession()
);
idClassState[i] = associationKey;
}
}
}
}
state = State.EXTRACTED;
}
@Override
public Object[] getValues() {
return state == State.NULL ? null : idClassState;
}
@Override
public <T> T getValue(int i, Class<T> clazz) {
return state == State.NULL ? null : clazz.cast( idClassState[i] );
}
@Override
public Object getOwner() {
return fetchParentAccess.getInitializedInstance();
}
@Override
public void finishUpRow(RowProcessingState rowProcessingState) {
compositeInstance = null;
state = State.INITIAL;
clearResolutionListeners();
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + navigablePath + ") : `" + getInitializedPart().getJavaType().getJavaTypeClass() + "`";
}
enum State {
INITIAL,
EXTRACTED,
NULL,
INJECTED
}
}

View File

@ -10,6 +10,7 @@
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.graph.AbstractFetchParent; import org.hibernate.sql.results.graph.AbstractFetchParent;
@ -98,7 +99,9 @@ public DomainResultAssembler<T> createResultAssembler(
final EmbeddableInitializer initializer = creationState.resolveInitializer( final EmbeddableInitializer initializer = creationState.resolveInitializer(
getNavigablePath(), getNavigablePath(),
getReferencedModePart(), getReferencedModePart(),
() -> new EmbeddableResultInitializer( this, parentAccess, creationState ) () -> getReferencedModePart() instanceof NonAggregatedIdentifierMapping
? new NonAggregatedIdentifierMappingResultInitializer( this, null, creationState )
: new EmbeddableResultInitializer( this, null, creationState )
).asEmbeddableInitializer(); ).asEmbeddableInitializer();
assert initializer != null; assert initializer != null;

View File

@ -6,13 +6,17 @@
*/ */
package org.hibernate.sql.results.graph.embeddable.internal; package org.hibernate.sql.results.graph.embeddable.internal;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.exec.internal.BaseExecutionContext; import org.hibernate.sql.exec.internal.BaseExecutionContext;
import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.Callback;
import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.EntityFetch; import org.hibernate.sql.results.graph.entity.EntityFetch;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.sql.results.spi.RowReader; import org.hibernate.sql.results.spi.RowReader;
@ -89,8 +93,43 @@ public QueryParameterBindings getQueryParameterBindings() {
return processingState.getQueryParameterBindings(); return processingState.getQueryParameterBindings();
} }
@Override
public boolean isScrollResult(){
return processingState.isScrollResult();
}
@Override @Override
public Callback getCallback() { public Callback getCallback() {
return processingState.getCallback(); return processingState.getCallback();
} }
@Override
public CollectionKey getCollectionKey() {
return processingState.getCollectionKey();
}
@Override
public Object getEntityInstance() {
return processingState.getEntityInstance();
}
@Override
public Object getEntityId() {
return processingState.getEntityId();
}
@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
processingState.registerLoadingEntityEntry( entityKey, entry );
}
@Override
public void afterStatement(LogicalConnectionImplementor logicalConnection) {
processingState.afterStatement( logicalConnection );
}
@Override
public boolean hasQueryExecutionToBeAddedToStatistics() {
return processingState.hasQueryExecutionToBeAddedToStatistics();
}
} }

View File

@ -0,0 +1,41 @@
/*
* 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.engine.FetchTiming;
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.FetchParent;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
public class NonAggregatedIdentifierMappingFetch extends EmbeddableFetchImpl {
public NonAggregatedIdentifierMappingFetch(
NavigablePath navigablePath,
NonAggregatedIdentifierMapping embeddedPartDescriptor,
FetchParent fetchParent,
FetchTiming fetchTiming,
boolean hasTableGroup,
DomainResultCreationState creationState) {
super( navigablePath, embeddedPartDescriptor, fetchParent, fetchTiming, hasTableGroup, creationState );
}
public NonAggregatedIdentifierMappingFetch(EmbeddableFetchImpl original) {
super( original );
}
@Override
protected Initializer buildEmbeddableFetchInitializer(
FetchParentAccess parentAccess,
EmbeddableResultGraphNode embeddableFetch,
AssemblerCreationState creationState) {
return new NonAggregatedIdentifierMappingFetchInitializer( parentAccess, this, creationState );
}
}

View File

@ -0,0 +1,31 @@
/*
* 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 Object getParentKey() {
return findFirstEntityDescriptorAccess().getParentKey();
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.NonAggregatedIdentifierMapping;
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.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
public class NonAggregatedIdentifierMappingResult<T> extends EmbeddableResultImpl<T> {
public NonAggregatedIdentifierMappingResult(
NavigablePath navigablePath,
NonAggregatedIdentifierMapping modelPart,
String resultVariable,
DomainResultCreationState creationState) {
super( navigablePath, modelPart, resultVariable, creationState );
}
@Override
public DomainResultAssembler<T> createResultAssembler(
FetchParentAccess parentAccess,
AssemblerCreationState creationState) {
final EmbeddableInitializer initializer = creationState.resolveInitializer(
getNavigablePath().append( "{embeddable_result}" ),
getReferencedModePart(),
() -> new NonAggregatedIdentifierMappingResultInitializer(
this,
parentAccess,
creationState
)
).asEmbeddableInitializer();
assert initializer != null;
//noinspection unchecked
return new EmbeddableAssembler( initializer );
}
}

View File

@ -0,0 +1,33 @@
/*
* 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 String toString() {
return "EmbeddableResultInitializer(" + getNavigablePath() + ")";
}
}

View File

@ -52,6 +52,7 @@
import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.basic.BasicResultAssembler; import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableAssembler;
import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.internal.NullValueAssembler;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
@ -524,6 +525,13 @@ else if ( entityInstanceFromExecutionContext != null ) {
// look to see if another initializer from a parent load context or an earlier // look to see if another initializer from a parent load context or an earlier
// initializer is already loading the entity // initializer is already loading the entity
entityInstance = resolveInstance( entityIdentifier, existingLoadingEntry, rowProcessingState ); entityInstance = resolveInstance( entityIdentifier, existingLoadingEntry, rowProcessingState );
if ( isOwningInitializer && !isInitialized && identifierAssembler instanceof EmbeddableAssembler ) {
// If this is the owning initializer and the returned object is not initialized,
// this means that the entity instance was just instantiated.
// In this case, we want to call "assemble" and hence "initializeInstance" on the initializer
// for possibly non-aggregated identifier mappings, so inject the virtual id representation
identifierAssembler.assemble( rowProcessingState );
}
} }
upgradeLockMode( rowProcessingState ); upgradeLockMode( rowProcessingState );

View File

@ -7,6 +7,8 @@
package org.hibernate.sql.results.internal; package org.hibernate.sql.results.internal;
import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterBindings;
@ -15,6 +17,7 @@
import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.EntityFetch; import org.hibernate.sql.results.graph.entity.EntityFetch;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.internal.JdbcValuesCacheHit; import org.hibernate.sql.results.jdbc.internal.JdbcValuesCacheHit;
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl; import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
import org.hibernate.sql.results.jdbc.spi.JdbcValues; import org.hibernate.sql.results.jdbc.spi.JdbcValues;
@ -138,6 +141,11 @@ public QueryParameterBindings getQueryParameterBindings() {
return getJdbcValuesSourceProcessingState().getExecutionContext().getQueryParameterBindings(); return getJdbcValuesSourceProcessingState().getExecutionContext().getQueryParameterBindings();
} }
@Override
public boolean isScrollResult(){
return executionContext.isScrollResult();
}
@Override @Override
public Callback getCallback() { public Callback getCallback() {
return executionContext.getCallback(); return executionContext.getCallback();
@ -148,6 +156,31 @@ public CollectionKey getCollectionKey() {
return executionContext.getCollectionKey(); return executionContext.getCollectionKey();
} }
@Override
public Object getEntityInstance() {
return executionContext.getEntityInstance();
}
@Override
public Object getEntityId() {
return executionContext.getEntityId();
}
@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
executionContext.registerLoadingEntityEntry( entityKey, entry );
}
@Override
public void afterStatement(LogicalConnectionImplementor logicalConnection) {
executionContext.afterStatement( logicalConnection );
}
@Override
public boolean hasQueryExecutionToBeAddedToStatistics() {
return executionContext.hasQueryExecutionToBeAddedToStatistics();
}
@Override @Override
public Initializer resolveInitializer(NavigablePath path) { public Initializer resolveInitializer(NavigablePath path) {
return this.initializers.resolveInitializer( path ); return this.initializers.resolveInitializer( path );