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,
ImmutableFetchList.Builder fetches) {
return (fetchable, isKeyFetchable) -> {
if ( !fetchable.isSelectable() ) {
if ( !fetchable.isSelectable() || fetchable.getPartName().equals( NavigablePath.IDENTIFIER_MAPPER_PROPERTY ) ) {
return;
}

View File

@ -10,6 +10,7 @@
import java.util.function.BiConsumer;
import org.hibernate.cache.MutableCacheKeyBuilder;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -35,8 +36,15 @@
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.TableGroup;
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.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
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.
@ -316,6 +324,38 @@ public void applySqlSelections(
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

View File

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

View File

@ -9,17 +9,12 @@
import java.util.List;
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.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
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.property.access.spi.PropertyAccess;
import org.hibernate.proxy.HibernateProxy;
@ -37,41 +32,27 @@
import org.hibernate.sql.results.internal.NullValueAssembler;
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.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY;
/**
* @author Steve Ebersole
*/
public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentAccess implements EmbeddableInitializer,
ValueAccess {
private static final Object NULL_MARKER = new Object() {
@Override
public String toString() {
return "Composite NULL_MARKER";
}
};
public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentAccess
implements EmbeddableInitializer, ValueAccess {
private final NavigablePath navigablePath;
private final EmbeddableValuedModelPart embedded;
private final EmbeddableMappingType representationEmbeddable;
private final EmbeddableRepresentationStrategy representationStrategy;
private final FetchParentAccess fetchParentAccess;
private final boolean createEmptyCompositesEnabled;
private final SessionFactoryImplementor sessionFactory;
private final List<DomainResultAssembler<?>> assemblers;
private final boolean usesStandardInstantiation;
// per-row state
private final Object[] rowState;
private Boolean stateAllNull;
private Boolean stateInjected;
private State state = State.INITIAL;
protected Object compositeInstance;
private boolean isParentInitialized;
private RowProcessingState wrappedProcessingState;
public AbstractEmbeddableInitializer(
@ -83,16 +64,6 @@ public AbstractEmbeddableInitializer(
this.fetchParentAccess = fetchParentAccess;
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();
this.rowState = new Object[ size ];
this.assemblers = arrayList( size );
@ -140,7 +111,7 @@ public NavigablePath getNavigablePath() {
@Override
public Object getCompositeInstance() {
return compositeInstance == NULL_MARKER ? null : compositeInstance;
return state == State.NULL ? null : compositeInstance;
}
@Override
@ -174,16 +145,6 @@ public void resolveInstance(RowProcessingState processingState) {
public void initializeInstance(RowProcessingState processingState) {
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.
// EmbeddableAssembler calls it as part of its `#assemble` and the RowReader calls it
// as part of its normal Initializer handling
@ -203,13 +164,12 @@ public void initializeInstance(RowProcessingState processingState) {
// 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
if ( !usesStandardInstantiation ) {
// we have a custom instantiator
if ( compositeInstance != null ) {
switch ( state ) {
case NULL:
return;
}
}
if ( isParentInitialized ) {
case INITIAL:
if ( isParentInstanceNull() ) {
state = State.NULL;
return;
}
@ -217,22 +177,15 @@ public void initializeInstance(RowProcessingState processingState) {
if ( wrappedProcessingState == null ) {
wrappedProcessingState = wrapProcessingState( processingState );
}
initializeInstance( );
}
private void initializeInstance() {
stateInjected = false;
extractRowState( wrappedProcessingState );
prepareCompositeInstance( wrappedProcessingState );
if ( isParentInitialized ) {
if ( state == State.NULL ) {
return;
}
if ( !stateInjected ) {
handleParentInjection( wrappedProcessingState );
}
if ( compositeInstance != NULL_MARKER ) {
notifyResolutionListeners( compositeInstance );
case EXTRACTED:
if ( embedded.getParentInjectionAttributePropertyAccess() != null || embedded instanceof VirtualModelPart ) {
handleParentInjection( wrappedProcessingState );
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( compositeInstance );
// If the composite instance has a lazy initializer attached, this means that the embeddable is actually virtual
@ -241,8 +194,8 @@ private void initializeInstance() {
final Initializer parentInitializer = wrappedProcessingState.resolveInitializer( navigablePath.getParent() );
if ( parentInitializer != this ) {
( (FetchParentAccess) parentInitializer ).registerResolutionListener( (entity) -> {
representationEmbeddable.setValues( entity, rowState );
stateInjected = true;
embedded.getEmbeddableTypeDescriptor().setValues( entity, rowState );
state = State.INJECTED;
} );
}
else {
@ -252,25 +205,25 @@ private void initializeInstance() {
// 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
final Object target = embedded.getEmbeddableTypeDescriptor().getRepresentationStrategy()
.getInstantiator()
.instantiate( this, sessionFactory);
stateInjected = true;
state = State.INJECTED;
lazyInitializer.setImplementation( target );
}
}
else if ( stateAllNull == FALSE && stateInjected != TRUE ) {
representationEmbeddable.setValues( compositeInstance, rowState );
stateInjected = true;
else {
embedded.getEmbeddableTypeDescriptor().setValues( compositeInstance, rowState );
state = State.INJECTED;
}
}
else {
state = State.INJECTED;
}
}
}
private void prepareCompositeInstance(RowProcessingState processingState) {
if ( compositeInstance != null ) {
return;
}
// 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.
@ -282,7 +235,6 @@ private void prepareCompositeInstance(RowProcessingState processingState) {
compositeInstance = fetchParentAccess.getInitializedInstance();
EntityInitializer entityInitializer = fetchParentAccess.asEntityInitializer();
if ( entityInitializer != null && entityInitializer.isEntityInitialized() ) {
this.isParentInitialized = true;
return;
}
}
@ -290,7 +242,6 @@ private void prepareCompositeInstance(RowProcessingState processingState) {
if ( compositeInstance == null ) {
compositeInstance = createCompositeInstance(
navigablePath,
representationStrategy,
sessionFactory
);
}
@ -326,7 +277,7 @@ private boolean isParentInstanceNull() {
}
private void extractRowState(RowProcessingState processingState) {
stateAllNull = true;
boolean stateAllNull = true;
final boolean isKey = ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() )
|| ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getLocalName() )
|| 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) {
final SharedSessionContractImplementor session = processingState.getSession();
if ( embedded instanceof CompositeIdentifierMapping ) {
final CompositeIdentifierMapping cid = (CompositeIdentifierMapping) embedded;
final EmbeddableMappingType mappedIdEmbeddable = cid.getMappedIdEmbeddableTypeDescriptor();
if ( cid.hasContainingClass() ) {
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(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;
}
}
private Object createCompositeInstance(
NavigablePath navigablePath,
EmbeddableRepresentationStrategy representationStrategy,
SessionFactoryImplementor sessionFactory) {
if ( !createEmptyCompositesEnabled && stateAllNull == TRUE ) {
return NULL_MARKER;
}
final Object instance = representationStrategy.getInstantiator().instantiate( this, sessionFactory );
stateInjected = true;
final Object instance = embedded.getEmbeddableTypeDescriptor()
.getRepresentationStrategy()
.getInstantiator()
.instantiate( this, sessionFactory );
state = State.EXTRACTED;
EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Created composite instance [%s] : %s", navigablePath, instance );
@ -406,12 +329,12 @@ private Object createCompositeInstance(
@Override
public Object[] getValues() {
return stateAllNull ? null : rowState;
return state == State.NULL ? null : rowState;
}
@Override
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
@ -420,17 +343,6 @@ public Object getOwner() {
}
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();
if ( parentInjectionAccess == null ) {
// embeddable defined no parent injection
@ -504,9 +416,7 @@ private Object determineParentInstance(RowProcessingState processingState) {
@Override
public void finishUpRow(RowProcessingState rowProcessingState) {
compositeInstance = null;
stateAllNull = null;
stateInjected = null;
isParentInitialized = false;
state = State.INITIAL;
wrappedProcessingState = null;
clearResolutionListeners();
@ -516,4 +426,10 @@ public void finishUpRow(RowProcessingState rowProcessingState) {
public String toString() {
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.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.graph.AbstractFetchParent;
@ -98,7 +99,9 @@ public DomainResultAssembler<T> createResultAssembler(
final EmbeddableInitializer initializer = creationState.resolveInitializer(
getNavigablePath(),
getReferencedModePart(),
() -> new EmbeddableResultInitializer( this, parentAccess, creationState )
() -> getReferencedModePart() instanceof NonAggregatedIdentifierMapping
? new NonAggregatedIdentifierMappingResultInitializer( this, null, creationState )
: new EmbeddableResultInitializer( this, null, creationState )
).asEmbeddableInitializer();
assert initializer != null;

View File

@ -6,13 +6,17 @@
*/
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.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.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.sql.results.spi.RowReader;
@ -89,8 +93,43 @@ public QueryParameterBindings getQueryParameterBindings() {
return processingState.getQueryParameterBindings();
}
@Override
public boolean isScrollResult(){
return processingState.isScrollResult();
}
@Override
public Callback 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.Initializer;
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.jdbc.spi.JdbcValuesSourceProcessingOptions;
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
// initializer is already loading the entity
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 );

View File

@ -7,6 +7,8 @@
package org.hibernate.sql.results.internal;
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.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
@ -15,6 +17,7 @@
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.graph.Initializer;
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.JdbcValuesSourceProcessingStateStandardImpl;
import org.hibernate.sql.results.jdbc.spi.JdbcValues;
@ -138,6 +141,11 @@ public QueryParameterBindings getQueryParameterBindings() {
return getJdbcValuesSourceProcessingState().getExecutionContext().getQueryParameterBindings();
}
@Override
public boolean isScrollResult(){
return executionContext.isScrollResult();
}
@Override
public Callback getCallback() {
return executionContext.getCallback();
@ -148,6 +156,31 @@ public CollectionKey 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
public Initializer resolveInitializer(NavigablePath path) {
return this.initializers.resolveInitializer( path );