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:
parent
55702e458b
commit
72e2da2da8
|
@ -266,6 +266,7 @@ public class EntityUpdateAction extends EntityAction {
|
|||
nextVersion = getVersion( state, persister );
|
||||
}
|
||||
entry.postUpdate( instance, state, nextVersion );
|
||||
entry.setMaybeLazySet( null );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -53,7 +53,7 @@ public class CollectionAssembler implements DomainResultAssembler {
|
|||
|
||||
@Override
|
||||
public void resolveState(RowProcessingState rowProcessingState) {
|
||||
initializer.resolveInstance( rowProcessingState );
|
||||
initializer.resolveState( rowProcessingState );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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() ) + ")";
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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() };
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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() };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ) );
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() ) + ")";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue