HHH-18271 Introduce lazy bitset for entities and other initializer improvements

* Cache more state in initializers
* Reduce mega-morphic call sites
* Do more efficient state resolving for query cache entries
This commit is contained in:
Christian Beikov 2024-08-02 13:08:52 +02:00 committed by Steve Ebersole
parent 55702e458b
commit 72e2da2da8
42 changed files with 1217 additions and 704 deletions

View File

@ -266,6 +266,7 @@ public class EntityUpdateAction extends EntityAction {
nextVersion = getVersion( state, persister );
}
entry.postUpdate( instance, state, nextVersion );
entry.setMaybeLazySet( null );
}
}

View File

@ -27,10 +27,13 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.internal.util.ImmutableBitSet;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.TypeHelper;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.LockMode.PESSIMISTIC_FORCE_INCREMENT;
import static org.hibernate.engine.internal.AbstractEntityEntry.BooleanState.EXISTS_IN_DATABASE;
import static org.hibernate.engine.internal.AbstractEntityEntry.BooleanState.IS_BEING_REPLICATED;
@ -71,6 +74,7 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
protected transient EntityKey cachedEntityKey; // cached EntityKey (lazy-initialized)
protected final transient Object rowId;
protected final transient PersistenceContext persistenceContext;
protected transient @Nullable ImmutableBitSet maybeLazySet;
protected EntityEntryExtraState next;
/**
@ -459,6 +463,16 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
}
}
@Override
public @Nullable ImmutableBitSet getMaybeLazySet() {
return maybeLazySet;
}
@Override
public void setMaybeLazySet(@Nullable ImmutableBitSet maybeLazySet) {
this.maybeLazySet = maybeLazySet;
}
@Override
public String toString() {
return "EntityEntry"

View File

@ -9,10 +9,14 @@ package org.hibernate.engine.spi;
import java.io.IOException;
import java.io.ObjectOutputStream;
import org.hibernate.Internal;
import org.hibernate.LockMode;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.internal.util.ImmutableBitSet;
import org.hibernate.persister.entity.EntityPersister;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Information about the current state of a managed entity instance with respect
* to its persistent state.
@ -130,6 +134,16 @@ public interface EntityEntry {
void setReadOnly(boolean readOnly, Object entity);
/**
* Has a bit set for every attribute position that is potentially lazy.
* When {@code null}, no knowledge is available and every attribute must be assumed potentially lazy.
*/
@Internal
@Nullable ImmutableBitSet getMaybeLazySet();
@Internal
void setMaybeLazySet(@Nullable ImmutableBitSet maybeLazySet);
@Override
String toString();

View File

@ -0,0 +1,77 @@
/*
* 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.internal.util;
import java.util.Arrays;
import java.util.BitSet;
/**
* An immutable variant of the {@link BitSet} class with some additional operations.
*/
public class ImmutableBitSet {
public static final ImmutableBitSet EMPTY = new ImmutableBitSet( new long[0] );
private static final int ADDRESS_BITS_PER_WORD = 6;
private final long[] words;
private static int wordIndex(int bitIndex) {
return bitIndex >> ADDRESS_BITS_PER_WORD;
}
private ImmutableBitSet(long[] words) {
this.words = words;
}
public static ImmutableBitSet valueOf(BitSet bitSet) {
final long[] words = bitSet.toLongArray();
return words.length == 0 ? EMPTY : new ImmutableBitSet( words );
}
public boolean get(int bitIndex) {
int wordIndex = wordIndex( bitIndex );
return wordIndex < words.length && ( ( words[wordIndex] & ( 1L << bitIndex ) ) != 0 );
}
public boolean isEmpty() {
return this == EMPTY;
}
public boolean contains(ImmutableBitSet set) {
if ( words.length < set.words.length ) {
return false;
}
// We check if every word from the given set is also contained in this set
for ( int i = 0; i < set.words.length; i++ ) {
if ( words[i] != ( set.words[i] | words[i] ) ) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
return Arrays.hashCode( words );
}
@Override
public boolean equals(Object obj) {
if ( !( obj instanceof ImmutableBitSet ) ) {
return false;
}
if ( this == obj ) {
return true;
}
final ImmutableBitSet set = (ImmutableBitSet) obj;
return Arrays.equals( words, set.words );
}
}

View File

@ -102,5 +102,11 @@ public class SqmMapEntryResult<K, V, R extends Map.Entry<K, V>> implements Domai
keyAssembler.forEachResultAssembler( consumer, arg );
valueAssembler.forEachResultAssembler( consumer, arg );
}
@Override
public void resolveState(RowProcessingState rowProcessingState) {
keyAssembler.resolveState( rowProcessingState );
valueAssembler.resolveState( rowProcessingState );
}
}
}

View File

@ -64,7 +64,10 @@ public interface Initializer<Data extends InitializerData> {
ModelPart getInitializedPart();
default Object getResolvedInstance(Data data) {
return data.getState() == State.RESOLVED || data.getState() == State.INITIALIZED ? data.getInstance() : null;
assert data.getState() != State.UNINITIALIZED
&& data.getState() != State.KEY_RESOLVED
&& ( data.getState() != State.MISSING || data.getInstance() == null );
return data.getInstance();
}
default Object getResolvedInstance(RowProcessingState rowProcessingState) {
return getResolvedInstance( getData( rowProcessingState ) );
@ -126,6 +129,12 @@ public interface Initializer<Data extends InitializerData> {
resolveInstance( getData( rowProcessingState ) );
}
void resolveState(Data data);
default void resolveState(RowProcessingState rowProcessingState) {
resolveState( getData( rowProcessingState ) );
}
/**
* Step 2.2 - Use the given instance as resolved instance for this initializer.
* Initializers are supposed to recursively call this method for sub-initializers.
@ -206,6 +215,16 @@ public interface Initializer<Data extends InitializerData> {
|| ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getLocalName() );
}
/**
* Indicates whether calling resolve is needed when the object for this initializer is initialized already.
*/
boolean isEager();
/**
* Indicates whether this initializers has eager sub-initializers, that could initialize lazy state of existing objects.
*/
boolean hasEagerSubInitializers();
/**
* Indicates if this is a result or fetch initializer.
*/

View File

@ -24,10 +24,10 @@ import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.InitializerData;
import org.hibernate.sql.results.graph.InitializerParent;
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
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.hibernate.type.Type;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -40,9 +40,10 @@ public abstract class AbstractCollectionInitializer<Data extends AbstractCollect
extends AbstractInitializer<Data> implements CollectionInitializer<Data> {
protected final NavigablePath collectionPath;
protected final PluralAttributeMapping collectionAttributeMapping;
protected final @Nullable Type keyTypeForEqualsHashCode;
protected final boolean isResultInitializer;
protected final @Nullable InitializerParent<?> parent;
protected final @Nullable EntityInitializer<?> owningEntityInitializer;
protected final @Nullable EntityInitializer<InitializerData> owningEntityInitializer;
/**
* refers to the collection's container value - which collection-key?
@ -77,9 +78,13 @@ public abstract class AbstractCollectionInitializer<Data extends AbstractCollect
super( creationState );
this.collectionPath = collectionPath;
this.collectionAttributeMapping = collectionAttributeMapping;
this.keyTypeForEqualsHashCode = collectionAttributeMapping.getCollectionDescriptor()
.getKeyType()
.getTypeForEqualsHashCode();
this.isResultInitializer = isResultInitializer;
this.parent = parent;
this.owningEntityInitializer = Initializer.findOwningEntityInitializer( parent );
//noinspection unchecked
this.owningEntityInitializer = (EntityInitializer<InitializerData>) Initializer.findOwningEntityInitializer( parent );
this.collectionKeyResultAssembler = collectionKeyResult == null
? null
: collectionKeyResult.createResultAssembler( this, creationState );
@ -117,6 +122,13 @@ public abstract class AbstractCollectionInitializer<Data extends AbstractCollect
}
}
@Override
public void resolveState(Data data) {
if ( collectionKeyResultAssembler != null ) {
collectionKeyResultAssembler.resolveState( data.getRowProcessingState() );
}
}
@Override
public void resolveFromPreviousRow(Data data) {
if ( data.getState() == State.UNINITIALIZED ) {
@ -124,12 +136,13 @@ public abstract class AbstractCollectionInitializer<Data extends AbstractCollect
setMissing( data );
}
else {
if ( collectionKeyResultAssembler != null ) {
final Initializer<?> initializer = collectionKeyResultAssembler.getInitializer();
if ( initializer != null ) {
initializer.resolveFromPreviousRow( data.getRowProcessingState() );
}
}
// A collection key can't contain collections, so no need to resolve the key
// if ( collectionKeyResultAssembler != null ) {
// final Initializer<?> initializer = collectionKeyResultAssembler.getInitializer();
// if ( initializer != null ) {
// initializer.resolveFromPreviousRow( data.getRowProcessingState() );
// }
// }
data.setState( State.RESOLVED );
}
}
@ -165,10 +178,7 @@ public abstract class AbstractCollectionInitializer<Data extends AbstractCollect
}
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(),
data.collectionKeyValue
) ) {
if ( checkPreviousRow && oldKey != null && areKeysEqual( oldKey.getKey(), data.collectionKeyValue ) ) {
data.collectionKey = oldKey;
data.setCollectionInstance( oldCollectionInstance );
data.setState( oldCollectionInstance == null ? State.MISSING : State.RESOLVED );
@ -179,111 +189,8 @@ public abstract class AbstractCollectionInitializer<Data extends AbstractCollect
}
}
protected void resolveInstance(Data data, boolean isEager) {
if ( data.getState() != State.KEY_RESOLVED ) {
// already resolved
return;
}
resolveCollectionKey( data, false );
if ( data.getState() == State.KEY_RESOLVED ) {
assert parent != null;
final RowProcessingState rowProcessingState = data.getRowProcessingState();
// We can avoid processing further if the parent is already initialized,
// as the value produced by this initializer will never be used anyway.
if ( owningEntityInitializer != null
&& owningEntityInitializer.getData( rowProcessingState ).getState() == State.INITIALIZED ) {
// 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
data.setState( State.MISSING );
return;
}
data.setState( State.RESOLVED );
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContext();
final LoadingCollectionEntry loadingEntry = persistenceContext.getLoadContexts()
.findLoadingCollectionEntry( data.collectionKey );
if ( loadingEntry != null ) {
data.setCollectionInstance( loadingEntry.getCollectionInstance() );
if ( data.getCollectionInstance().getOwner() == null ) {
assert owningEntityInitializer.getTargetInstance( rowProcessingState ) != null;
data.getCollectionInstance().setOwner( owningEntityInitializer.getTargetInstance( rowProcessingState ) );
}
return;
}
final PersistentCollection<?> existing = persistenceContext.getCollection( data.collectionKey );
if ( existing != null ) {
data.setCollectionInstance( existing );
if ( data.getCollectionInstance().getOwner() == null ) {
assert owningEntityInitializer.getTargetInstance( rowProcessingState ) != null;
data.getCollectionInstance().setOwner( owningEntityInitializer.getTargetInstance( rowProcessingState ) );
}
return;
}
final CollectionPersister collectionDescriptor = collectionAttributeMapping.getCollectionDescriptor();
final CollectionSemantics<?, ?> collectionSemantics = collectionDescriptor.getCollectionSemantics();
final Object key = data.collectionKey.getKey();
data.setCollectionInstance( collectionSemantics.instantiateWrapper(
key,
collectionDescriptor,
session
) );
assert owningEntityInitializer.getTargetInstance( rowProcessingState ) != null;
data.getCollectionInstance().setOwner( owningEntityInitializer.getTargetInstance( rowProcessingState ) );
persistenceContext.addUninitializedCollection(
collectionDescriptor,
data.getCollectionInstance(),
key
);
if ( isEager ) {
persistenceContext.addNonLazyCollection( data.getCollectionInstance() );
}
if ( collectionSemantics.getCollectionClassification() == CollectionClassification.ARRAY ) {
session.getPersistenceContext().addCollectionHolder( data.getCollectionInstance() );
}
}
}
protected void resolveInstance(Object instance, Data data, boolean isEager) {
if ( instance == null ) {
setMissing( data );
}
else {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
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 );
data.setCollectionInstance( persistentCollection );
data.setState( State.RESOLVED );
if ( isEager && !data.getCollectionInstance().wasInitialized() ) {
persistenceContext.addNonLazyCollection( data.getCollectionInstance() );
}
if ( collectionKeyResultAssembler != null && rowProcessingState.needsResolveState() ) {
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
collectionKeyResultAssembler.resolveState( rowProcessingState );
}
}
private boolean areKeysEqual(Object key1, Object key2) {
return keyTypeForEqualsHashCode == null ? key1.equals( key2 ) : keyTypeForEqualsHashCode.isEqual( key1, key2 );
}
@Override
@ -327,6 +234,16 @@ public abstract class AbstractCollectionInitializer<Data extends AbstractCollect
return false;
}
@Override
public boolean isEager() {
return true;
}
@Override
public boolean hasEagerSubInitializers() {
return true;
}
@Override
public boolean isResultInitializer() {
return isResultInitializer;

View File

@ -12,9 +12,9 @@ import java.util.function.BiConsumer;
import org.hibernate.LockMode;
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.PluralAttributeMapping;
import org.hibernate.persister.collection.CollectionPersister;
@ -49,7 +49,7 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
public static class ImmediateCollectionInitializerData extends CollectionInitializerData {
protected boolean shallowCached;
protected final boolean shallowCached;
/**
* The value of the collection side of the collection key (FK). Identifies
@ -59,8 +59,10 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
protected Object collectionValueKey;
protected LoadingCollectionEntryImpl responsibility;
public ImmediateCollectionInitializerData(RowProcessingState rowProcessingState) {
public ImmediateCollectionInitializerData(AbstractImmediateCollectionInitializer<?> initializer, RowProcessingState rowProcessingState) {
super( rowProcessingState );
shallowCached = rowProcessingState.isQueryCacheHit()
&& initializer.getInitializingCollectionDescriptor().useShallowQueryCacheLayout();
}
}
@ -88,11 +90,9 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
@Override
protected ImmediateCollectionInitializerData createInitializerData(RowProcessingState rowProcessingState) {
return new ImmediateCollectionInitializerData( rowProcessingState );
return new ImmediateCollectionInitializerData( this, rowProcessingState );
}
protected abstract String getSimpleConcreteImplName();
@Override
protected void forEachSubInitializer(BiConsumer<Initializer<?>, RowProcessingState> consumer, InitializerData data) {
super.forEachSubInitializer( consumer, data );
@ -104,16 +104,6 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
}
}
@Override
public void startLoading(RowProcessingState rowProcessingState) {
final ImmediateCollectionInitializerData data = createInitializerData( rowProcessingState );
rowProcessingState.setInitializerData( initializerId, data );
if ( rowProcessingState.isQueryCacheHit() && getInitializingCollectionDescriptor().useShallowQueryCacheLayout() ) {
data.shallowCached = true;
}
forEachSubInitializer( Initializer::startLoading, data );
}
@Override
public void resolveKey(Data data) {
if ( data.getState() != State.UNINITIALIZED ) {
@ -135,6 +125,22 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
}
}
@Override
public void resolveState(Data data) {
super.resolveState( data );
if ( collectionValueKeyResultAssembler != null ) {
collectionValueKeyResultAssembler.resolveState( data.getRowProcessingState() );
}
final DomainResultAssembler<?> indexAssembler = getIndexAssembler();
if ( indexAssembler != null ) {
indexAssembler.resolveState( data.getRowProcessingState() );
}
final DomainResultAssembler<?> elementAssembler = getElementAssembler();
if ( elementAssembler != null ) {
elementAssembler.resolveState( data.getRowProcessingState() );
}
}
@Override
public void resolveFromPreviousRow(Data data) {
super.resolveFromPreviousRow( data );
@ -212,8 +218,10 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final CollectionKey collectionKey = data.collectionKey;
assert collectionKey != null;
final LoadingCollectionEntry existingLoadingEntry = persistenceContext.getLoadContexts()
.findLoadingCollectionEntry( data.collectionKey );
.findLoadingCollectionEntry( collectionKey );
final PersistentCollection<?> existing;
final PersistentCollection<?> existingUnowned;
if ( existingLoadingEntry != null ) {
@ -229,26 +237,26 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
data.setState( State.INITIALIZED );
}
}
else if ( ( existing = persistenceContext.getCollection( data.collectionKey ) ) != null ) {
else if ( ( existing = persistenceContext.getCollection( collectionKey ) ) != null ) {
data.setCollectionInstance( existing );
// we found the corresponding collection instance on the Session. If
// it is already initialized we have nothing to do
if ( data.getCollectionInstance().wasInitialized() ) {
if ( existing.wasInitialized() ) {
data.setState( State.INITIALIZED );
}
else if ( !data.shallowCached ) {
takeResponsibility( data );
}
}
else if ( ( existingUnowned = persistenceContext.useUnownedCollection( data.collectionKey ) ) != null ) {
else if ( ( existingUnowned = persistenceContext.useUnownedCollection( collectionKey ) ) != null ) {
data.setCollectionInstance( existingUnowned );
// we found the corresponding collection instance as unowned on the Session. If
// it is already initialized we have nothing to do
if ( data.getCollectionInstance().wasInitialized() ) {
if ( existingUnowned.wasInitialized() ) {
data.setState( State.INITIALIZED );
}
else if ( !data.shallowCached ) {
@ -258,12 +266,12 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
else {
final CollectionPersister collectionDescriptor = getCollectionAttributeMapping().getCollectionDescriptor();
final CollectionSemantics<?, ?> collectionSemantics = collectionDescriptor.getCollectionSemantics();
data.setCollectionInstance( collectionSemantics.instantiateWrapper(
data.collectionKey.getKey(),
final PersistentCollection<?> persistentCollection = collectionSemantics.instantiateWrapper(
collectionKey.getKey(),
getInitializingCollectionDescriptor(),
session
) );
);
data.setCollectionInstance( persistentCollection );
if ( owningEntityInitializer != null ) {
assert owningEntityInitializer.getTargetInstance( rowProcessingState ) != null;
@ -272,8 +280,8 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
persistenceContext.addUninitializedCollection(
collectionDescriptor,
data.getCollectionInstance(),
data.collectionKey.getKey()
persistentCollection,
collectionKey.getKey()
);
if ( !data.shallowCached ) {
@ -293,11 +301,13 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
.getPersistenceContextInternal();
// If this is a query cache hit with the shallow query cache layout,
// we have to lazy load the collection instead
data.getCollectionInstance().forceInitialization();
final PersistentCollection<?> collectionInstance = data.getCollectionInstance();
assert collectionInstance != null;
collectionInstance.forceInitialization();
if ( collectionAttributeMapping.getCollectionDescriptor()
.getCollectionSemantics()
.getCollectionClassification() == CollectionClassification.ARRAY ) {
persistenceContext.addCollectionHolder( data.getCollectionInstance() );
persistenceContext.addCollectionHolder( collectionInstance );
}
data.setState( State.INITIALIZED );
initializeSubInstancesFromParent( data );
@ -318,10 +328,10 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
return;
}
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final PersistentCollection<?> persistentCollection;
// Check if the given instance is different from the previous row state to avoid creating CollectionKey
if ( data.getCollectionInstance() != instance ) {
final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal();
final PersistentCollection<?> persistentCollection;
if ( collectionAttributeMapping.getCollectionDescriptor()
.getCollectionSemantics()
.getCollectionClassification() == CollectionClassification.ARRAY ) {
@ -335,8 +345,11 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
data.setCollectionInstance( persistentCollection );
data.responsibility = null;
}
else {
persistentCollection = (PersistentCollection<?>) instance;
}
data.collectionValueKey = null;
if ( data.getCollectionInstance().wasInitialized() ) {
if ( persistentCollection.wasInitialized() ) {
data.setState( State.INITIALIZED );
if ( data.shallowCached ) {
initializeShallowCached( data );
@ -410,16 +423,6 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
getElementAssembler().resolveState( rowProcessingState );
}
/**
* Specialized toString handling for PersistentCollection. All `PersistentCollection#toString`
* implementations are crazy expensive as they trigger a load
*/
private String toLoggableString(PersistentCollection<?> collectionInstance) {
return collectionInstance == null
? LoggingHelper.NULL
: collectionInstance.getClass().getName() + "@" + System.identityHashCode( collectionInstance );
}
protected void takeResponsibility(Data data) {
data.responsibility = new LoadingCollectionEntryImpl(
getCollectionAttributeMapping().getCollectionDescriptor(),
@ -473,10 +476,4 @@ public abstract class AbstractImmediateCollectionInitializer<Data extends Abstra
public abstract @Nullable DomainResultAssembler<?> getIndexAssembler();
public abstract DomainResultAssembler<?> getElementAssembler();
@Override
public void endLoading(Data data) {
super.endLoading( data );
data.shallowCached = false;
}
}

View File

@ -0,0 +1,165 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.sql.results.graph.collection.internal;
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.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.collection.CollectionPersister;
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.InitializerData;
import org.hibernate.sql.results.graph.InitializerParent;
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Base support for CollectionInitializer implementations that don't join data
*
* @author Steve Ebersole
*/
public abstract class AbstractNonJoinCollectionInitializer<Data extends AbstractCollectionInitializer.CollectionInitializerData>
extends AbstractCollectionInitializer<Data> {
public AbstractNonJoinCollectionInitializer(
NavigablePath collectionPath,
PluralAttributeMapping collectionAttributeMapping,
InitializerParent<?> parent,
@Nullable DomainResult<?> collectionKeyResult,
boolean isResultInitializer,
AssemblerCreationState creationState) {
super(
collectionPath,
collectionAttributeMapping,
parent,
collectionKeyResult,
isResultInitializer,
creationState
);
}
protected void resolveInstance(Data data, boolean isEager) {
if ( data.getState() != State.KEY_RESOLVED ) {
// already resolved
return;
}
resolveCollectionKey( data, false );
if ( data.getState() == State.KEY_RESOLVED ) {
assert owningEntityInitializer != null;
final RowProcessingState rowProcessingState = data.getRowProcessingState();
// We can avoid processing further if the parent is already initialized,
// as the value produced by this initializer will never be used anyway.
final InitializerData owningEntityData = owningEntityInitializer.getData( rowProcessingState );
if ( owningEntityData.getState() == State.INITIALIZED ) {
// 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
data.setState( State.MISSING );
return;
}
// This initializer is done initializing, since this is only invoked for delayed or select initializers
data.setState( State.INITIALIZED );
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final CollectionKey collectionKey = data.collectionKey;
assert collectionKey != null;
final LoadingCollectionEntry loadingEntry = persistenceContext.getLoadContexts()
.findLoadingCollectionEntry( collectionKey );
if ( loadingEntry != null ) {
final PersistentCollection<?> collectionInstance = loadingEntry.getCollectionInstance();
data.setCollectionInstance( collectionInstance );
if ( collectionInstance.getOwner() == null ) {
assert owningEntityInitializer.getTargetInstance( owningEntityData ) != null;
collectionInstance.setOwner( owningEntityInitializer.getTargetInstance( owningEntityData ) );
}
return;
}
final PersistentCollection<?> existing = persistenceContext.getCollection( collectionKey );
if ( existing != null ) {
data.setCollectionInstance( existing );
if ( existing.getOwner() == null ) {
assert owningEntityInitializer.getTargetInstance( owningEntityData ) != null;
existing.setOwner( owningEntityInitializer.getTargetInstance( owningEntityData ) );
}
return;
}
final CollectionPersister collectionDescriptor = collectionAttributeMapping.getCollectionDescriptor();
final CollectionSemantics<?, ?> collectionSemantics = collectionDescriptor.getCollectionSemantics();
final Object key = collectionKey.getKey();
final PersistentCollection<?> persistentCollection = collectionSemantics.instantiateWrapper(
key,
collectionDescriptor,
session
);
data.setCollectionInstance( persistentCollection );
assert owningEntityInitializer.getTargetInstance( owningEntityData ) != null;
persistentCollection.setOwner( owningEntityInitializer.getTargetInstance( owningEntityData ) );
persistenceContext.addUninitializedCollection(
collectionDescriptor,
persistentCollection,
key
);
if ( isEager ) {
persistenceContext.addNonLazyCollection( persistentCollection );
}
if ( collectionSemantics.getCollectionClassification() == CollectionClassification.ARRAY ) {
persistenceContext.addCollectionHolder( persistentCollection );
}
}
}
protected void resolveInstance(Object instance, Data data, boolean isEager) {
if ( instance == null ) {
setMissing( data );
}
else {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
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 );
data.setCollectionInstance( persistentCollection );
// This initializer is done initializing, since this is only invoked for delayed or select initializers
data.setState( State.INITIALIZED );
if ( isEager && !persistentCollection.wasInitialized() ) {
persistenceContext.addNonLazyCollection( persistentCollection );
}
if ( collectionKeyResultAssembler != null && rowProcessingState.needsResolveState() ) {
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
collectionKeyResultAssembler.resolveState( rowProcessingState );
}
}
}
}

View File

@ -31,8 +31,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* @author Chris Cranford
*/
public class ArrayInitializer extends AbstractImmediateCollectionInitializer<AbstractImmediateCollectionInitializer.ImmediateCollectionInitializerData> {
private static final String CONCRETE_NAME = ArrayInitializer.class.getSimpleName();
private final DomainResultAssembler<Integer> listIndexAssembler;
private final DomainResultAssembler<?> elementAssembler;
@ -65,11 +63,6 @@ public class ArrayInitializer extends AbstractImmediateCollectionInitializer<Abs
this.indexBase = getCollectionAttributeMapping().getIndexMetadata().getListIndexBase();
}
@Override
protected String getSimpleConcreteImplName() {
return CONCRETE_NAME;
}
@Override
protected void forEachSubInitializer(BiConsumer<Initializer<?>, RowProcessingState> consumer, InitializerData data) {
super.forEachSubInitializer( consumer, data );

View File

@ -34,7 +34,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* @author Steve Ebersole
*/
public class BagInitializer extends AbstractImmediateCollectionInitializer<AbstractImmediateCollectionInitializer.ImmediateCollectionInitializerData> {
private static final String CONCRETE_NAME = BagInitializer.class.getSimpleName();
private final DomainResultAssembler<?> elementAssembler;
private final DomainResultAssembler<?> collectionIdAssembler;
@ -66,11 +65,6 @@ public class BagInitializer extends AbstractImmediateCollectionInitializer<Abstr
: collectionIdFetch.createAssembler( this, creationState );
}
@Override
protected String getSimpleConcreteImplName() {
return CONCRETE_NAME;
}
@Override
protected void forEachSubInitializer(BiConsumer<Initializer<?>, RowProcessingState> consumer, InitializerData data) {
super.forEachSubInitializer( consumer, data );

View File

@ -53,7 +53,7 @@ public class CollectionAssembler implements DomainResultAssembler {
@Override
public void resolveState(RowProcessingState rowProcessingState) {
initializer.resolveInstance( rowProcessingState );
initializer.resolveState( rowProcessingState );
}
@Override

View File

@ -16,7 +16,7 @@ import org.hibernate.sql.results.graph.InitializerParent;
/**
* @author Steve Ebersole
*/
public class DelayedCollectionInitializer extends AbstractCollectionInitializer<AbstractCollectionInitializer.CollectionInitializerData> {
public class DelayedCollectionInitializer extends AbstractNonJoinCollectionInitializer<AbstractCollectionInitializer.CollectionInitializerData> {
public DelayedCollectionInitializer(
NavigablePath fetchedPath,
@ -37,6 +37,17 @@ public class DelayedCollectionInitializer extends AbstractCollectionInitializer<
resolveInstance( instance, data, false );
}
@Override
public boolean isEager() {
// No need to call resolve on this initializer if parent is initialized
return false;
}
@Override
public boolean hasEagerSubInitializers() {
return false;
}
@Override
public String toString() {
return "DelayedCollectionInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";

View File

@ -32,7 +32,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* @author Steve Ebersole
*/
public class ListInitializer extends AbstractImmediateCollectionInitializer<AbstractImmediateCollectionInitializer.ImmediateCollectionInitializerData> {
private static final String CONCRETE_NAME = ListInitializer.class.getSimpleName();
private final DomainResultAssembler<Integer> listIndexAssembler;
private final DomainResultAssembler<?> elementAssembler;
@ -66,11 +65,6 @@ public class ListInitializer extends AbstractImmediateCollectionInitializer<Abst
this.listIndexBase = attributeMapping.getIndexMetadata().getListIndexBase();
}
@Override
protected String getSimpleConcreteImplName() {
return CONCRETE_NAME;
}
@Override
protected void forEachSubInitializer(BiConsumer<Initializer<?>, RowProcessingState> consumer, InitializerData data) {
super.forEachSubInitializer( consumer, data );

View File

@ -35,7 +35,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* @author Steve Ebersole
*/
public class MapInitializer extends AbstractImmediateCollectionInitializer<AbstractImmediateCollectionInitializer.ImmediateCollectionInitializerData> {
private static final String CONCRETE_NAME = MapInitializer.class.getSimpleName();
private final DomainResultAssembler<?> mapKeyAssembler;
private final DomainResultAssembler<?> mapValueAssembler;
@ -65,11 +64,6 @@ public class MapInitializer extends AbstractImmediateCollectionInitializer<Abstr
this.mapValueAssembler = mapValueFetch.createAssembler( this, creationState );
}
@Override
protected String getSimpleConcreteImplName() {
return CONCRETE_NAME;
}
@Override
protected void forEachSubInitializer(BiConsumer<Initializer<?>, RowProcessingState> consumer, InitializerData data) {
super.forEachSubInitializer( consumer, data );

View File

@ -20,7 +20,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
/**
* @author Andrea Boriero
*/
public class SelectEagerCollectionInitializer extends AbstractCollectionInitializer<AbstractCollectionInitializer.CollectionInitializerData> {
public class SelectEagerCollectionInitializer extends AbstractNonJoinCollectionInitializer<AbstractCollectionInitializer.CollectionInitializerData> {
public SelectEagerCollectionInitializer(
NavigablePath fetchedPath,
@ -44,17 +44,24 @@ public class SelectEagerCollectionInitializer extends AbstractCollectionInitiali
@Override
public void initializeInstanceFromParent(Object parentInstance, CollectionInitializerData data) {
final Object instance = getInitializedPart().getValue( parentInstance );
if ( collectionAttributeMapping.getCollectionDescriptor()
.getCollectionSemantics()
.getCollectionClassification() == CollectionClassification.ARRAY ) {
data.setCollectionInstance( data.getRowProcessingState().getSession().getPersistenceContextInternal()
.getCollectionHolder( instance ) );
if ( instance == null ) {
setMissing( data );
}
else {
data.setCollectionInstance( (PersistentCollection<?>) instance );
final PersistentCollection<?> collection;
if ( collectionAttributeMapping.getCollectionDescriptor()
.getCollectionSemantics()
.getCollectionClassification() == CollectionClassification.ARRAY ) {
collection = data.getRowProcessingState().getSession().getPersistenceContextInternal()
.getCollectionHolder( instance );
}
else {
collection = (PersistentCollection<?>) instance;
}
data.setState( State.INITIALIZED );
data.setCollectionInstance( collection );
collection.forceInitialization();
}
data.setState( State.INITIALIZED );
data.getCollectionInstance().forceInitialization();
}
@Override

View File

@ -29,7 +29,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* @author Steve Ebersole
*/
public class SetInitializer extends AbstractImmediateCollectionInitializer<AbstractImmediateCollectionInitializer.ImmediateCollectionInitializerData> {
private static final String CONCRETE_NAME = SetInitializer.class.getSimpleName();
private final DomainResultAssembler<?> elementAssembler;
@ -56,11 +55,6 @@ public class SetInitializer extends AbstractImmediateCollectionInitializer<Abstr
this.elementAssembler = elementFetch.createAssembler( this, creationState );
}
@Override
protected String getSimpleConcreteImplName() {
return CONCRETE_NAME;
}
@Override
protected void forEachSubInitializer(BiConsumer<Initializer<?>, RowProcessingState> consumer, InitializerData data) {
super.forEachSubInitializer( consumer, data );

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;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface AggregateEmbeddableResultGraphNode extends EmbeddableResultGraphNode {
/**
* Returns the positions within the values array of the respective nesting level
* at which the data for this aggregate can be found.
*/
int[] getAggregateValuesArrayPositions();
static int[] determineAggregateValuesArrayPositions(@Nullable FetchParent parent, SqlSelection structSelection) {
if ( parent instanceof AggregateEmbeddableResultGraphNode ) {
final int[] parentAggregateValuesArrayPositions = ( (AggregateEmbeddableResultGraphNode) parent ).getAggregateValuesArrayPositions();
final int[] aggregateValuesArrayPositions = new int[parentAggregateValuesArrayPositions.length + 1];
System.arraycopy(
parentAggregateValuesArrayPositions,
0,
aggregateValuesArrayPositions,
0,
parentAggregateValuesArrayPositions.length
);
aggregateValuesArrayPositions[aggregateValuesArrayPositions.length - 1] = structSelection.getValuesArrayPosition();
return aggregateValuesArrayPositions;
}
else if ( parent instanceof Fetch && parent instanceof EmbeddableResultGraphNode ) {
return determineAggregateValuesArrayPositions( ( (Fetch) parent ).getFetchParent(), structSelection );
}
return new int[] { structSelection.getValuesArrayPosition() };
}
}

View File

@ -6,11 +6,9 @@
*/
package org.hibernate.sql.results.graph.embeddable;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.sql.results.graph.InitializerData;
import org.hibernate.sql.results.graph.InitializerParent;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -23,11 +21,6 @@ public interface EmbeddableInitializer<Data extends InitializerData> extends Ini
@Override
EmbeddableValuedModelPart getInitializedPart();
Object getCompositeInstance(Data data);
default Object getCompositeInstance(RowProcessingState rowProcessingState) {
return getCompositeInstance( getData( rowProcessingState ) );
}
@Override
@Nullable InitializerParent<?> getParent();
@ -41,6 +34,4 @@ public interface EmbeddableInitializer<Data extends InitializerData> extends Ini
return this;
}
void resolveState(RowProcessingState rowProcessingState);
}

View File

@ -30,8 +30,8 @@ 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.AggregateEmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
import org.hibernate.type.spi.TypeConfiguration;
@ -45,14 +45,14 @@ import static org.hibernate.internal.util.NullnessUtil.castNonNull;
* for creating the fetches for the attributes of the embeddable.
*/
public class AggregateEmbeddableFetchImpl extends AbstractFetchParent
implements EmbeddableResultGraphNode, Fetch, InitializerProducer<AggregateEmbeddableFetchImpl> {
implements AggregateEmbeddableResultGraphNode, Fetch, InitializerProducer<AggregateEmbeddableFetchImpl> {
private final FetchParent fetchParent;
private final FetchTiming fetchTiming;
private final TableGroup tableGroup;
private final boolean hasTableGroup;
private final SqlSelection aggregateSelection;
private final EmbeddableMappingType fetchContainer;
private final BasicFetch<?> discriminatorFetch;
private final int[] aggregateValuesArrayPositions;
public AggregateEmbeddableFetchImpl(
NavigablePath navigablePath,
@ -97,16 +97,22 @@ public class AggregateEmbeddableFetchImpl extends AbstractFetchParent
final TypeConfiguration typeConfiguration = sqlAstCreationState.getCreationContext()
.getSessionFactory()
.getTypeConfiguration();
this.aggregateSelection = sqlExpressionResolver.resolveSqlSelection(
final SqlSelection aggregateSelection = sqlExpressionResolver.resolveSqlSelection(
expression,
typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Object[].class ),
fetchParent,
typeConfiguration
);
this.discriminatorFetch = creationState.visitEmbeddableDiscriminatorFetch( this, true );
this.aggregateValuesArrayPositions = AggregateEmbeddableResultGraphNode.determineAggregateValuesArrayPositions( fetchParent, aggregateSelection );
resetFetches( creationState.visitNestedFetches( this ) );
}
@Override
public int[] getAggregateValuesArrayPositions() {
return aggregateValuesArrayPositions;
}
@Override
public FetchTiming getTiming() {
return fetchTiming;
@ -181,8 +187,7 @@ public class AggregateEmbeddableFetchImpl extends AbstractFetchParent
discriminatorFetch,
parent,
creationState,
false,
aggregateSelection
false
);
}

View File

@ -6,15 +6,11 @@
*/
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.Initializer;
import org.hibernate.sql.results.graph.InitializerParent;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.embeddable.AggregateEmbeddableResultGraphNode;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
/**
@ -27,25 +23,13 @@ public class AggregateEmbeddableInitializerImpl extends EmbeddableInitializerImp
private final int[] aggregateValuesArrayPositions;
public AggregateEmbeddableInitializerImpl(
EmbeddableResultGraphNode resultDescriptor,
AggregateEmbeddableResultGraphNode resultDescriptor,
BasicFetch<?> discriminatorFetch,
InitializerParent<?> parent,
AssemblerCreationState creationState,
boolean isResultInitializer,
SqlSelection structSelection) {
boolean isResultInitializer) {
super( resultDescriptor, discriminatorFetch, parent, creationState, isResultInitializer );
this.aggregateValuesArrayPositions = determineAggregateValuesArrayPositions(
parent,
structSelection
);
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 );
this.aggregateValuesArrayPositions = resultDescriptor.getAggregateValuesArrayPositions();
}
@Override
@ -53,16 +37,6 @@ public class AggregateEmbeddableInitializerImpl extends EmbeddableInitializerImp
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.isPolymorphic() ? embeddableTypeDescriptor.getConcreteEmbeddableTypes().size() : 1][];
}
public int[] getAggregateValuesArrayPositions() {
return aggregateValuesArrayPositions;
}
@ -79,26 +53,4 @@ public class AggregateEmbeddableInitializerImpl extends EmbeddableInitializerImp
return jdbcValue;
}
static int[] determineAggregateValuesArrayPositions(
InitializerParent<?> parent,
SqlSelection structSelection) {
if ( parent instanceof AggregateEmbeddableInitializerImpl ) {
final int[] parentAggregateValuesArrayPositions = ( (AggregateEmbeddableInitializerImpl) parent ).getAggregateValuesArrayPositions();
final int[] aggregateValuesArrayPositions = new int[parentAggregateValuesArrayPositions.length + 1];
System.arraycopy(
parentAggregateValuesArrayPositions,
0,
aggregateValuesArrayPositions,
0,
parentAggregateValuesArrayPositions.length
);
aggregateValuesArrayPositions[aggregateValuesArrayPositions.length - 1] = structSelection.getValuesArrayPosition();
return aggregateValuesArrayPositions;
}
else if ( parent instanceof EmbeddableInitializer ) {
return determineAggregateValuesArrayPositions( parent.getParent(), structSelection );
}
return new int[] { structSelection.getValuesArrayPosition() };
}
}

View File

@ -31,8 +31,8 @@ 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.AggregateEmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResult;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.spi.TypeConfiguration;
@ -44,15 +44,15 @@ import org.hibernate.type.spi.TypeConfiguration;
* uses {@link org.hibernate.sql.results.graph.DomainResultCreationState#visitNestedFetches(FetchParent)}
* for creating the fetches for the attributes of the embeddable.
*/
public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implements EmbeddableResultGraphNode,
public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implements AggregateEmbeddableResultGraphNode,
DomainResult<T>,
EmbeddableResult<T>,
InitializerProducer<AggregateEmbeddableResultImpl<T>> {
private final String resultVariable;
private final boolean containsAnyNonScalars;
private final SqlSelection aggregateSelection;
private final EmbeddableMappingType fetchContainer;
private final BasicFetch<?> discriminatorFetch;
private final int[] aggregateValuesArrayPositions;
public AggregateEmbeddableResultImpl(
NavigablePath navigablePath,
@ -99,7 +99,7 @@ public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implem
final TypeConfiguration typeConfiguration = sqlAstCreationState.getCreationContext()
.getSessionFactory()
.getTypeConfiguration();
this.aggregateSelection = sqlExpressionResolver.resolveSqlSelection(
final SqlSelection aggregateSelection = sqlExpressionResolver.resolveSqlSelection(
expression,
// Using the Object[] type here, so that a different JDBC extractor is chosen
typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Object[].class ),
@ -107,10 +107,16 @@ public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implem
typeConfiguration
);
this.discriminatorFetch = creationState.visitEmbeddableDiscriminatorFetch( this, true );
this.aggregateValuesArrayPositions = AggregateEmbeddableResultGraphNode.determineAggregateValuesArrayPositions( null, aggregateSelection );
resetFetches( creationState.visitNestedFetches( this ) );
this.containsAnyNonScalars = determineIfContainedAnyScalars( getFetches() );
}
@Override
public int[] getAggregateValuesArrayPositions() {
return aggregateValuesArrayPositions;
}
private static boolean determineIfContainedAnyScalars(ImmutableFetchList fetches) {
for ( Fetch fetch : fetches ) {
if ( fetch.containsAnyNonScalarResults() ) {
@ -174,8 +180,7 @@ public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implem
discriminatorFetch,
parent,
creationState,
true,
aggregateSelection
true
);
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph.embeddable.internal;
import java.util.function.BiConsumer;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.InitializerData;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
import org.hibernate.sql.results.graph.DomainResultAssembler;
@ -32,8 +33,11 @@ public class EmbeddableAssembler implements DomainResultAssembler {
@Override
public Object assemble(RowProcessingState rowProcessingState) {
final InitializerData data = initializer.getData( rowProcessingState );
initializer.resolveInstance( data );
return initializer.getCompositeInstance( data );
final Initializer.State state = data.getState();
if ( state == Initializer.State.KEY_RESOLVED ) {
initializer.resolveInstance( data );
}
return initializer.getResolvedInstance( data );
}
@Override

View File

@ -6,7 +6,7 @@
*/
package org.hibernate.sql.results.graph.embeddable.internal;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.function.BiConsumer;
@ -52,6 +52,7 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
private final NavigablePath navigablePath;
private final EmbeddableValuedModelPart embedded;
private final EmbeddableMappingType embeddableMappingType;
private final InitializerParent<InitializerData> parent;
private final boolean isResultInitializer;
private final boolean isPartOfKey;
@ -59,8 +60,9 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
private final SessionFactoryImplementor sessionFactory;
protected final DomainResultAssembler<?>[][] assemblers;
private final BasicResultAssembler<?> discriminatorAssembler;
protected final Initializer<InitializerData>[][] subInitializers;
protected final BasicResultAssembler<?> discriminatorAssembler;
protected final @Nullable Initializer<InitializerData>[][] subInitializers;
protected final BitSet subInitializersNeedingResolveIfParentInitialized;
public static class EmbeddableInitializerData extends InitializerData implements ValueAccess {
protected final InitializerData parentData;
@ -70,8 +72,7 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
public EmbeddableInitializerData(EmbeddableInitializerImpl initializer, RowProcessingState rowProcessingState) {
super( rowProcessingState );
this.parentData = initializer.parent == null ? null : initializer.parent.getData( rowProcessingState );
final EmbeddableMappingType embeddableTypeDescriptor = initializer.embedded.getEmbeddableTypeDescriptor();
final int size = embeddableTypeDescriptor.getNumberOfFetchables();
final int size = initializer.embeddableMappingType.getNumberOfFetchables();
this.rowState = new Object[ size ];
}
@ -107,21 +108,33 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
this.parent = (InitializerParent<InitializerData>) parent;
this.isResultInitializer = isResultInitializer;
final EmbeddableMappingType embeddableTypeDescriptor = embedded.getEmbeddableTypeDescriptor();
this.embeddableMappingType = embedded.getEmbeddableTypeDescriptor();
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.createEmptyCompositesEnabled = !isPartOfKey && embeddableMappingType.isCreateEmptyCompositesEnabled();
this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
this.assemblers = createAssemblers(
resultDescriptor,
creationState,
embeddableTypeDescriptor
embeddableMappingType
);
this.discriminatorAssembler = discriminatorFetch != null ?
(BasicResultAssembler<?>) discriminatorFetch.createAssembler( this, creationState ) :
null;
this.subInitializers = createInitializers( assemblers );
final BitSet subInitializersNeedingResolveIfParentInitialized = new BitSet(subInitializers.length * embeddableMappingType.getNumberOfFetchables());
for ( int subclassId = 0; subclassId < subInitializers.length; subclassId++ ) {
final Initializer<InitializerData>[] subInitializer = subInitializers[subclassId];
for ( int i = 0; i < subInitializer.length; i++ ) {
final Initializer<InitializerData> initializer = subInitializer[i];
if ( initializer != null ) {
subInitializersNeedingResolveIfParentInitialized.set( subclassId * embeddableMappingType.getNumberOfFetchables() + i );
}
}
}
this.subInitializersNeedingResolveIfParentInitialized = subInitializersNeedingResolveIfParentInitialized;
}
protected DomainResultAssembler<?>[][] createAssemblers(
@ -157,27 +170,23 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
return new DomainResultAssembler[][] { overallAssemblers };
}
protected static Initializer<InitializerData>[][] createInitializers(DomainResultAssembler<?>[][] assemblers) {
Initializer<?>[][] subInitializers = new Initializer<?>[assemblers.length][];
protected static @Nullable Initializer<InitializerData>[][] createInitializers(DomainResultAssembler<?>[][] assemblers) {
@Nullable 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 );
}
final @Nullable Initializer<?>[] initializers = new Initializer[subAssemblers.length];
boolean empty = true;
for ( int j = 0; j < subAssemblers.length; j++ ) {
final DomainResultAssembler<?> assembler = subAssemblers[j];
if ( assembler != null ) {
final Initializer<?> initializer = assembler.getInitializer();
if ( initializer != null ) {
initializers[j] = initializer;
empty = false;
}
}
subInitializers[i] = initializers.isEmpty()
? Initializer.EMPTY_ARRAY
: initializers.toArray( EMPTY_ARRAY );
}
else {
subInitializers[i] = Initializer.EMPTY_ARRAY;
}
subInitializers[i] = empty ? Initializer.EMPTY_ARRAY : initializers;
}
//noinspection unchecked
return (Initializer<InitializerData>[][]) subInitializers;
@ -204,14 +213,20 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
}
@Override
public Object getCompositeInstance(EmbeddableInitializerData data) {
final State state = data.getState();
return state == State.RESOLVED || state == State.INITIALIZED ? data.getInstance() : null;
public boolean isPartOfKey() {
return isPartOfKey;
}
@Override
public boolean isPartOfKey() {
return isPartOfKey;
public boolean isEager() {
// Embeddables are never lazy
return true;
}
@Override
public boolean hasEagerSubInitializers() {
// Since embeddables are never lazy, we only need to check the components
return !subInitializersNeedingResolveIfParentInitialized.isEmpty();
}
@Override
@ -227,16 +242,16 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
// We need to possibly wrap the processing state if the embeddable is within an aggregate
data.setInstance( null );
if ( discriminatorAssembler != null ) {
assert embedded.getEmbeddableTypeDescriptor().getDiscriminatorMapping() != null;
assert embeddableMappingType.getDiscriminatorMapping() != null;
// todo: add more info into EmbeddableDiscriminatorConverter to extract this details object directly
final Object discriminatorValue = discriminatorAssembler.extractRawValue( data.getRowProcessingState() );
data.concreteEmbeddableType = discriminatorValue == null
? null
: embedded.getEmbeddableTypeDescriptor().findSubtypeByDiscriminator( discriminatorValue );
: embeddableMappingType.findSubtypeByDiscriminator( discriminatorValue );
}
if ( isPartOfKey ) {
data.setState( State.KEY_RESOLVED );
if ( subInitializers.length == 0 ) {
if ( subInitializers[data.getSubclassId()].length == 0 ) {
// Resolve the component early to know if the key is missing or not
resolveInstance( data );
}
@ -252,11 +267,13 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
private void resolveKeySubInitializers(EmbeddableInitializerData data) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( Initializer<InitializerData> initializer : subInitializers[data.getSubclassId()] ) {
final InitializerData subData = initializer.getData( rowProcessingState );
initializer.resolveKey( subData );
if ( subData.getState() == State.MISSING ) {
data.setState( State.MISSING );
return;
if ( initializer != null ) {
final InitializerData subData = initializer.getData( rowProcessingState );
initializer.resolveKey( subData );
if ( subData.getState() == State.MISSING ) {
data.setState( State.MISSING );
return;
}
}
}
}
@ -270,7 +287,9 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
else {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( Initializer<InitializerData> initializer : subInitializers[data.getSubclassId()] ) {
initializer.resolveFromPreviousRow( rowProcessingState );
if ( initializer != null ) {
initializer.resolveFromPreviousRow( rowProcessingState );
}
}
data.setState( State.INITIALIZED );
}
@ -297,11 +316,30 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
else {
data.setState( State.INITIALIZED );
data.setInstance( instance );
final int subclassId = data.getSubclassId();
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( Initializer<?> initializer : subInitializers[data.getSubclassId()] ) {
final Object subInstance = initializer.getInitializedPart()
.asAttributeMapping()
.getValue( instance );
resolveInstanceSubInitializers( subclassId, instance, rowProcessingState );
if ( rowProcessingState.needsResolveState() ) {
for ( DomainResultAssembler<?> assembler : assemblers[subclassId] ) {
assembler.resolveState( rowProcessingState );
}
}
}
}
private void resolveInstanceSubInitializers(int subclassId, Object instance, RowProcessingState rowProcessingState) {
final int fetchables = embedded.getNumberOfFetchables();
final int startIndex = subclassId * fetchables;
final int fetchableIndexNeedingResolve = subInitializersNeedingResolveIfParentInitialized.nextSetBit( startIndex );
if ( fetchableIndexNeedingResolve < 0 || fetchableIndexNeedingResolve >= startIndex + fetchables ) {
return;
}
final Initializer<InitializerData>[] subInitializer = subInitializers[subclassId];
for ( int i = 0; i < subInitializer.length; i++ ) {
if ( subInitializersNeedingResolveIfParentInitialized.get( startIndex + i ) ) {
final Initializer<?> initializer = subInitializer[i];
assert initializer != null;
final Object subInstance = embeddableMappingType.getValue( instance, i );
if ( subInstance == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
// Go through the normal initializer process
initializer.resolveKey( rowProcessingState );
@ -310,11 +348,6 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
initializer.resolveInstance( subInstance, rowProcessingState );
}
}
if ( rowProcessingState.needsResolveState() ) {
for ( DomainResultAssembler<?> assembler : assemblers[data.getSubclassId()] ) {
assembler.resolveState( rowProcessingState );
}
}
}
}
@ -333,10 +366,7 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
// 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(),
data.rowState
);
embeddableMappingType.setValues( lazyInitializer.getImplementation(), data.rowState );
}
else {
// At this point, createEmptyCompositesEnabled is always true, so we generate
@ -345,14 +375,14 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
// 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()
final Object target = embeddableMappingType.getRepresentationStrategy()
.getInstantiator()
.instantiate( data, sessionFactory );
lazyInitializer.setImplementation( target );
}
}
else {
embedded.getEmbeddableTypeDescriptor().setValues( data.getInstance(), data.rowState );
embeddableMappingType.setValues( data.getInstance(), data.rowState );
}
}
}
@ -364,13 +394,17 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
if ( embeddableInitializerData.concreteEmbeddableType == null ) {
for ( Initializer<?>[] initializers : subInitializers ) {
for ( Initializer<?> initializer : initializers ) {
consumer.accept( initializer, rowProcessingState );
if ( initializer != null ) {
consumer.accept( initializer, rowProcessingState );
}
}
}
}
else {
for ( Initializer<?> initializer : subInitializers[embeddableInitializerData.getSubclassId()] ) {
consumer.accept( initializer, rowProcessingState );
if ( initializer != null ) {
consumer.accept( initializer, rowProcessingState );
}
}
}
}
@ -387,8 +421,11 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
}
else {
data.setState( State.INITIALIZED );
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( Initializer<?> initializer : subInitializers[data.getSubclassId()] ) {
initializer.initializeInstanceFromParent( instance, data.getRowProcessingState() );
if ( initializer != null ) {
initializer.initializeInstanceFromParent( instance, rowProcessingState );
}
}
}
}
@ -397,7 +434,7 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
// 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 ) {
if ( parent != null && embedded instanceof VirtualModelPart && !isPartOfKey && data.getState() != State.MISSING ) {
final InitializerData subData = parent.getData( data.getRowProcessingState() );
parent.resolveInstance( subData );
data.setInstance( parent.getResolvedInstance( subData ) );
@ -414,17 +451,17 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
private void extractRowState(EmbeddableInitializerData data) {
boolean stateAllNull = true;
final DomainResultAssembler<?>[] subAssemblers = assemblers[data.getSubclassId()];
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final Object[] rowState = data.rowState;
for ( int i = 0; i < subAssemblers.length; i++ ) {
final DomainResultAssembler<?> assembler = subAssemblers[i];
final Object contributorValue = assembler == null ? null : assembler.assemble(
data.getRowProcessingState()
);
final Object contributorValue = assembler == null ? null : assembler.assemble( rowProcessingState );
if ( contributorValue == BATCH_PROPERTY ) {
data.rowState[i] = null;
rowState[i] = null;
}
else {
data.rowState[i] = contributorValue;
rowState[i] = contributorValue;
}
if ( contributorValue != null ) {
stateAllNull = false;
@ -441,8 +478,8 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
}
@Override
public void resolveState(RowProcessingState rowProcessingState) {
final EmbeddableInitializerData data = getData( rowProcessingState );
public void resolveState(EmbeddableInitializerData data) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( final DomainResultAssembler<?> assembler : assemblers[data.getSubclassId()] ) {
assembler.resolveState( rowProcessingState );
}
@ -458,7 +495,7 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
}
final EmbeddableInstantiator instantiator = data.concreteEmbeddableType == null
? embedded.getEmbeddableTypeDescriptor().getRepresentationStrategy().getInstantiator()
? embeddableMappingType.getRepresentationStrategy().getInstantiator()
: data.concreteEmbeddableType.getInstantiator();
final Object instance = instantiator.instantiate( data, sessionFactory );
data.setState( State.RESOLVED );

View File

@ -6,7 +6,7 @@
*/
package org.hibernate.sql.results.graph.embeddable.internal;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.function.BiConsumer;
import java.util.function.Function;
@ -18,7 +18,7 @@ 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.EmbeddableInstantiator;
import org.hibernate.metamodel.spi.ValueAccess;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
@ -49,23 +49,29 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
private final NavigablePath navigablePath;
private final NonAggregatedIdentifierMapping embedded;
private final EmbeddableMappingType virtualIdEmbeddable;
private final EmbeddableMappingType representationEmbeddable;
private final EmbeddableRepresentationStrategy representationStrategy;
private final EmbeddableInstantiator embeddableInstantiator;
private final @Nullable InitializerParent<?> parent;
private final SessionFactoryImplementor sessionFactory;
private final boolean isResultInitializer;
private final DomainResultAssembler<?>[] assemblers;
private final Initializer<InitializerData>[] initializers;
private final @Nullable Initializer<InitializerData>[] initializers;
private final BitSet subInitializersNeedingResolveIfParentInitialized;
private final boolean hasIdClass;
public static class NonAggregatedIdentifierMappingInitializerData extends InitializerData implements ValueAccess {
protected final boolean isFindByIdLookup;
protected final InitializerData parentData;
protected final Object[] virtualIdState;
protected final Object[] idClassState;
public NonAggregatedIdentifierMappingInitializerData(NonAggregatedIdentifierMappingInitializer initializer, RowProcessingState rowProcessingState) {
super( rowProcessingState );
this.isFindByIdLookup = !initializer.hasIdClass && rowProcessingState.getEntityId() != null
&& initializer.navigablePath.getParent().getParent() == null
&& initializer.navigablePath instanceof EntityIdentifierNavigablePath;
this.parentData = initializer.parent == null ? null : initializer.parent.getData( rowProcessingState );
final EmbeddableMappingType virtualIdEmbeddable = initializer.embedded.getEmbeddableTypeDescriptor();
final int size = virtualIdEmbeddable.getNumberOfFetchables();
@ -111,24 +117,31 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
this.parent = parent;
this.isResultInitializer = isResultInitializer;
final EmbeddableMappingType virtualIdEmbeddable = embedded.getEmbeddableTypeDescriptor();
this.virtualIdEmbeddable = embedded.getEmbeddableTypeDescriptor();
this.representationEmbeddable = embedded.getMappedIdEmbeddableTypeDescriptor();
this.representationStrategy = representationEmbeddable.getRepresentationStrategy();
this.embeddableInstantiator = representationEmbeddable.getRepresentationStrategy().getInstantiator();
this.hasIdClass = embedded.hasContainingClass() && virtualIdEmbeddable != representationEmbeddable;
this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
this.assemblers = createAssemblers( this, resultDescriptor, creationState, virtualIdEmbeddable, fetchConverter );
final ArrayList<Initializer<?>> initializers = new ArrayList<>( assemblers.length );
for ( DomainResultAssembler<?> assembler : assemblers ) {
final Initializer<?> initializer = assembler.getInitializer();
final Initializer<?>[] initializers = new Initializer[assemblers.length];
final BitSet subInitializersNeedingResolveIfParentInitialized = new BitSet(assemblers.length);
boolean empty = true;
for ( int i = 0; i < assemblers.length; i++ ) {
final Initializer<?> initializer = assemblers[i].getInitializer();
if ( initializer != null ) {
initializers.add( initializer );
if ( initializer.isEager() ) {
subInitializersNeedingResolveIfParentInitialized.set( i );
}
initializers[i] = initializer;
empty = false;
}
}
//noinspection unchecked
this.initializers = (Initializer<InitializerData>[]) (
initializers.isEmpty() ? Initializer.EMPTY_ARRAY : initializers.toArray( EMPTY_ARRAY )
empty ? Initializer.EMPTY_ARRAY : initializers
);
this.subInitializersNeedingResolveIfParentInitialized = subInitializersNeedingResolveIfParentInitialized;
}
protected static DomainResultAssembler<?>[] createAssemblers(
@ -172,12 +185,6 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
return isResultInitializer;
}
@Override
public Object getCompositeInstance(NonAggregatedIdentifierMappingInitializerData data) {
final State state = data.getState();
return state == State.RESOLVED || state == State.INITIALIZED ? data.getInstance() : null;
}
@Override
protected InitializerData createInitializerData(RowProcessingState rowProcessingState) {
return new NonAggregatedIdentifierMappingInitializerData( this, rowProcessingState );
@ -198,11 +205,13 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
else {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( Initializer<InitializerData> initializer : initializers ) {
final InitializerData subData = initializer.getData( rowProcessingState );
initializer.resolveKey( subData );
if ( subData.getState() == State.MISSING ) {
data.setState( State.MISSING );
return;
if ( initializer != null ) {
final InitializerData subData = initializer.getData( rowProcessingState );
initializer.resolveKey( subData );
if ( subData.getState() == State.MISSING ) {
data.setState( State.MISSING );
return;
}
}
}
}
@ -215,7 +224,7 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
}
// If we don't have an id class and this is a find by id lookup, we just use that instance
if ( isFindByIdLookup( data.getRowProcessingState() ) ) {
if ( data.isFindByIdLookup ) {
data.setInstance( data.getRowProcessingState().getEntityId() );
data.setState( State.INITIALIZED );
return;
@ -227,7 +236,7 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
data.setInstance( null );
}
else {
data.setInstance( representationStrategy.getInstantiator().instantiate( data, sessionFactory ) );
data.setInstance( embeddableInstantiator.instantiate( data, sessionFactory ) );
}
}
@ -241,10 +250,24 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
data.setState( State.INITIALIZED );
data.setInstance( instance );
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( Initializer<?> initializer : initializers ) {
final Object subInstance = initializer.getInitializedPart()
.asAttributeMapping()
.getValue( instance );
resolveInstanceSubInitializers( instance, rowProcessingState );
if ( rowProcessingState.needsResolveState() ) {
for ( DomainResultAssembler<?> assembler : assemblers ) {
assembler.resolveState( rowProcessingState );
}
}
}
}
private void resolveInstanceSubInitializers(Object instance, RowProcessingState rowProcessingState) {
if ( subInitializersNeedingResolveIfParentInitialized.nextSetBit( 0 ) < 0) {
return;
}
for ( int i = 0; i < initializers.length; i++ ) {
if ( subInitializersNeedingResolveIfParentInitialized.get( i ) ) {
final Initializer<InitializerData> initializer = initializers[i];
assert initializer != null;
final Object subInstance = virtualIdEmbeddable.getValue( instance, i );
if ( subInstance == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
// Go through the normal initializer process
initializer.resolveKey( rowProcessingState );
@ -253,11 +276,6 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
initializer.resolveInstance( subInstance, rowProcessingState );
}
}
if ( rowProcessingState.needsResolveState() ) {
for ( DomainResultAssembler<?> assembler : assemblers ) {
assembler.resolveState( rowProcessingState );
}
}
}
}
@ -276,13 +294,10 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
// 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(),
data.virtualIdState
);
virtualIdEmbeddable.setValues( lazyInitializer.getImplementation(), data.virtualIdState );
}
else {
embedded.getVirtualIdEmbeddable().setValues( parentInstance, data.virtualIdState );
virtualIdEmbeddable.setValues( parentInstance, data.virtualIdState );
}
}
}
@ -291,20 +306,16 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
protected void forEachSubInitializer(BiConsumer<Initializer<?>, RowProcessingState> consumer, InitializerData data) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( Initializer<?> initializer : initializers ) {
consumer.accept( initializer, rowProcessingState );
if ( initializer != null ) {
consumer.accept( initializer, rowProcessingState );
}
}
}
private boolean isFindByIdLookup(RowProcessingState rowProcessingState) {
return !hasIdClass && rowProcessingState.getEntityId() != null
&& navigablePath.getParent().getParent() == null
&& navigablePath instanceof EntityIdentifierNavigablePath;
}
private void extractRowState(NonAggregatedIdentifierMappingInitializerData data) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( int i = 0; i < assemblers.length; i++ ) {
final DomainResultAssembler<?> assembler = assemblers[i];
final Object contributorValue = assembler.assemble( data.getRowProcessingState() );
final Object contributorValue = assemblers[i].assemble( rowProcessingState );
if ( contributorValue == null ) {
// This is a key and there is a null part, the whole thing has to be turned into null
@ -319,7 +330,7 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
data.virtualIdState[i] = contributorValue;
data.idClassState[i] = contributorValue;
if ( hasIdClass ) {
final AttributeMapping virtualIdAttribute = embedded.getEmbeddableTypeDescriptor().getAttributeMapping( i );
final AttributeMapping virtualIdAttribute = virtualIdEmbeddable.getAttributeMapping( i );
final AttributeMapping mappedIdAttribute = representationEmbeddable.getAttributeMapping( i );
if ( virtualIdAttribute instanceof ToOneAttributeMapping
&& !( mappedIdAttribute instanceof ToOneAttributeMapping ) ) {
@ -328,7 +339,7 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
final Object associationKey = fkDescriptor.getAssociationKeyFromSide(
data.virtualIdState[i],
toOneAttributeMapping.getSideNature().inverse(),
data.getRowProcessingState().getSession()
rowProcessingState.getSession()
);
data.idClassState[i] = associationKey;
}
@ -338,8 +349,9 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
}
@Override
public void resolveState(RowProcessingState rowProcessingState) {
if ( !isFindByIdLookup( rowProcessingState ) ) {
public void resolveState(NonAggregatedIdentifierMappingInitializerData data) {
if ( !data.isFindByIdLookup ) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( final DomainResultAssembler<?> assembler : assemblers ) {
assembler.resolveState( rowProcessingState );
}
@ -351,10 +363,22 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
return true;
}
@Override
public boolean isEager() {
// Embeddables are never lazy
return true;
}
@Override
public boolean hasEagerSubInitializers() {
// Since embeddables are never lazy, we only need to check the components
return !subInitializersNeedingResolveIfParentInitialized.isEmpty();
}
/*
* Used by Hibernate Reactive
*/
protected Initializer<InitializerData>[] getInitializers() {
protected @Nullable Initializer<InitializerData>[] getInitializers() {
return initializers;
}

View File

@ -33,19 +33,14 @@ public interface EntityInitializer<Data extends InitializerData> extends Initial
}
/**
* Get the entity instance for the currently processing "row".
* Get the target entity instance for the currently processing "row".
*
* @apiNote Calling this method is only valid from the time
* {@link #resolveKey(InitializerData)} has been called until {@link #finishUpRow(InitializerData)}
* has been called for the currently processing row
*/
Object getEntityInstance(Data data);
default Object getEntityInstance(RowProcessingState rowProcessingState) {
return getEntityInstance( getData( rowProcessingState ) );
}
default Object getTargetInstance(Data data) {
return getEntityInstance( data );
return getResolvedInstance( data );
}
default Object getTargetInstance(RowProcessingState rowProcessingState) {
return getTargetInstance( getData( rowProcessingState ) );

View File

@ -38,14 +38,19 @@ public abstract class AbstractBatchEntitySelectFetchInitializer<Data extends Abs
protected final EntityInitializer<InitializerData> owningEntityInitializer;
protected boolean batchDisabled;
public static abstract class AbstractBatchEntitySelectFetchInitializerData extends EntitySelectFetchInitializerData {
final boolean batchDisabled;
// per-row state
protected @Nullable EntityKey entityKey;
public AbstractBatchEntitySelectFetchInitializerData(RowProcessingState rowProcessingState) {
super( rowProcessingState );
public AbstractBatchEntitySelectFetchInitializerData(
AbstractBatchEntitySelectFetchInitializer<?> initializer,
RowProcessingState rowProcessingState) {
super( initializer, rowProcessingState );
batchDisabled = rowProcessingState.isScrollResult()
|| !rowProcessingState.getLoadQueryInfluencers().effectivelyBatchLoadable( initializer.toOneMapping.getEntityMappingType().getEntityPersister() );
}
}
@ -65,15 +70,6 @@ public abstract class AbstractBatchEntitySelectFetchInitializer<Data extends Abs
protected abstract void registerResolutionListener(Data data);
@Override
public void startLoading(RowProcessingState rowProcessingState) {
batchDisabled = rowProcessingState.isScrollResult()
|| !rowProcessingState
.getLoadQueryInfluencers()
.effectivelyBatchLoadable( toOneMapping.getEntityMappingType().getEntityPersister() );
super.startLoading( rowProcessingState );
}
@Override
public void resolveKey(Data data) {
if ( data.getState() != State.UNINITIALIZED ) {
@ -104,10 +100,10 @@ public abstract class AbstractBatchEntitySelectFetchInitializer<Data extends Abs
}
data.setState( State.RESOLVED );
final RowProcessingState initializerRowProcessingState = data.getRowProcessingState();
final RowProcessingState rowProcessingState = data.getRowProcessingState();
if ( data.entityIdentifier == null ) {
// entityIdentifier can be null if the identifier is based on an initializer
data.entityIdentifier = keyAssembler.assemble( initializerRowProcessingState );
data.entityIdentifier = keyAssembler.assemble( rowProcessingState );
if ( data.entityIdentifier == null ) {
data.entityKey = null;
data.setInstance( null );
@ -115,7 +111,7 @@ public abstract class AbstractBatchEntitySelectFetchInitializer<Data extends Abs
return;
}
}
if ( batchDisabled ) {
if ( data.batchDisabled ) {
initialize( data );
}
else {
@ -138,22 +134,20 @@ public abstract class AbstractBatchEntitySelectFetchInitializer<Data extends Abs
return;
}
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final Initializer<?> initializer = keyAssembler.getInitializer();
// Only need to extract the identifier if the identifier has a many to one
final boolean hasKeyManyToOne = initializer != null;
final LazyInitializer lazyInitializer = extractLazyInitializer( instance );
data.entityKey = null;
if ( lazyInitializer == null ) {
// Entity is initialized
data.setState( State.INITIALIZED );
if ( hasKeyManyToOne ) {
if ( keyIsEager ) {
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, rowProcessingState.getSession() );
}
data.setInstance( instance );
}
else if ( lazyInitializer.isUninitialized() ) {
data.setState( State.RESOLVED );
if ( hasKeyManyToOne ) {
if ( keyIsEager ) {
data.entityIdentifier = lazyInitializer.getIdentifier();
}
// Resolve and potentially create the entity instance
@ -162,12 +156,14 @@ public abstract class AbstractBatchEntitySelectFetchInitializer<Data extends Abs
else {
// Entity is initialized
data.setState( State.INITIALIZED );
if ( hasKeyManyToOne ) {
if ( keyIsEager ) {
data.entityIdentifier = lazyInitializer.getIdentifier();
}
data.setInstance( lazyInitializer.getImplementation() );
}
if ( hasKeyManyToOne ) {
if ( keyIsEager ) {
final Initializer<?> initializer = keyAssembler.getInitializer();
assert initializer != null;
initializer.resolveInstance( data.entityIdentifier, rowProcessingState );
}
else if ( rowProcessingState.needsResolveState() ) {
@ -182,14 +178,14 @@ public abstract class AbstractBatchEntitySelectFetchInitializer<Data extends Abs
return;
}
data.setState( State.INITIALIZED );
if ( batchDisabled ) {
if ( data.batchDisabled ) {
Hibernate.initialize( data.getInstance() );
}
}
protected Object getExistingInitializedInstance(Data data) {
final SharedSessionContractImplementor session = data.getRowProcessingState().getSession();
final PersistenceContext persistenceContext = session.getPersistenceContext();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityHolder holder = persistenceContext.getEntityHolder( data.entityKey );
if ( holder != null && holder.getEntity() != null && holder.isEventuallyInitialized() ) {
return holder.getEntity();
@ -202,7 +198,7 @@ public abstract class AbstractBatchEntitySelectFetchInitializer<Data extends Abs
protected void registerToBatchFetchQueue(Data data) {
assert data.entityKey != null;
data.getRowProcessingState().getSession().getPersistenceContext()
data.getRowProcessingState().getSession().getPersistenceContextInternal()
.getBatchFetchQueue().addBatchLoadableEntityKey( data.entityKey );
}
@ -228,11 +224,6 @@ public abstract class AbstractBatchEntitySelectFetchInitializer<Data extends Abs
}
}
@Override
public Object getEntityInstance(Data data) {
return data.getState() == State.RESOLVED || data.getState() == State.INITIALIZED ? data.getInstance() : null;
}
protected static Object loadInstance(
EntityKey entityKey,
ToOneAttributeMapping toOneMapping,

View File

@ -15,6 +15,7 @@ import java.util.Map;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.log.LoggingHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
@ -54,10 +55,12 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
};
public static class BatchEntityInsideEmbeddableSelectFetchInitializerData extends AbstractBatchEntitySelectFetchInitializerData {
private Map<EntityKey, List<ParentInfo>> toBatchLoad;
private HashMap<EntityKey, List<ParentInfo>> toBatchLoad;
public BatchEntityInsideEmbeddableSelectFetchInitializerData(RowProcessingState rowProcessingState) {
super( rowProcessingState );
public BatchEntityInsideEmbeddableSelectFetchInitializerData(
BatchEntityInsideEmbeddableSelectFetchInitializer initializer,
RowProcessingState rowProcessingState) {
super( initializer, rowProcessingState );
}
}
@ -90,7 +93,7 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
@Override
protected InitializerData createInitializerData(RowProcessingState rowProcessingState) {
return new BatchEntityInsideEmbeddableSelectFetchInitializerData( rowProcessingState );
return new BatchEntityInsideEmbeddableSelectFetchInitializerData( this, rowProcessingState );
}
protected Type[] getParentEntityAttributeTypes(String attributeName) {
@ -129,10 +132,11 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
final int owningEntitySubclassId = owningEntityInitializer.getConcreteDescriptor( owningData ).getSubclassId();
final AttributeMapping rootEmbeddableAttribute = rootEmbeddableAttributes[owningEntitySubclassId];
if ( rootEmbeddableAttribute != null ) {
if ( data.toBatchLoad == null ) {
data.toBatchLoad = new HashMap<>();
HashMap<EntityKey, List<ParentInfo>> toBatchLoad = data.toBatchLoad;
if ( toBatchLoad == null ) {
toBatchLoad = data.toBatchLoad = new HashMap<>();
}
data.toBatchLoad.computeIfAbsent( data.entityKey, key -> new ArrayList<>() ).add(
toBatchLoad.computeIfAbsent( data.entityKey, key -> new ArrayList<>() ).add(
new ParentInfo(
owningEntityInitializer.getTargetInstance( owningData ),
parent.getResolvedInstance( rowProcessingState ),
@ -169,34 +173,38 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
@Override
public void endLoading(BatchEntityInsideEmbeddableSelectFetchInitializerData data) {
super.endLoading( data );
if ( data.toBatchLoad != null ) {
data.toBatchLoad.forEach(
(entityKey, parentInfos) -> {
final SharedSessionContractImplementor session = data.getRowProcessingState().getSession();
final Object loadedInstance = loadInstance( entityKey, toOneMapping, affectedByFilter, session );
for ( ParentInfo parentInfo : parentInfos ) {
final PersistenceContext persistenceContext = session.getPersistenceContext();
final EntityEntry parentEntityEntry = persistenceContext.getEntry( parentInfo.parentEntityInstance );
referencedModelPartSetter.set( parentInfo.parentInstance, loadedInstance );
final Object[] loadedState = parentEntityEntry.getLoadedState();
if ( loadedState != null ) {
/*
E.g.
final HashMap<EntityKey, List<ParentInfo>> toBatchLoad = data.toBatchLoad;
if ( toBatchLoad != null ) {
for ( Map.Entry<EntityKey, List<ParentInfo>> entry : toBatchLoad.entrySet() ) {
final EntityKey entityKey = entry.getKey();
final List<ParentInfo> parentInfos = entry.getValue();
final SharedSessionContractImplementor session = data.getRowProcessingState().getSession();
final SessionFactoryImplementor factory = session.getFactory();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final Object loadedInstance = loadInstance( entityKey, toOneMapping, affectedByFilter, session );
for ( ParentInfo parentInfo : parentInfos ) {
final Object parentEntityInstance = parentInfo.parentEntityInstance;
final EntityEntry parentEntityEntry = persistenceContext.getEntry( parentEntityInstance );
referencedModelPartSetter.set( parentInfo.parentInstance, loadedInstance );
final Object[] loadedState = parentEntityEntry.getLoadedState();
if ( loadedState != null ) {
/*
E.g.
ParentEntity -> RootEmbeddable -> ParentEmbeddable -> toOneAttributeMapping
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()
);
}
}
The value of RootEmbeddable is needed to update the ParentEntity loaded state
*/
final int parentEntitySubclassId = parentInfo.parentEntitySubclassId;
final Object rootEmbeddable = rootEmbeddableGetters[parentEntitySubclassId].get( parentEntityInstance );
loadedState[parentInfo.propertyIndex] = rootEmbeddablePropertyTypes[parentEntitySubclassId].deepCopy(
rootEmbeddable,
factory
);
}
);
data.toBatchLoad.clear();
}
}
data.toBatchLoad = null;
}
}

View File

@ -13,6 +13,7 @@ import java.util.Map;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.log.LoggingHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
@ -33,10 +34,12 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect
protected final Type referencedModelPartType;
public static class BatchEntitySelectFetchInitializerData extends AbstractBatchEntitySelectFetchInitializerData {
private Map<EntityKey, List<ParentInfo>> toBatchLoad;
private HashMap<EntityKey, List<ParentInfo>> toBatchLoad;
public BatchEntitySelectFetchInitializerData(RowProcessingState rowProcessingState) {
super( rowProcessingState );
public BatchEntitySelectFetchInitializerData(
BatchEntitySelectFetchInitializer initializer,
RowProcessingState rowProcessingState) {
super( initializer, rowProcessingState );
}
}
@ -57,7 +60,7 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect
@Override
protected InitializerData createInitializerData(RowProcessingState rowProcessingState) {
return new BatchEntitySelectFetchInitializerData( rowProcessingState );
return new BatchEntitySelectFetchInitializerData( this, rowProcessingState );
}
@Override
@ -67,10 +70,11 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect
final AttributeMapping parentAttribute;
if ( owningData.getState() != State.INITIALIZED
&& ( parentAttribute = parentAttributes[owningEntityInitializer.getConcreteDescriptor( owningData ).getSubclassId()] ) != null ) {
if ( data.toBatchLoad == null ) {
data.toBatchLoad = new HashMap<>();
HashMap<EntityKey, List<ParentInfo>> toBatchLoad = data.toBatchLoad;
if ( toBatchLoad == null ) {
toBatchLoad = data.toBatchLoad = new HashMap<>();
}
data.toBatchLoad.computeIfAbsent( data.entityKey, key -> new ArrayList<>() ).add(
toBatchLoad.computeIfAbsent( data.entityKey, key -> new ArrayList<>() ).add(
new ParentInfo(
owningEntityInitializer.getTargetInstance( owningData ),
parentAttribute.getStateArrayPosition()
@ -92,26 +96,28 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect
@Override
public void endLoading(BatchEntitySelectFetchInitializerData data) {
super.endLoading( data );
if ( data.toBatchLoad != null ) {
data.toBatchLoad.forEach(
(entityKey, parentInfos) -> {
final SharedSessionContractImplementor session = data.getRowProcessingState().getSession();
final Object instance = loadInstance( entityKey, toOneMapping, affectedByFilter, session );
for ( ParentInfo parentInfo : parentInfos ) {
final Object parentInstance = parentInfo.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()
);
}
}
final HashMap<EntityKey, List<ParentInfo>> toBatchLoad = data.toBatchLoad;
if ( toBatchLoad != null ) {
final SharedSessionContractImplementor session = data.getRowProcessingState().getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
for ( Map.Entry<EntityKey, List<ParentInfo>> entry : toBatchLoad.entrySet() ) {
final EntityKey entityKey = entry.getKey();
final List<ParentInfo> parentInfos = entry.getValue();
final Object instance = loadInstance( entityKey, toOneMapping, affectedByFilter, session );
for ( ParentInfo parentInfo : parentInfos ) {
final Object parentInstance = parentInfo.parentInstance;
final EntityEntry entityEntry = persistenceContext.getEntry( parentInstance );
referencedModelPartSetter.set( parentInstance, instance );
final Object[] loadedState = entityEntry.getLoadedState();
if ( loadedState != null ) {
loadedState[parentInfo.propertyIndex] = referencedModelPartType.deepCopy(
instance,
session.getFactory()
);
}
);
data.toBatchLoad.clear();
}
}
data.toBatchLoad = null;
}
}

View File

@ -7,7 +7,6 @@
package org.hibernate.sql.results.graph.entity.internal;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -28,10 +27,12 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
public class BatchInitializeEntitySelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer<BatchInitializeEntitySelectFetchInitializer.BatchInitializeEntitySelectFetchInitializerData> {
public static class BatchInitializeEntitySelectFetchInitializerData extends AbstractBatchEntitySelectFetchInitializerData {
private final Set<EntityKey> toBatchLoad = new HashSet<>();
private HashSet<EntityKey> toBatchLoad;
public BatchInitializeEntitySelectFetchInitializerData(RowProcessingState rowProcessingState) {
super( rowProcessingState );
public BatchInitializeEntitySelectFetchInitializerData(
BatchInitializeEntitySelectFetchInitializer initializer,
RowProcessingState rowProcessingState) {
super( initializer, rowProcessingState );
}
}
@ -48,7 +49,7 @@ public class BatchInitializeEntitySelectFetchInitializer extends AbstractBatchEn
@Override
protected InitializerData createInitializerData(RowProcessingState rowProcessingState) {
return new BatchInitializeEntitySelectFetchInitializerData( rowProcessingState );
return new BatchInitializeEntitySelectFetchInitializerData( this, rowProcessingState );
}
@Override
@ -66,17 +67,24 @@ public class BatchInitializeEntitySelectFetchInitializer extends AbstractBatchEn
false,
false
) );
data.toBatchLoad.add( data.entityKey );
HashSet<EntityKey> toBatchLoad = data.toBatchLoad;
if ( toBatchLoad == null ) {
toBatchLoad = data.toBatchLoad = new HashSet<>();
}
toBatchLoad.add( data.entityKey );
}
@Override
public void endLoading(BatchInitializeEntitySelectFetchInitializerData data) {
super.endLoading( data );
final SharedSessionContractImplementor session = data.getRowProcessingState().getSession();
for ( EntityKey key : data.toBatchLoad ) {
loadInstance( key, toOneMapping, affectedByFilter, session );
final HashSet<EntityKey> toBatchLoad = data.toBatchLoad;
if ( toBatchLoad != null ) {
final SharedSessionContractImplementor session = data.getRowProcessingState().getSession();
for ( EntityKey key : toBatchLoad ) {
loadInstance( key, toOneMapping, affectedByFilter, session );
}
data.toBatchLoad = null;
}
data.toBatchLoad.clear();
}
@Override

View File

@ -41,7 +41,6 @@ import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
public class DiscriminatedEntityInitializer
extends AbstractInitializer<DiscriminatedEntityInitializer.DiscriminatedEntityInitializerData>
implements EntityInitializer<DiscriminatedEntityInitializer.DiscriminatedEntityInitializerData> {
private static final String CONCRETE_NAME = DiscriminatedEntityInitializer.class.getSimpleName();
protected final InitializerParent<?> parent;
private final NavigablePath navigablePath;
@ -52,6 +51,7 @@ public class DiscriminatedEntityInitializer
private final DiscriminatedAssociationModelPart fetchedPart;
private final boolean eager;
private final boolean resultInitializer;
private final boolean keyIsEager;
public static class DiscriminatedEntityInitializerData extends InitializerData {
protected EntityPersister concreteDescriptor;
@ -80,6 +80,9 @@ public class DiscriminatedEntityInitializer
this.keyValueAssembler = keyFetch.createAssembler( this, creationState );
this.eager = eager;
this.resultInitializer = resultInitializer;
this.keyIsEager = keyValueAssembler != null
&& keyValueAssembler.getInitializer() != null
&& keyValueAssembler.getInitializer().isEager();
}
@Override
@ -109,7 +112,8 @@ public class DiscriminatedEntityInitializer
// resolve the key and the discriminator, and then use those to load the indicated entity
final Object discriminatorValue = discriminatorValueAssembler.assemble( data.getRowProcessingState() );
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState );
if ( discriminatorValue == null ) {
data.setState( State.MISSING );
@ -117,21 +121,27 @@ public class DiscriminatedEntityInitializer
data.entityIdentifier = null;
data.setInstance( null );
// null association
assert keyValueAssembler.assemble( data.getRowProcessingState() ) == null;
assert keyValueAssembler.assemble( rowProcessingState ) == null;
}
else {
data.setState( State.KEY_RESOLVED );
data.concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister();
data.entityIdentifier = keyValueAssembler.assemble( data.getRowProcessingState() );
data.entityIdentifier = keyValueAssembler.assemble( rowProcessingState );
}
}
@Override
public void resolveState(DiscriminatedEntityInitializerData data) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
discriminatorValueAssembler.resolveState( rowProcessingState );
keyValueAssembler.resolveState( rowProcessingState );
}
@Override
public void resolveFromPreviousRow(DiscriminatedEntityInitializerData data) {
if ( data.getState() == State.UNINITIALIZED ) {
if ( data.entityIdentifier == null ) {
if ( data.getInstance() == null ) {
data.setState( State.MISSING );
data.setInstance( null );
}
else {
final Initializer<?> initializer = keyValueAssembler.getInitializer();
@ -157,9 +167,10 @@ public class DiscriminatedEntityInitializer
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
if ( holder != null ) {
data.setInstance( holder.getEntity() );
final Object instance = holder.getEntity();
data.setInstance( instance );
if ( holder.getEntityInitializer() == null ) {
if ( data.getInstance() != null && Hibernate.isInitialized( data.getInstance() ) ) {
if ( instance != null && Hibernate.isInitialized( instance ) ) {
return;
}
}
@ -167,7 +178,7 @@ public class DiscriminatedEntityInitializer
// the entity is already being loaded elsewhere
return;
}
else if ( data.getInstance() == null ) {
else if ( instance == null ) {
// todo: maybe mark this as resolved instead?
assert holder.getProxy() == null : "How to handle this case?";
return;
@ -193,28 +204,35 @@ public class DiscriminatedEntityInitializer
}
else {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final LazyInitializer lazyInitializer = extractLazyInitializer( data.getInstance() );
final LazyInitializer lazyInitializer = extractLazyInitializer( instance );
if ( lazyInitializer == null ) {
data.setState( State.INITIALIZED );
data.concreteDescriptor = session.getEntityPersister( null, instance );
data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session );
if ( keyIsEager ) {
final SharedSessionContractImplementor session = rowProcessingState.getSession();
data.concreteDescriptor = session.getEntityPersister( null, instance );
data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session );
}
}
else if ( lazyInitializer.isUninitialized() ) {
data.setState( eager ? State.RESOLVED : State.INITIALIZED );
// Read the discriminator from the result set if necessary
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState );
data.concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister();
data.entityIdentifier = lazyInitializer.getIdentifier();
if ( keyIsEager ) {
// Read the discriminator from the result set if necessary
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState );
data.concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister();
data.entityIdentifier = lazyInitializer.getIdentifier();
}
}
else {
data.setState( State.INITIALIZED );
data.concreteDescriptor = session.getEntityPersister( null, lazyInitializer.getImplementation() );
data.entityIdentifier = lazyInitializer.getIdentifier();
if ( keyIsEager ) {
data.concreteDescriptor = rowProcessingState.getSession().getEntityPersister( null, lazyInitializer.getImplementation() );
data.entityIdentifier = lazyInitializer.getIdentifier();
}
}
data.setInstance( instance );
final Initializer<?> initializer = keyValueAssembler.getInitializer();
if ( initializer != null ) {
if ( keyIsEager ) {
final Initializer<?> initializer = keyValueAssembler.getInitializer();
assert initializer != null;
initializer.resolveInstance( data.entityIdentifier, rowProcessingState );
}
else if ( rowProcessingState.needsResolveState() ) {
@ -277,11 +295,6 @@ public class DiscriminatedEntityInitializer
throw new UnsupportedOperationException("Discriminated association has no static entity type");
}
@Override
public Object getEntityInstance(DiscriminatedEntityInitializerData data) {
return data.getInstance();
}
@Override
public EntityPersister getConcreteDescriptor(DiscriminatedEntityInitializerData data) {
return data.concreteDescriptor;
@ -297,6 +310,16 @@ public class DiscriminatedEntityInitializer
return isPartOfKey;
}
@Override
public boolean isEager() {
return eager || keyIsEager;
}
@Override
public boolean hasEagerSubInitializers() {
return keyIsEager;
}
@Override
public boolean isResultInitializer() {
return resultInitializer;

View File

@ -9,6 +9,7 @@ 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.Initializer;
import org.hibernate.sql.results.graph.InitializerData;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
@ -37,8 +38,16 @@ public class EntityAssembler implements DomainResultAssembler {
// 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
final InitializerData data = initializer.getData( rowProcessingState );
initializer.resolveInstance( data );
return initializer.getEntityInstance( data );
final Initializer.State state = data.getState();
if ( state == Initializer.State.KEY_RESOLVED ) {
initializer.resolveInstance( data );
}
return initializer.getResolvedInstance( data );
}
@Override
public void resolveState(RowProcessingState rowProcessingState) {
initializer.resolveState( rowProcessingState );
}
@Override

View File

@ -54,6 +54,7 @@ public class EntityDelayedFetchInitializer
private final boolean selectByUniqueKey;
private final DomainResultAssembler<?> identifierAssembler;
private final @Nullable BasicResultAssembler<?> discriminatorAssembler;
private final boolean keyIsEager;
public static class EntityDelayedFetchInitializerData extends InitializerData {
// per-row state
@ -85,6 +86,9 @@ public class EntityDelayedFetchInitializer
this.discriminatorAssembler = discriminatorResult == null
? null
: (BasicResultAssembler<?>) discriminatorResult.createResultAssembler( this, creationState );
this.keyIsEager = identifierAssembler != null
&& identifierAssembler.getInitializer() != null
&& identifierAssembler.getInitializer().isEager();
}
@Override
@ -105,16 +109,15 @@ public class EntityDelayedFetchInitializer
@Override
public void resolveFromPreviousRow(EntityDelayedFetchInitializerData data) {
if ( data.getState() == State.UNINITIALIZED ) {
if ( data.entityIdentifier == null ) {
if ( data.getInstance() == null ) {
data.setState( State.MISSING );
data.setInstance( null );
}
else {
final Initializer<?> initializer = identifierAssembler.getInitializer();
if ( initializer != null ) {
initializer.resolveFromPreviousRow( data.getRowProcessingState() );
}
data.setState( State.RESOLVED );
data.setState( State.INITIALIZED );
}
}
}
@ -125,7 +128,8 @@ public class EntityDelayedFetchInitializer
return;
}
data.setState( State.RESOLVED );
// This initializer is done initializing, since this is only invoked for delayed or select initializers
data.setState( State.INITIALIZED );
final RowProcessingState rowProcessingState = data.getRowProcessingState();
data.entityIdentifier = identifierAssembler.assemble( rowProcessingState );
@ -137,7 +141,7 @@ public class EntityDelayedFetchInitializer
else {
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final EntityPersister entityPersister = referencedModelPart.getEntityMappingType().getEntityPersister();
final EntityPersister entityPersister = getEntityDescriptor();
final EntityPersister concreteDescriptor;
if ( discriminatorAssembler != null ) {
concreteDescriptor = determineConcreteEntityDescriptor(
@ -177,55 +181,58 @@ public class EntityDelayedFetchInitializer
uniqueKeyPropertyType,
session.getFactory()
);
data.setInstance( persistenceContext.getEntity( euk ) );
if ( data.getInstance() == null ) {
Object instance = persistenceContext.getEntity( euk );
if ( instance == null ) {
// For unique-key mappings, we always use bytecode-laziness if possible,
// because we can't generate a proxy based on the unique key yet
if ( referencedModelPart.isLazy() ) {
data.setInstance( LazyPropertyInitializer.UNFETCHED_PROPERTY );
instance = LazyPropertyInitializer.UNFETCHED_PROPERTY;
}
else {
data.setInstance( concreteDescriptor.loadByUniqueKey(
instance = concreteDescriptor.loadByUniqueKey(
uniqueKeyPropertyName,
data.entityIdentifier,
session
) );
);
// If the entity was not in the Persistence Context, but was found now,
// add it to the Persistence Context
if ( data.getInstance() != null ) {
persistenceContext.addEntity( euk, data.getInstance() );
if ( instance != null ) {
persistenceContext.addEntity( euk, instance );
}
}
}
if ( data.getInstance() != null ) {
data.setInstance( persistenceContext.proxyFor( data.getInstance() ) );
if ( instance != null ) {
instance = persistenceContext.proxyFor( instance );
}
data.setInstance( instance );
}
else {
final EntityKey entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor );
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
final Object instance;
if ( holder != null && holder.getEntity() != null ) {
data.setInstance( persistenceContext.proxyFor( holder, concreteDescriptor ) );
instance = persistenceContext.proxyFor( holder, concreteDescriptor );
}
// For primary key based mappings we only use bytecode-laziness if the attribute is optional,
// because the non-optionality implies that it is safe to have a proxy
else if ( referencedModelPart.isOptional() && referencedModelPart.isLazy() ) {
data.setInstance( LazyPropertyInitializer.UNFETCHED_PROPERTY );
instance = LazyPropertyInitializer.UNFETCHED_PROPERTY;
}
else {
data.setInstance( session.internalLoad(
instance = session.internalLoad(
concreteDescriptor.getEntityName(),
data.entityIdentifier,
false,
false
) );
);
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( data.getInstance() );
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( instance );
if ( lazyInitializer != null ) {
lazyInitializer.setUnwrap( referencedModelPart.isUnwrapProxy() && concreteDescriptor.isInstrumented() );
}
}
data.setInstance( instance );
}
}
}
@ -238,14 +245,14 @@ public class EntityDelayedFetchInitializer
data.setInstance( null );
}
else {
data.setState( State.RESOLVED );
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final EntityPersister concreteDescriptor = referencedModelPart.getEntityMappingType().getEntityPersister();
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
// This initializer is done initializing, since this is only invoked for delayed or select initializers
data.setState( State.INITIALIZED );
data.setInstance( instance );
final Initializer<?> initializer = identifierAssembler.getInitializer();
if ( initializer != null ) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
if ( keyIsEager ) {
data.entityIdentifier = getEntityDescriptor().getIdentifier( instance, rowProcessingState.getSession() );
final Initializer<?> initializer = identifierAssembler.getInitializer();
assert initializer != null;
initializer.resolveInstance( data.entityIdentifier, rowProcessingState );
}
else if ( rowProcessingState.needsResolveState() ) {
@ -268,11 +275,6 @@ public class EntityDelayedFetchInitializer
return referencedModelPart.getEntityMappingType().getEntityPersister();
}
@Override
public Object getEntityInstance(EntityDelayedFetchInitializerData data) {
return data.getInstance();
}
@Override
public @Nullable InitializerParent<?> getParent() {
return parent;
@ -283,6 +285,16 @@ public class EntityDelayedFetchInitializer
return isPartOfKey;
}
@Override
public boolean isEager() {
return keyIsEager;
}
@Override
public boolean hasEagerSubInitializers() {
return keyIsEager;
}
@Override
public boolean isResultInitializer() {
return false;
@ -293,6 +305,15 @@ public class EntityDelayedFetchInitializer
return getEntityDescriptor();
}
@Override
public void resolveState(EntityDelayedFetchInitializerData data) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
identifierAssembler.resolveState( rowProcessingState );
if ( discriminatorAssembler != null ) {
discriminatorAssembler.resolveState( rowProcessingState );
}
}
@Override
public @Nullable Object getEntityIdentifier(EntityDelayedFetchInitializerData data) {
return data.entityIdentifier;

View File

@ -6,7 +6,10 @@
*/
package org.hibernate.sql.results.graph.entity.internal;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import org.hibernate.EntityFilterException;
@ -36,6 +39,7 @@ import org.hibernate.event.spi.HibernateMonitoringEvent;
import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.event.spi.PreLoadEventListener;
import org.hibernate.internal.log.LoggingHelper;
import org.hibernate.internal.util.ImmutableBitSet;
import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMetadata;
@ -45,7 +49,6 @@ import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister;
@ -100,6 +103,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
private final EntityValuedModelPart referencedModelPart;
private final EntityPersister entityDescriptor;
private final EntityPersister rootEntityDescriptor;
private final @Nullable Type keyTypeForEqualsHashCode;
private final NavigablePath navigablePath;
private final String sourceAlias;
private final @Nullable InitializerParent<?> parent;
@ -116,6 +120,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
* so that these initializers can avoid unnecessary processing as well.
*/
private final boolean previousRowReuse;
private final boolean couldUseEmbeddedIdentifierInstanceAsEntity;
private final @Nullable DomainResultAssembler<?> keyAssembler;
private final @Nullable DomainResultAssembler<?> identifierAssembler;
@ -125,16 +130,21 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
private final DomainResultAssembler<?>[][] assemblers;
private final Initializer<?>[][] subInitializers;
private final Initializer<?>[][] subInitializersForResolveFromInitialized;
private final MutabilityPlan<Object>[][] updatableAttributeMutabilityPlans;
private final ImmutableBitSet[] lazySubInitializers;
private final ImmutableBitSet[] maybeLazySets;
private final boolean hasEagerSubInitializers;
public static class EntityInitializerData extends InitializerData {
protected boolean shallowCached;
protected LockMode lockMode;
protected String uniqueKeyAttributePath;
protected Type[] uniqueKeyPropertyTypes;
protected boolean canUseEmbeddedIdentifierInstanceAsEntity;
protected boolean hasCallbackActions;
protected final boolean shallowCached;
protected final LockMode lockMode;
protected final String uniqueKeyAttributePath;
protected final Type[] uniqueKeyPropertyTypes;
protected final boolean canUseEmbeddedIdentifierInstanceAsEntity;
protected final boolean hasCallbackActions;
protected final @Nullable EntityPersister defaultConcreteDescriptor;
// per-row state
protected @Nullable EntityPersister concreteDescriptor;
@ -142,8 +152,35 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
protected @Nullable Object entityInstanceForNotify;
protected @Nullable EntityHolder entityHolder;
public EntityInitializerData(RowProcessingState rowProcessingState) {
public EntityInitializerData(EntityInitializerImpl initializer, RowProcessingState rowProcessingState) {
super( rowProcessingState );
final EntityPersister entityDescriptor = initializer.entityDescriptor;
shallowCached = rowProcessingState.isQueryCacheHit() && entityDescriptor.useShallowQueryCacheLayout();
lockMode = rowProcessingState.determineEffectiveLockMode( initializer.sourceAlias );
if ( initializer.isResultInitializer() ) {
uniqueKeyAttributePath = rowProcessingState.getEntityUniqueKeyAttributePath();
if ( uniqueKeyAttributePath != null ) {
uniqueKeyPropertyTypes = initializer.getParentEntityAttributeTypes( uniqueKeyAttributePath );
}
else {
uniqueKeyPropertyTypes = null;
}
canUseEmbeddedIdentifierInstanceAsEntity = rowProcessingState.getEntityId() != null
&& initializer.couldUseEmbeddedIdentifierInstanceAsEntity;
}
else {
uniqueKeyAttributePath = null;
uniqueKeyPropertyTypes = null;
canUseEmbeddedIdentifierInstanceAsEntity = false;
}
hasCallbackActions = rowProcessingState.hasCallbackActions();
if ( initializer.discriminatorAssembler == null
|| rowProcessingState.isQueryCacheHit() && entityDescriptor.useShallowQueryCacheLayout() && !entityDescriptor.storeDiscriminatorInShallowQueryCacheLayout() ) {
defaultConcreteDescriptor = entityDescriptor;
}
else {
defaultConcreteDescriptor = null;
}
}
/*
@ -157,6 +194,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
this.uniqueKeyPropertyTypes = original.uniqueKeyPropertyTypes;
this.canUseEmbeddedIdentifierInstanceAsEntity = original.canUseEmbeddedIdentifierInstanceAsEntity;
this.hasCallbackActions = original.hasCallbackActions;
this.defaultConcreteDescriptor = original.defaultConcreteDescriptor;
this.concreteDescriptor = original.concreteDescriptor;
this.entityKey = original.entityKey;
this.entityInstanceForNotify = original.entityInstanceForNotify;
@ -185,6 +223,10 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
rootEntityDescriptor = rootEntityName == null || rootEntityName.equals( entityDescriptor.getEntityName() )
? entityDescriptor
: entityDescriptor.getRootEntityDescriptor().getEntityPersister();
keyTypeForEqualsHashCode = entityDescriptor.getIdentifierType().getTypeForEqualsHashCode();
// The id can only be the entity instance if this is a non-aggregated id that has no containing class
couldUseEmbeddedIdentifierInstanceAsEntity = entityDescriptor.getIdentifierMapping() instanceof CompositeIdentifierMapping
&& !( (CompositeIdentifierMapping) entityDescriptor.getIdentifierMapping() ).hasContainingClass();
this.navigablePath = resultDescriptor.getNavigablePath();
this.sourceAlias = sourceAlias;
@ -237,6 +279,9 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
final Collection<EntityMappingType> subMappingTypes = rootEntityDescriptor.getSubMappingTypes();
final DomainResultAssembler<?>[][] assemblers = new DomainResultAssembler[subMappingTypes.size() + 1][];
final Initializer<?>[][] subInitializers = new Initializer<?>[subMappingTypes.size() + 1][];
final Initializer<?>[][] eagerSubInitializers = new Initializer<?>[subMappingTypes.size() + 1][];
final BitSet[] lazySubInitializers = new BitSet[subMappingTypes.size() + 1];
final BitSet[] maybeLazySets = new BitSet[subMappingTypes.size() + 1];
final MutabilityPlan[][] updatableAttributeMutabilityPlans = new MutabilityPlan[subMappingTypes.size() + 1][];
assemblers[rootEntityDescriptor.getSubclassId()] = new DomainResultAssembler[rootEntityDescriptor.getNumberOfFetchables()];
updatableAttributeMutabilityPlans[rootEntityDescriptor.getSubclassId()] = new MutabilityPlan[rootEntityDescriptor.getNumberOfAttributeMappings()];
@ -246,6 +291,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
updatableAttributeMutabilityPlans[subMappingType.getSubclassId()] = new MutabilityPlan[subMappingType.getNumberOfAttributeMappings()];
}
boolean hasEagerSubInitializers = false;
final int size = entityDescriptor.getNumberOfFetchables();
for ( int i = 0; i < size; i++ ) {
final AttributeMapping attributeMapping = entityDescriptor.getFetchable( i ).asAttributeMapping();
@ -262,8 +308,22 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
if ( subInitializer != null ) {
if ( subInitializers[subclassId] == null ) {
subInitializers[subclassId] = new Initializer<?>[size];
eagerSubInitializers[subclassId] = new Initializer<?>[size];
lazySubInitializers[subclassId] = new BitSet(size);
maybeLazySets[subclassId] = new BitSet(size);
}
subInitializers[subclassId][stateArrayPosition] = subInitializer;
if ( subInitializer.isEager() ) {
hasEagerSubInitializers = true;
eagerSubInitializers[subclassId][stateArrayPosition] = subInitializer;
if ( subInitializer.hasEagerSubInitializers() ) {
maybeLazySets[subclassId].set( stateArrayPosition );
}
}
else {
lazySubInitializers[subclassId].set( stateArrayPosition );
maybeLazySets[subclassId].set( stateArrayPosition );
}
}
assemblers[subclassId][stateArrayPosition] = stateAssembler;
@ -277,11 +337,26 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
if ( subInitializer != null ) {
if ( subInitializers[subMappingType.getSubclassId()] == null ) {
subInitializers[subMappingType.getSubclassId()] = new Initializer<?>[size];
eagerSubInitializers[subMappingType.getSubclassId()] = new Initializer<?>[size];
lazySubInitializers[subMappingType.getSubclassId()] = new BitSet(size);
maybeLazySets[subMappingType.getSubclassId()] = new BitSet(size);
}
subInitializers[subMappingType.getSubclassId()][stateArrayPosition] = subInitializer;
if ( subInitializer.isEager() ) {
hasEagerSubInitializers = true;
eagerSubInitializers[subMappingType.getSubclassId()][stateArrayPosition] = subInitializer;
if ( subInitializer.hasEagerSubInitializers() ) {
maybeLazySets[subMappingType.getSubclassId()].set( stateArrayPosition );
}
}
else {
lazySubInitializers[subMappingType.getSubclassId()].set( stateArrayPosition );
maybeLazySets[subMappingType.getSubclassId()].set( stateArrayPosition );
}
}
}
}
final BitSet emptyBitSet = new BitSet();
OUTER: for ( int i = 0; i < subInitializers.length; i++ ) {
if ( subInitializers[i] != null ) {
for ( Initializer<?> initializer : subInitializers[i] ) {
@ -291,10 +366,28 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
}
subInitializers[i] = Initializer.EMPTY_ARRAY;
lazySubInitializers[i] = emptyBitSet;
maybeLazySets[i] = emptyBitSet;
}
OUTER: for ( int i = 0; i < eagerSubInitializers.length; i++ ) {
if ( eagerSubInitializers[i] != null ) {
for ( Initializer<?> initializer : eagerSubInitializers[i] ) {
if ( initializer != null ) {
continue OUTER;
}
}
}
eagerSubInitializers[i] = Initializer.EMPTY_ARRAY;
}
this.assemblers = assemblers;
this.subInitializers = subInitializers;
this.subInitializersForResolveFromInitialized = rootEntityDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading()
? subInitializers
: eagerSubInitializers;
this.lazySubInitializers = Arrays.stream(lazySubInitializers).map( ImmutableBitSet::valueOf ).toArray(ImmutableBitSet[]::new);
this.maybeLazySets = Arrays.stream(maybeLazySets).map( ImmutableBitSet::valueOf ).toArray(ImmutableBitSet[]::new);
this.hasEagerSubInitializers = hasEagerSubInitializers;
this.updatableAttributeMutabilityPlans = updatableAttributeMutabilityPlans;
this.notFoundAction = notFoundAction;
@ -319,7 +412,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
@Override
protected EntityInitializerData createInitializerData(RowProcessingState rowProcessingState) {
return new EntityInitializerData( rowProcessingState );
return new EntityInitializerData( this, rowProcessingState );
}
@Override
@ -365,7 +458,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
final Object oldEntityInstanceForNotify = data.entityInstanceForNotify;
final EntityHolder oldEntityHolder = data.entityHolder;
// reset row state
data.concreteDescriptor = null;
final EntityPersister concreteDescriptor = data.concreteDescriptor = data.defaultConcreteDescriptor;
data.entityKey = null;
data.setInstance( null );
data.entityInstanceForNotify = null;
@ -388,12 +481,14 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
return;
}
else {
data.concreteDescriptor = determineConcreteEntityDescriptor(
rowProcessingState,
discriminatorAssembler,
entityDescriptor
);
assert data.concreteDescriptor != null;
if ( concreteDescriptor == null ) {
data.concreteDescriptor = determineConcreteEntityDescriptor(
rowProcessingState,
discriminatorAssembler,
entityDescriptor
);
assert data.concreteDescriptor != null;
}
if ( hasKeyManyToOne ) {
if ( !data.shallowCached && !entityKeyOnly ) {
resolveKeySubInitializers( data );
@ -410,7 +505,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
if ( oldEntityKey != null && previousRowReuse && oldEntityInstance != null
&& entityDescriptor.getIdentifierType().isEqual( oldEntityKey.getIdentifier(), id ) ) {
&& areKeysEqual( oldEntityKey.getIdentifier(), id ) ) {
// The row we read previously referred to this entity already, so we can safely assume it's initialized.
// Unfortunately we can't set the state to INITIALIZED though, as that has other implications,
// but RESOLVED is fine, since the EntityEntry is marked as initialized which skips instance initialization
@ -450,12 +545,23 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
}
private boolean areKeysEqual(Object key1, Object key2) {
return keyTypeForEqualsHashCode == null ? key1.equals( key2 ) : keyTypeForEqualsHashCode.isEqual( key1, key2 );
}
protected void resolveInstanceSubInitializers(EntityInitializerData data) {
final Initializer<?>[] initializers = subInitializers[data.concreteDescriptor.getSubclassId()];
if ( initializers.length == 0 ) {
final int subclassId = data.concreteDescriptor.getSubclassId();
final Initializer<?>[] initializers = subInitializersForResolveFromInitialized[subclassId];
final EntityEntry entityEntry;
final ImmutableBitSet maybeLazySet;
// Skip resolving if this initializer has no sub-initializers
if ( initializers.length == 0
// or the entity entry has a lazy set available
|| ( maybeLazySet = ( entityEntry = data.entityHolder.getEntityEntry() ).getMaybeLazySet() ) != null
// which is contained in the lazy sub-initializers
&& lazySubInitializers[subclassId].contains( maybeLazySet ) ) {
return;
}
final EntityEntry entityEntry = data.entityHolder.getEntityEntry();
final RowProcessingState rowProcessingState = data.getRowProcessingState();
assert entityEntry == rowProcessingState.getSession()
.getPersistenceContextInternal()
@ -479,7 +585,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
for ( int i = 0; i < initializers.length; i++ ) {
final Initializer<?> initializer = initializers[i];
if ( initializer != null ) {
if ( initializer != null && ( maybeLazySet == null || maybeLazySet.get( i ) ) ) {
final Object subInstance = state[i];
if ( subInstance == UNFETCHED_PROPERTY ) {
// Go through the normal initializer process
@ -510,17 +616,18 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
}
@EnsuresNonNull( "entityKey" )
@EnsuresNonNull( "data.entityKey" )
protected void resolveEntityKey(EntityInitializerData data, Object id) {
if ( data.concreteDescriptor == null ) {
data.concreteDescriptor = determineConcreteEntityDescriptor(
EntityPersister concreteDescriptor = data.concreteDescriptor;
if ( concreteDescriptor == null ) {
concreteDescriptor = data.concreteDescriptor = determineConcreteEntityDescriptor(
data.getRowProcessingState(),
discriminatorAssembler,
entityDescriptor
);
assert data.concreteDescriptor != null;
assert concreteDescriptor != null;
}
data.entityKey = new EntityKey( id, data.concreteDescriptor );
data.entityKey = new EntityKey( id, concreteDescriptor );
}
protected void setMissing(EntityInitializerData data) {
@ -581,11 +688,11 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
else {
data.setInstance( instance );
data.entityInstanceForNotify = Hibernate.unproxy( instance );
data.concreteDescriptor = session.getEntityPersister( null, data.entityInstanceForNotify );
final Object entityInstanceForNotify = data.entityInstanceForNotify = Hibernate.unproxy( instance );
data.concreteDescriptor = session.getEntityPersister( null, entityInstanceForNotify );
resolveEntityKey(
data,
data.concreteDescriptor.getIdentifier( data.entityInstanceForNotify, session )
data.concreteDescriptor.getIdentifier( entityInstanceForNotify, session )
);
data.entityHolder = session.getPersistenceContextInternal().getEntityHolder( data.entityKey );
data.setState( State.INITIALIZED );
@ -593,10 +700,6 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
}
protected String getSimpleConcreteImplName() {
return "EntityInitializerImpl";
}
@Override
public boolean isResultInitializer() {
return isResultInitializer;
@ -633,11 +736,6 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
return entityDescriptor;
}
@Override
public Object getEntityInstance(EntityInitializerData data) {
return data.getInstance();
}
@Override
public Object getTargetInstance(EntityInitializerData data) {
return data.entityInstanceForNotify;
@ -648,47 +746,23 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
return parent;
}
@Override
public void startLoading(RowProcessingState rowProcessingState) {
final EntityInitializerData data = createInitializerData( rowProcessingState );
rowProcessingState.setInitializerData( initializerId, data );
if ( rowProcessingState.isQueryCacheHit() && entityDescriptor.useShallowQueryCacheLayout() ) {
data.shallowCached = true;
}
data.lockMode = rowProcessingState.determineEffectiveLockMode( sourceAlias );
if ( isResultInitializer() ) {
data.uniqueKeyAttributePath = rowProcessingState.getEntityUniqueKeyAttributePath();
if ( data.uniqueKeyAttributePath != null ) {
data.uniqueKeyPropertyTypes = getParentEntityAttributeTypes( data.uniqueKeyAttributePath );
}
else {
data.uniqueKeyPropertyTypes = null;
}
data.canUseEmbeddedIdentifierInstanceAsEntity = data.getRowProcessingState().getEntityId() != null
// The id can only be the entity instance if this is a non-aggregated id that has no containing class
&& entityDescriptor.getIdentifierMapping() instanceof CompositeIdentifierMapping
&& !( (CompositeIdentifierMapping) entityDescriptor.getIdentifierMapping() ).hasContainingClass();
}
else {
data.uniqueKeyAttributePath = null;
data.uniqueKeyPropertyTypes = null;
data.canUseEmbeddedIdentifierInstanceAsEntity = false;
}
data.hasCallbackActions = rowProcessingState.hasCallbackActions();
forEachSubInitializer( Initializer::startLoading, data );
}
private final ConcurrentHashMap<String, Type[]> parentEntityAttributeTypes = new ConcurrentHashMap<>();
protected Type[] getParentEntityAttributeTypes(String attributeName) {
final Type[] attributeTypes = new Type[
entityDescriptor.getRootEntityDescriptor()
.getSubclassEntityNames()
.size()
];
initializeAttributeType( attributeTypes, entityDescriptor, attributeName );
for ( EntityMappingType subMappingType : entityDescriptor.getSubMappingTypes() ) {
initializeAttributeType( attributeTypes, subMappingType.getEntityPersister(), attributeName );
Type[] types = parentEntityAttributeTypes.get( attributeName );
if ( types == null ) {
types = new Type[
entityDescriptor.getRootEntityDescriptor()
.getSubclassEntityNames()
.size()
];
initializeAttributeType( types, entityDescriptor, attributeName );
for ( EntityMappingType subMappingType : entityDescriptor.getSubMappingTypes() ) {
initializeAttributeType( types, subMappingType.getEntityPersister(), attributeName );
}
parentEntityAttributeTypes.putIfAbsent( attributeName, types );
}
return attributeTypes;
return types;
}
protected void initializeAttributeType(Type[] attributeTypes, EntityPersister entityDescriptor, String attributeName) {
@ -699,7 +773,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
public static @Nullable EntityPersister determineConcreteEntityDescriptor(
RowProcessingState rowProcessingState,
BasicResultAssembler<?> discriminatorAssembler,
@Nullable BasicResultAssembler<?> discriminatorAssembler,
EntityPersister entityDescriptor)
throws WrongClassException {
if ( discriminatorAssembler == null
@ -747,18 +821,19 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
return;
}
data.setInstance( instance );
final LazyInitializer lazyInitializer = extractLazyInitializer( data.getInstance() );
final LazyInitializer lazyInitializer = extractLazyInitializer( instance );
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
if ( lazyInitializer == null ) {
// Entity is most probably initialized
data.entityInstanceForNotify = data.getInstance();
data.concreteDescriptor = session.getEntityPersister( null, data.getInstance() );
data.entityInstanceForNotify = instance;
data.concreteDescriptor = session.getEntityPersister( null, instance );
resolveEntityKey(
data,
data.concreteDescriptor.getIdentifier( data.getInstance(), session )
data.concreteDescriptor.getIdentifier( instance, session )
);
data.entityHolder = session.getPersistenceContextInternal().getEntityHolder( data.entityKey );
data.entityHolder = persistenceContext.getEntityHolder( data.entityKey );
if ( data.entityHolder == null ) {
// Entity was most probably removed in the same session without setting the reference to null
resolveKey( data );
@ -779,7 +854,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
: determineConcreteEntityDescriptor( rowProcessingState, discriminatorAssembler, entityDescriptor );
assert data.concreteDescriptor != null;
resolveEntityKey( data, lazyInitializer.getIdentifier() );
data.entityHolder = session.getPersistenceContextInternal().claimEntityHolderIfPossible(
data.entityHolder = persistenceContext.claimEntityHolderIfPossible(
data.entityKey,
null,
rowProcessingState.getJdbcValuesSourceProcessingState(),
@ -795,7 +870,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
data.entityInstanceForNotify = lazyInitializer.getImplementation();
data.concreteDescriptor = session.getEntityPersister( null, data.entityInstanceForNotify );
resolveEntityKey( data, lazyInitializer.getIdentifier() );
data.entityHolder = session.getPersistenceContextInternal().getEntityHolder( data.entityKey );
data.entityHolder = persistenceContext.getEntityHolder( data.entityKey );
}
if ( identifierAssembler != null ) {
final Initializer<?> initializer = identifierAssembler.getInitializer();
@ -809,7 +884,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
resolveInstanceSubInitializers( data );
if ( rowProcessingState.needsResolveState() ) {
// We need to read result set values to correctly populate the query cache
resolveState( data );
resolveEntityState( data );
}
}
else {
@ -857,7 +932,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
data.uniqueKeyPropertyTypes[concreteDescriptor.getSubclassId()],
session.getFactory()
);
session.getPersistenceContextInternal().addEntity( euk, getEntityInstance( data ) );
session.getPersistenceContextInternal().addEntity( euk, data.getInstance() );
}
}
@ -867,7 +942,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
registerReloadedEntity( data );
if ( rowProcessingState.needsResolveState() ) {
// We need to read result set values to correctly populate the query cache
resolveState( data );
resolveEntityState( data );
}
}
if ( data.shallowCached ) {
@ -891,12 +966,12 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
else {
data.setInstance( proxy );
if ( Hibernate.isInitialized( data.getInstance() ) ) {
if ( Hibernate.isInitialized( proxy ) ) {
data.setState( State.INITIALIZED );
data.entityInstanceForNotify = Hibernate.unproxy( data.getInstance() );
data.entityInstanceForNotify = Hibernate.unproxy( proxy );
}
else {
final LazyInitializer lazyInitializer = extractLazyInitializer( data.getInstance() );
final LazyInitializer lazyInitializer = extractLazyInitializer( proxy );
assert lazyInitializer != null;
data.entityInstanceForNotify = resolveEntityInstance2( data );
lazyInitializer.setImplementation( data.entityInstanceForNotify );
@ -913,7 +988,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
data.setState( State.INITIALIZED );
}
else if ( isResultInitializer() ) {
registerLoadingEntity( data, data.getInstance() );
registerLoadingEntity( data, existingEntity );
}
}
else if ( data.entityHolder.getEntityInitializer() != this ) {
@ -924,7 +999,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
// This is the entity to refresh, so don't set the state to initialized
data.setInstance( data.entityInstanceForNotify = entityFromExecutionContext );
if ( isResultInitializer() ) {
registerLoadingEntity( data, data.getInstance() );
registerLoadingEntity( data, entityFromExecutionContext );
}
}
else {
@ -953,7 +1028,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
final ExecutionContext executionContext = rowProcessingState.getJdbcValuesSourceProcessingState()
.getExecutionContext();
if ( rootEntityDescriptor == executionContext.getRootEntityDescriptor()
&& data.entityKey.getIdentifier().equals( executionContext.getEntityId() ) ) {
&& areKeysEqual( data.entityKey.getIdentifier(), executionContext.getEntityId() ) ) {
return executionContext.getEntityInstance();
}
return null;
@ -1064,9 +1139,10 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
protected Object instantiateEntity(EntityInitializerData data) {
final Object instance = data.getRowProcessingState().getSession()
.instantiate( data.concreteDescriptor, data.entityKey.getIdentifier() );
return instance;
return data.getRowProcessingState().getSession().instantiate(
data.concreteDescriptor,
data.entityKey.getIdentifier()
);
}
private Object resolveToOptionalInstance(EntityInitializerData data) {
@ -1088,7 +1164,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
final Object requestedEntityId = processingOptions.getEffectiveOptionalId();
return requestedEntityId != null
&& optionalEntityInstance != null
&& requestedEntityId.equals( data.entityKey.getIdentifier() );
&& areKeysEqual( requestedEntityId, data.entityKey.getIdentifier() );
}
private Object resolveInstanceFromCache(EntityInitializerData data) {
@ -1102,10 +1178,11 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
protected void registerLoadingEntity(EntityInitializerData data, Object instance) {
data.getRowProcessingState().getSession().getPersistenceContextInternal().claimEntityHolderIfPossible(
final RowProcessingState rowProcessingState = data.getRowProcessingState();
rowProcessingState.getSession().getPersistenceContextInternal().claimEntityHolderIfPossible(
data.entityKey,
instance,
data.getRowProcessingState().getJdbcValuesSourceProcessingState(),
rowProcessingState.getJdbcValuesSourceProcessingState(),
this
);
}
@ -1139,28 +1216,31 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
protected void initializeEntityInstance(EntityInitializerData data) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final Object entityIdentifier = data.entityKey.getIdentifier();
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityKey entityKey = data.entityKey;
assert entityKey != null;
final Object entityIdentifier = entityKey.getIdentifier();
final Object[] resolvedEntityState = extractConcreteTypeStateValues( data );
preLoad( data, resolvedEntityState );
if ( isPersistentAttributeInterceptable( data.entityInstanceForNotify ) ) {
final Object entityInstanceForNotify = data.entityInstanceForNotify;
if ( isPersistentAttributeInterceptable( entityInstanceForNotify ) ) {
final PersistentAttributeInterceptor persistentAttributeInterceptor =
asPersistentAttributeInterceptable( data.entityInstanceForNotify ).$$_hibernate_getInterceptor();
asPersistentAttributeInterceptable( entityInstanceForNotify ).$$_hibernate_getInterceptor();
if ( persistentAttributeInterceptor == null
|| persistentAttributeInterceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
// if we do this after the entity has been initialized the
// BytecodeLazyAttributeInterceptor#isAttributeLoaded(String fieldName) would return false;
data.concreteDescriptor.getBytecodeEnhancementMetadata()
.injectInterceptor( data.entityInstanceForNotify, entityIdentifier, session );
.injectInterceptor( entityInstanceForNotify, entityIdentifier, session );
}
}
data.concreteDescriptor.setPropertyValues( data.entityInstanceForNotify, resolvedEntityState );
data.concreteDescriptor.setPropertyValues( entityInstanceForNotify, resolvedEntityState );
persistenceContext.addEntity( data.entityKey, data.entityInstanceForNotify );
persistenceContext.addEntity( entityKey, entityInstanceForNotify );
// Also register possible unique key entries
registerPossibleUniqueKeyEntries( data, resolvedEntityState, session );
@ -1173,26 +1253,27 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
final LockMode lockModeToAcquire = data.lockMode == LockMode.NONE ? LockMode.READ : data.lockMode;
final EntityEntry entityEntry = persistenceContext.addEntry(
data.entityInstanceForNotify,
entityInstanceForNotify,
Status.LOADING,
resolvedEntityState,
rowId,
data.entityKey.getIdentifier(),
entityIdentifier,
version,
lockModeToAcquire,
true,
data.concreteDescriptor,
false
);
entityEntry.setMaybeLazySet( maybeLazySets[data.concreteDescriptor.getSubclassId()] );
data.entityHolder.setEntityEntry( entityEntry );
registerNaturalIdResolution( data, persistenceContext, resolvedEntityState );
takeSnapshot( data, session, persistenceContext, entityEntry, resolvedEntityState );
data.concreteDescriptor.afterInitialize( data.entityInstanceForNotify, session );
data.concreteDescriptor.afterInitialize( entityInstanceForNotify, session );
assert data.concreteDescriptor.getIdentifier( data.entityInstanceForNotify, session ) != null;
assert data.concreteDescriptor.getIdentifier( entityInstanceForNotify, session ) != null;
final StatisticsImplementor statistics = session.getFactory().getStatistics();
if ( statistics.isStatisticsEnabled() ) {
@ -1399,7 +1480,27 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
return values;
}
protected void resolveState(EntityInitializerData data) {
@Override
public void resolveState(EntityInitializerData data) {
if ( identifierAssembler != null ) {
identifierAssembler.resolveState( data.getRowProcessingState() );
}
if ( discriminatorAssembler != null ) {
discriminatorAssembler.resolveState( data.getRowProcessingState() );
}
if ( keyAssembler != null ) {
keyAssembler.resolveState( data.getRowProcessingState() );
}
if ( versionAssembler != null ) {
versionAssembler.resolveState( data.getRowProcessingState() );
}
if ( rowIdAssembler != null ) {
rowIdAssembler.resolveState( data.getRowProcessingState() );
}
resolveEntityState( data );
}
protected void resolveEntityState(EntityInitializerData data) {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
for ( final DomainResultAssembler<?> assembler : assemblers[data.concreteDescriptor.getSubclassId()] ) {
if ( assembler != null ) {
@ -1478,6 +1579,16 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
return isPartOfKey;
}
@Override
public boolean isEager() {
return true;
}
@Override
public boolean hasEagerSubInitializers() {
return hasEagerSubInitializers;
}
public boolean isPreviousRowReuse() {
return previousRowReuse;
}
@ -1532,12 +1643,6 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
}
}
@Override
public void endLoading(EntityInitializerData data) {
super.endLoading( data );
data.shallowCached = false;
}
@Override
public String toString() {
return "EntityJoinedFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";

View File

@ -36,7 +36,6 @@ 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;
/**
@ -44,8 +43,6 @@ import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
*/
public class EntitySelectFetchInitializer<Data extends EntitySelectFetchInitializer.EntitySelectFetchInitializerData>
extends AbstractInitializer<Data> implements EntityInitializer<Data> {
private static final String CONCRETE_NAME = EntitySelectFetchInitializer.class.getSimpleName();
protected final InitializerParent<?> parent;
private final NavigablePath navigablePath;
private final boolean isPartOfKey;
@ -55,12 +52,13 @@ public class EntitySelectFetchInitializer<Data extends EntitySelectFetchInitiali
protected final DomainResultAssembler<?> keyAssembler;
protected final ToOneAttributeMapping toOneMapping;
protected final boolean affectedByFilter;
protected final boolean keyIsEager;
public static class EntitySelectFetchInitializerData extends InitializerData {
// per-row state
protected @Nullable Object entityIdentifier;
public EntitySelectFetchInitializerData(RowProcessingState rowProcessingState) {
public EntitySelectFetchInitializerData(EntitySelectFetchInitializer<?> initializer, RowProcessingState rowProcessingState) {
super( rowProcessingState );
}
@ -90,11 +88,14 @@ public class EntitySelectFetchInitializer<Data extends EntitySelectFetchInitiali
this.keyAssembler = keyResult.createResultAssembler( this, creationState );
this.isEnhancedForLazyLoading = concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading();
this.affectedByFilter = affectedByFilter;
this.keyIsEager = keyAssembler != null
&& keyAssembler.getInitializer() != null
&& keyAssembler.getInitializer().isEager();
}
@Override
protected InitializerData createInitializerData(RowProcessingState rowProcessingState) {
return new EntitySelectFetchInitializerData( rowProcessingState );
return new EntitySelectFetchInitializerData( this, rowProcessingState );
}
public ModelPart getInitializedPart(){
@ -114,9 +115,8 @@ public class EntitySelectFetchInitializer<Data extends EntitySelectFetchInitiali
@Override
public void resolveFromPreviousRow(Data data) {
if ( data.getState() == State.UNINITIALIZED ) {
if ( data.entityIdentifier == null ) {
if ( data.getInstance() == null ) {
data.setState( State.MISSING );
data.setInstance( null );
}
else {
final Initializer<?> initializer = keyAssembler.getInitializer();
@ -155,23 +155,29 @@ public class EntitySelectFetchInitializer<Data extends EntitySelectFetchInitiali
}
else {
final RowProcessingState rowProcessingState = data.getRowProcessingState();
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final LazyInitializer lazyInitializer = extractLazyInitializer( data.getInstance() );
if ( lazyInitializer == null ) {
data.setState( State.INITIALIZED );
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
if ( keyIsEager ) {
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, rowProcessingState.getSession() );
}
}
else if ( lazyInitializer.isUninitialized() ) {
data.setState( State.RESOLVED );
data.entityIdentifier = lazyInitializer.getIdentifier();
if ( keyIsEager ) {
data.entityIdentifier = lazyInitializer.getIdentifier();
}
}
else {
data.setState( State.INITIALIZED );
data.entityIdentifier = lazyInitializer.getIdentifier();
if ( keyIsEager ) {
data.entityIdentifier = lazyInitializer.getIdentifier();
}
}
data.setInstance( instance );
final Initializer<?> initializer = keyAssembler.getInitializer();
if ( initializer != null ) {
if ( keyIsEager ) {
final Initializer<?> initializer = keyAssembler.getInitializer();
assert initializer != null;
initializer.resolveInstance( data.entityIdentifier, rowProcessingState );
}
else if ( rowProcessingState.needsResolveState() ) {
@ -241,7 +247,7 @@ public class EntitySelectFetchInitializer<Data extends EntitySelectFetchInitiali
throw new FetchNotFoundException( entityName, data.entityIdentifier );
}
}
rowProcessingState.getSession().getPersistenceContextInternal().claimEntityHolderIfPossible(
persistenceContext.claimEntityHolderIfPossible(
new EntityKey( data.entityIdentifier, concreteDescriptor ),
instance,
rowProcessingState.getJdbcValuesSourceProcessingState(),
@ -289,11 +295,6 @@ public class EntitySelectFetchInitializer<Data extends EntitySelectFetchInitiali
return concreteDescriptor;
}
@Override
public Object getEntityInstance(Data data) {
return data.getInstance();
}
@Override
public EntityPersister getConcreteDescriptor(Data data) {
return concreteDescriptor;
@ -309,6 +310,21 @@ public class EntitySelectFetchInitializer<Data extends EntitySelectFetchInitiali
return isPartOfKey;
}
@Override
public boolean isEager() {
return true;
}
@Override
public void resolveState(EntitySelectFetchInitializerData data) {
keyAssembler.resolveState( data.getRowProcessingState() );
}
@Override
public boolean hasEagerSubInitializers() {
return keyIsEager;
}
@Override
public boolean isResultInitializer() {
return false;

View File

@ -61,6 +61,13 @@ public class DynamicInstantiationAssemblerConstructorImpl<R> implements DomainRe
}
}
@Override
public void resolveState(RowProcessingState rowProcessingState) {
for ( ArgumentReader<?> argumentReader : argumentReaders ) {
argumentReader.resolveState( rowProcessingState );
}
}
@Override
public <X> void forEachResultAssembler(BiConsumer<Initializer<?>, X> consumer, X arg) {
for ( ArgumentReader<?> argumentReader : argumentReaders ) {

View File

@ -110,6 +110,13 @@ public class DynamicInstantiationAssemblerInjectionImpl<T> implements DomainResu
return result;
}
@Override
public void resolveState(RowProcessingState rowProcessingState) {
for ( BeanInjection beanInjection : beanInjections ) {
beanInjection.getValueAssembler().resolveState( rowProcessingState );
}
}
@Override
public <X> void forEachResultAssembler(BiConsumer<Initializer<?>, X> consumer, X arg) {
for ( BeanInjection beanInjection : beanInjections ) {

View File

@ -49,6 +49,13 @@ public class DynamicInstantiationAssemblerListImpl implements DomainResultAssemb
return result;
}
@Override
public void resolveState(RowProcessingState rowProcessingState) {
for ( ArgumentReader<?> argumentReader : argumentReaders ) {
argumentReader.resolveState( rowProcessingState );
}
}
@Override
public <X> void forEachResultAssembler(BiConsumer<Initializer<?>, X> consumer, X arg) {
for ( ArgumentReader<?> argumentReader : argumentReaders ) {

View File

@ -75,6 +75,13 @@ public class DynamicInstantiationAssemblerMapImpl implements DomainResultAssembl
return result;
}
@Override
public void resolveState(RowProcessingState rowProcessingState) {
for ( ArgumentReader<?> argumentReader : argumentReaders ) {
argumentReader.resolveState( rowProcessingState );
}
}
@Override
public <X> void forEachResultAssembler(BiConsumer<Initializer<?>, X> consumer, X arg) {
for ( ArgumentReader<?> argumentReader : argumentReaders ) {

View File

@ -136,8 +136,11 @@ public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch {
}
}
final InitializerData data = initializer.getData( rowProcessingState );
initializer.resolveInstance( data );
final Object initializedInstance = initializer.getEntityInstance( data );
final Initializer.State state = data.getState();
if ( state == Initializer.State.KEY_RESOLVED ) {
initializer.resolveInstance( data );
}
final Object initializedInstance = initializer.getResolvedInstance( data );
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( initializedInstance );
if ( lazyInitializer != null ) {
final Class<?> concreteProxyClass = initializer.getConcreteDescriptor( data ).getConcreteProxyClass();

View File

@ -178,8 +178,16 @@ public class CircularFetchImpl extends AbstractNonJoinedEntityFetch implements B
@Override
public Object assemble(RowProcessingState rowProcessingState) {
final InitializerData data = initializer.getData( rowProcessingState );
initializer.resolveInstance( data );
return initializer.getEntityInstance( data );
final Initializer.State state = data.getState();
if ( state == Initializer.State.KEY_RESOLVED ) {
initializer.resolveInstance( data );
}
return initializer.getResolvedInstance( data );
}
@Override
public void resolveState(RowProcessingState rowProcessingState) {
initializer.resolveState( rowProcessingState );
}
@Override

View File

@ -9,8 +9,11 @@ package org.hibernate.orm.test.query;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -61,6 +64,35 @@ public class ReloadEntityTest {
} );
}
@Test
public void testFlushAndReload(EntityManagerFactoryScope scope) {
scope.inTransaction( em -> {
// Load an Author with EAGER details
final List<Author> authors1 = em.createQuery( "from Author", Author.class ).getResultList();
final Author author = authors1.get(0);
// Create a new details object and then detach it
final AuthorDetails details = new AuthorDetails();
details.name = "Author Details";
details.author = author;
author.details = null;
em.persist( details );
em.flush();
em.detach( details );
// Replace the details with a lazy proxy
author.details = em.getReference( AuthorDetails.class, details.detailsId );
em.flush();
Assertions.assertFalse( Hibernate.isInitialized( author.details ) );
final List<Author> authors2 = em.createQuery( "from Author join fetch details", Author.class ).getResultList();
final Author author2 = authors2.get(0);
Assertions.assertTrue( Hibernate.isInitialized( author2.details ) );
Assertions.assertEquals( details.name, author2.details.getName() );
} );
}
@Entity(name = "Author")
@Table(name = "Author")
public static class Author {
@ -74,7 +106,7 @@ public class ReloadEntityTest {
@OneToMany(fetch = FetchType.LAZY, mappedBy = "author")
public List<Book> books = new ArrayList<>();
@OneToOne(fetch = FetchType.EAGER, optional = false, cascade = CascadeType.ALL, orphanRemoval = true)
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
public AuthorDetails details;
}
@ -91,6 +123,10 @@ public class ReloadEntityTest {
@OneToOne(fetch = FetchType.LAZY, mappedBy = "details", optional = false)
public Author author;
public String getName() {
return name;
}
}
@Entity(name = "Book")