HHH-18271 Fix faulty maybe lazy set determination leading to issue with previous row optimization
This commit is contained in:
parent
badf4f278f
commit
a2c948909a
|
@ -221,9 +221,18 @@ public interface Initializer<Data extends InitializerData> {
|
|||
boolean isEager();
|
||||
|
||||
/**
|
||||
* Indicates whether this initializers has eager sub-initializers, that could initialize lazy state of existing objects.
|
||||
* Indicates whether this initializer or one of its sub-parts could be made lazy.
|
||||
*/
|
||||
boolean hasEagerSubInitializers();
|
||||
default boolean isLazyCapable() {
|
||||
// Usually, every model part for which an initializer exists is lazy capable
|
||||
// except for embeddable initializers with no sub-initializers
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this initializer has sub-initializers which are lazy.
|
||||
*/
|
||||
boolean hasLazySubInitializers();
|
||||
|
||||
/**
|
||||
* Indicates if this is a result or fetch initializer.
|
||||
|
|
|
@ -240,7 +240,7 @@ public abstract class AbstractCollectionInitializer<Data extends AbstractCollect
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEagerSubInitializers() {
|
||||
public boolean hasLazySubInitializers() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ public class DelayedCollectionInitializer extends AbstractNonJoinCollectionIniti
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEagerSubInitializers() {
|
||||
public boolean hasLazySubInitializers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.embeddable.internal;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.engine.internal.ManagedTypeHelper;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
|
@ -53,7 +54,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 @Nullable InitializerParent<InitializerData> parent;
|
||||
private final boolean isResultInitializer;
|
||||
private final boolean isPartOfKey;
|
||||
private final boolean createEmptyCompositesEnabled;
|
||||
|
@ -62,7 +63,10 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
|
|||
protected final DomainResultAssembler<?>[][] assemblers;
|
||||
protected final BasicResultAssembler<?> discriminatorAssembler;
|
||||
protected final @Nullable Initializer<InitializerData>[][] subInitializers;
|
||||
protected final BitSet subInitializersNeedingResolveIfParentInitialized;
|
||||
protected final @Nullable Initializer<InitializerData>[][] subInitializersForResolveFromInitialized;
|
||||
// protected final BitSet eagerSubInitializers;
|
||||
protected final boolean lazyCapable;
|
||||
protected final boolean hasLazySubInitializer;
|
||||
|
||||
public static class EmbeddableInitializerData extends InitializerData implements ValueAccess {
|
||||
protected final InitializerData parentData;
|
||||
|
@ -123,18 +127,42 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
|
|||
(BasicResultAssembler<?>) discriminatorFetch.createAssembler( this, creationState ) :
|
||||
null;
|
||||
this.subInitializers = createInitializers( assemblers );
|
||||
final BitSet subInitializersNeedingResolveIfParentInitialized = new BitSet(subInitializers.length * embeddableMappingType.getNumberOfFetchables());
|
||||
final @Nullable Initializer<InitializerData>[][] eagerSubInitializers = new Initializer[subInitializers.length][];
|
||||
Arrays.fill( eagerSubInitializers, Initializer.EMPTY_ARRAY );
|
||||
// final BitSet eagerSubInitializers = new BitSet(subInitializers.length * embeddableMappingType.getNumberOfFetchables());
|
||||
boolean lazyCapable = false;
|
||||
boolean hasLazySubInitializers = false;
|
||||
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 );
|
||||
if ( initializer.isEager() ) {
|
||||
if ( eagerSubInitializers[subclassId] == Initializer.EMPTY_ARRAY ) {
|
||||
eagerSubInitializers[subclassId] = new Initializer[subInitializer.length];
|
||||
}
|
||||
eagerSubInitializers[subclassId][i] = initializer;
|
||||
hasLazySubInitializers = hasLazySubInitializers || initializer.hasLazySubInitializers();
|
||||
}
|
||||
else {
|
||||
hasLazySubInitializers = true;
|
||||
}
|
||||
lazyCapable = lazyCapable || initializer.isLazyCapable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.subInitializersNeedingResolveIfParentInitialized = subInitializersNeedingResolveIfParentInitialized;
|
||||
this.subInitializersForResolveFromInitialized = isEnhancedForLazyLoading( embeddableMappingType )
|
||||
? subInitializers
|
||||
: eagerSubInitializers;
|
||||
this.lazyCapable = lazyCapable;
|
||||
this.hasLazySubInitializer = hasLazySubInitializers;
|
||||
}
|
||||
|
||||
private static boolean isEnhancedForLazyLoading(EmbeddableMappingType embeddableMappingType) {
|
||||
return ManagedTypeHelper.isPersistentAttributeInterceptableType(
|
||||
embeddableMappingType.getRepresentationStrategy().getMappedJavaType().getJavaTypeClass()
|
||||
);
|
||||
}
|
||||
|
||||
protected DomainResultAssembler<?>[][] createAssemblers(
|
||||
|
@ -224,9 +252,13 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEagerSubInitializers() {
|
||||
// Since embeddables are never lazy, we only need to check the components
|
||||
return !subInitializersNeedingResolveIfParentInitialized.isEmpty();
|
||||
public boolean isLazyCapable() {
|
||||
return lazyCapable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLazySubInitializers() {
|
||||
return hasLazySubInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -328,17 +360,10 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
|
|||
}
|
||||
|
||||
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 Initializer<?>[] initializers = subInitializersForResolveFromInitialized[subclassId];
|
||||
for ( int i = 0; i < initializers.length; i++ ) {
|
||||
final Initializer<?> initializer = initializers[i];
|
||||
if ( initializer != null ) {
|
||||
final Object subInstance = embeddableMappingType.getValue( instance, i );
|
||||
if ( subInstance == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
|
||||
// Go through the normal initializer process
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.graph.embeddable.internal;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -58,7 +57,9 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
|
|||
|
||||
private final DomainResultAssembler<?>[] assemblers;
|
||||
private final @Nullable Initializer<InitializerData>[] initializers;
|
||||
private final BitSet subInitializersNeedingResolveIfParentInitialized;
|
||||
private final @Nullable Initializer<InitializerData>[] subInitializersForResolveFromInitialized;
|
||||
private final boolean lazyCapable;
|
||||
private final boolean hasLazySubInitializer;
|
||||
private final boolean hasIdClass;
|
||||
|
||||
public static class NonAggregatedIdentifierMappingInitializerData extends InitializerData implements ValueAccess {
|
||||
|
@ -125,14 +126,23 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
|
|||
this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
|
||||
this.assemblers = createAssemblers( this, resultDescriptor, creationState, virtualIdEmbeddable, fetchConverter );
|
||||
final Initializer<?>[] initializers = new Initializer[assemblers.length];
|
||||
final BitSet subInitializersNeedingResolveIfParentInitialized = new BitSet(assemblers.length);
|
||||
final Initializer<?>[] eagerSubInitializers = new Initializer[assemblers.length];
|
||||
boolean empty = true;
|
||||
boolean emptyEager = true;
|
||||
boolean lazyCapable = false;
|
||||
boolean hasLazySubInitializers = false;
|
||||
for ( int i = 0; i < assemblers.length; i++ ) {
|
||||
final Initializer<?> initializer = assemblers[i].getInitializer();
|
||||
if ( initializer != null ) {
|
||||
if ( initializer.isEager() ) {
|
||||
subInitializersNeedingResolveIfParentInitialized.set( i );
|
||||
eagerSubInitializers[i] = initializer;
|
||||
hasLazySubInitializers = hasLazySubInitializers || initializer.hasLazySubInitializers();
|
||||
emptyEager = false;
|
||||
}
|
||||
else {
|
||||
hasLazySubInitializers = true;
|
||||
}
|
||||
lazyCapable = lazyCapable || initializer.isLazyCapable();
|
||||
initializers[i] = initializer;
|
||||
empty = false;
|
||||
}
|
||||
|
@ -141,7 +151,12 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
|
|||
this.initializers = (Initializer<InitializerData>[]) (
|
||||
empty ? Initializer.EMPTY_ARRAY : initializers
|
||||
);
|
||||
this.subInitializersNeedingResolveIfParentInitialized = subInitializersNeedingResolveIfParentInitialized;
|
||||
// No need to think about bytecode enhancement here, since ids can't contain lazy basic attributes
|
||||
this.subInitializersForResolveFromInitialized = (Initializer<InitializerData>[]) (
|
||||
emptyEager ? Initializer.EMPTY_ARRAY : initializers
|
||||
);
|
||||
this.lazyCapable = lazyCapable;
|
||||
this.hasLazySubInitializer = hasLazySubInitializers;
|
||||
}
|
||||
|
||||
protected static DomainResultAssembler<?>[] createAssemblers(
|
||||
|
@ -260,13 +275,9 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
|
|||
}
|
||||
|
||||
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;
|
||||
for ( int i = 0; i < subInitializersForResolveFromInitialized.length; i++ ) {
|
||||
final Initializer<InitializerData> initializer = subInitializersForResolveFromInitialized[i];
|
||||
if ( initializer != null ) {
|
||||
final Object subInstance = virtualIdEmbeddable.getValue( instance, i );
|
||||
if ( subInstance == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
|
||||
// Go through the normal initializer process
|
||||
|
@ -370,9 +381,13 @@ public class NonAggregatedIdentifierMappingInitializer extends AbstractInitializ
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEagerSubInitializers() {
|
||||
// Since embeddables are never lazy, we only need to check the components
|
||||
return !subInitializersNeedingResolveIfParentInitialized.isEmpty();
|
||||
public boolean isLazyCapable() {
|
||||
return lazyCapable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLazySubInitializers() {
|
||||
return hasLazySubInitializer;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -52,6 +52,7 @@ public class DiscriminatedEntityInitializer
|
|||
private final boolean eager;
|
||||
private final boolean resultInitializer;
|
||||
private final boolean keyIsEager;
|
||||
private final boolean hasLazySubInitializer;
|
||||
|
||||
public static class DiscriminatedEntityInitializerData extends InitializerData {
|
||||
protected EntityPersister concreteDescriptor;
|
||||
|
@ -80,9 +81,15 @@ 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();
|
||||
final Initializer<?> initializer = keyValueAssembler.getInitializer();
|
||||
if ( initializer == null ) {
|
||||
this.keyIsEager = false;
|
||||
this.hasLazySubInitializer = false;
|
||||
}
|
||||
else {
|
||||
this.keyIsEager = initializer.isEager();
|
||||
this.hasLazySubInitializer = !initializer.isEager() || initializer.hasLazySubInitializers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -316,8 +323,8 @@ public class DiscriminatedEntityInitializer
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEagerSubInitializers() {
|
||||
return keyIsEager;
|
||||
public boolean hasLazySubInitializers() {
|
||||
return hasLazySubInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -55,6 +55,7 @@ public class EntityDelayedFetchInitializer
|
|||
private final DomainResultAssembler<?> identifierAssembler;
|
||||
private final @Nullable BasicResultAssembler<?> discriminatorAssembler;
|
||||
private final boolean keyIsEager;
|
||||
private final boolean hasLazySubInitializer;
|
||||
|
||||
public static class EntityDelayedFetchInitializerData extends InitializerData {
|
||||
// per-row state
|
||||
|
@ -86,9 +87,15 @@ public class EntityDelayedFetchInitializer
|
|||
this.discriminatorAssembler = discriminatorResult == null
|
||||
? null
|
||||
: (BasicResultAssembler<?>) discriminatorResult.createResultAssembler( this, creationState );
|
||||
this.keyIsEager = identifierAssembler != null
|
||||
&& identifierAssembler.getInitializer() != null
|
||||
&& identifierAssembler.getInitializer().isEager();
|
||||
final Initializer<?> initializer;
|
||||
if ( identifierAssembler == null || ( initializer = identifierAssembler.getInitializer() ) == null ) {
|
||||
this.keyIsEager = false;
|
||||
this.hasLazySubInitializer = false;
|
||||
}
|
||||
else {
|
||||
this.keyIsEager = initializer.isEager();
|
||||
this.hasLazySubInitializer = !initializer.isEager() || initializer.hasLazySubInitializers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -291,8 +298,8 @@ public class EntityDelayedFetchInitializer
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEagerSubInitializers() {
|
||||
return keyIsEager;
|
||||
public boolean hasLazySubInitializers() {
|
||||
return hasLazySubInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -132,9 +132,9 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
|
|||
private final Initializer<?>[][] subInitializers;
|
||||
private final Initializer<?>[][] subInitializersForResolveFromInitialized;
|
||||
private final MutabilityPlan<Object>[][] updatableAttributeMutabilityPlans;
|
||||
private final ImmutableBitSet[] lazySubInitializers;
|
||||
private final ImmutableBitSet[] lazySets;
|
||||
private final ImmutableBitSet[] maybeLazySets;
|
||||
private final boolean hasEagerSubInitializers;
|
||||
private final boolean hasLazySubInitializers;
|
||||
|
||||
public static class EntityInitializerData extends InitializerData {
|
||||
|
||||
|
@ -280,7 +280,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
|
|||
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[] lazySets = 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()];
|
||||
|
@ -291,7 +291,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
|
|||
updatableAttributeMutabilityPlans[subMappingType.getSubclassId()] = new MutabilityPlan[subMappingType.getNumberOfAttributeMappings()];
|
||||
}
|
||||
|
||||
boolean hasEagerSubInitializers = false;
|
||||
boolean hasLazySubInitializers = false;
|
||||
final int size = entityDescriptor.getNumberOfFetchables();
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
final AttributeMapping attributeMapping = entityDescriptor.getFetchable( i ).asAttributeMapping();
|
||||
|
@ -309,20 +309,22 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
|
|||
if ( subInitializers[subclassId] == null ) {
|
||||
subInitializers[subclassId] = new Initializer<?>[size];
|
||||
eagerSubInitializers[subclassId] = new Initializer<?>[size];
|
||||
lazySubInitializers[subclassId] = new BitSet(size);
|
||||
maybeLazySets[subclassId] = new BitSet(size);
|
||||
lazySets[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() ) {
|
||||
if ( subInitializer.hasLazySubInitializers() ) {
|
||||
maybeLazySets[subclassId].set( stateArrayPosition );
|
||||
hasLazySubInitializers = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
lazySubInitializers[subclassId].set( stateArrayPosition );
|
||||
// Lazy initializer
|
||||
lazySets[subclassId].set( stateArrayPosition );
|
||||
maybeLazySets[subclassId].set( stateArrayPosition );
|
||||
hasLazySubInitializers = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,19 +340,18 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
|
|||
if ( subInitializers[subMappingType.getSubclassId()] == null ) {
|
||||
subInitializers[subMappingType.getSubclassId()] = new Initializer<?>[size];
|
||||
eagerSubInitializers[subMappingType.getSubclassId()] = new Initializer<?>[size];
|
||||
lazySubInitializers[subMappingType.getSubclassId()] = new BitSet(size);
|
||||
lazySets[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() ) {
|
||||
if ( subInitializer.hasLazySubInitializers() ) {
|
||||
maybeLazySets[subMappingType.getSubclassId()].set( stateArrayPosition );
|
||||
}
|
||||
}
|
||||
else {
|
||||
lazySubInitializers[subMappingType.getSubclassId()].set( stateArrayPosition );
|
||||
lazySets[subMappingType.getSubclassId()].set( stateArrayPosition );
|
||||
maybeLazySets[subMappingType.getSubclassId()].set( stateArrayPosition );
|
||||
}
|
||||
}
|
||||
|
@ -366,7 +367,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
|
|||
}
|
||||
}
|
||||
subInitializers[i] = Initializer.EMPTY_ARRAY;
|
||||
lazySubInitializers[i] = emptyBitSet;
|
||||
lazySets[i] = emptyBitSet;
|
||||
maybeLazySets[i] = emptyBitSet;
|
||||
}
|
||||
OUTER: for ( int i = 0; i < eagerSubInitializers.length; i++ ) {
|
||||
|
@ -385,9 +386,11 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
|
|||
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.lazySets = Arrays.stream( lazySets ).map( ImmutableBitSet::valueOf ).toArray( ImmutableBitSet[]::new );
|
||||
this.maybeLazySets = Arrays.stream( maybeLazySets )
|
||||
.map( ImmutableBitSet::valueOf )
|
||||
.toArray( ImmutableBitSet[]::new );
|
||||
this.hasLazySubInitializers = hasLazySubInitializers;
|
||||
this.updatableAttributeMutabilityPlans = updatableAttributeMutabilityPlans;
|
||||
this.notFoundAction = notFoundAction;
|
||||
|
||||
|
@ -559,7 +562,7 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
|
|||
// 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 ) ) {
|
||||
&& lazySets[subclassId].contains( maybeLazySet ) ) {
|
||||
return;
|
||||
}
|
||||
final RowProcessingState rowProcessingState = data.getRowProcessingState();
|
||||
|
@ -1585,8 +1588,8 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEagerSubInitializers() {
|
||||
return hasEagerSubInitializers;
|
||||
public boolean hasLazySubInitializers() {
|
||||
return hasLazySubInitializers;
|
||||
}
|
||||
|
||||
public boolean isPreviousRowReuse() {
|
||||
|
|
|
@ -53,6 +53,7 @@ public class EntitySelectFetchInitializer<Data extends EntitySelectFetchInitiali
|
|||
protected final ToOneAttributeMapping toOneMapping;
|
||||
protected final boolean affectedByFilter;
|
||||
protected final boolean keyIsEager;
|
||||
protected final boolean hasLazySubInitializer;
|
||||
|
||||
public static class EntitySelectFetchInitializerData extends InitializerData {
|
||||
// per-row state
|
||||
|
@ -88,9 +89,15 @@ 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();
|
||||
final Initializer<?> initializer = keyAssembler.getInitializer();
|
||||
if ( initializer == null ) {
|
||||
this.keyIsEager = false;
|
||||
this.hasLazySubInitializer = false;
|
||||
}
|
||||
else {
|
||||
this.keyIsEager = initializer.isEager();
|
||||
this.hasLazySubInitializer = !initializer.isEager() || initializer.hasLazySubInitializers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -321,8 +328,8 @@ public class EntitySelectFetchInitializer<Data extends EntitySelectFetchInitiali
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEagerSubInitializers() {
|
||||
return keyIsEager;
|
||||
public boolean hasLazySubInitializers() {
|
||||
return hasLazySubInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.List;
|
|||
import org.hibernate.Hibernate;
|
||||
|
||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Jira;
|
||||
import org.hibernate.testing.orm.junit.Jpa;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -32,6 +33,7 @@ import jakarta.persistence.Table;
|
|||
@Jpa(
|
||||
annotatedClasses = { ReloadEntityTest.Book.class, ReloadEntityTest.Author.class, ReloadEntityTest.AuthorDetails.class }
|
||||
)
|
||||
@Jira("https://hibernate.atlassian.net/browse/HHH-18271")
|
||||
public class ReloadEntityTest {
|
||||
|
||||
@BeforeEach
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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.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.Jira;
|
||||
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;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Jpa(
|
||||
annotatedClasses = { ReloadWithPreviousRowEntityTest.Book.class, ReloadWithPreviousRowEntityTest.Author.class, ReloadWithPreviousRowEntityTest.AuthorDetails.class }
|
||||
)
|
||||
@Jira("https://hibernate.atlassian.net/browse/HHH-18271")
|
||||
public class ReloadWithPreviousRowEntityTest {
|
||||
|
||||
@BeforeEach
|
||||
public void prepareTestData(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
final Book book1 = new Book();
|
||||
book1.name = "Book 1";
|
||||
final Book book2 = new Book();
|
||||
book2.name = "Book 2";
|
||||
final Book book3 = new Book();
|
||||
book3.name = "Book 3";
|
||||
|
||||
final Author author1 = new Author();
|
||||
author1.name = "Author 1";
|
||||
final Author author2 = new Author();
|
||||
author2.name = "Author 2";
|
||||
|
||||
final AuthorDetails details1 = new AuthorDetails();
|
||||
details1.name = "Author Details";
|
||||
details1.author = author1;
|
||||
author1.details = details1;
|
||||
|
||||
final AuthorDetails details2 = new AuthorDetails();
|
||||
details2.name = "Author Details";
|
||||
details2.author = author2;
|
||||
author2.details = details2;
|
||||
|
||||
author1.books.add( book1 );
|
||||
author1.books.add( book2 );
|
||||
author1.books.add( book3 );
|
||||
book1.author = author1;
|
||||
book2.author = author1;
|
||||
book3.author = author2;
|
||||
details1.favoriteBook = book3;
|
||||
|
||||
entityManager.persist( author1 );
|
||||
entityManager.persist( author2 );
|
||||
entityManager.persist( book1 );
|
||||
entityManager.persist( book2 );
|
||||
entityManager.persist( book3 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReloadWithPreviousRow(EntityManagerFactoryScope scope) {
|
||||
scope.inEntityManager( em -> {
|
||||
// Load authors into persistence context
|
||||
Author author = em.createQuery( "from Author a join fetch a.details where a.name = 'Author 1'", Author.class ).getSingleResult();
|
||||
em.createQuery( "from Author a join fetch a.details d left join fetch d.favoriteBook join fetch a.books where a.name = 'Author 1'", Author.class ).getResultList();
|
||||
Assertions.assertTrue( Hibernate.isInitialized( author.details.favoriteBook ) );
|
||||
Assertions.assertTrue( Hibernate.isInitialized( author.books ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Author")
|
||||
@Table(name = "Author")
|
||||
public static class Author {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long authorId;
|
||||
|
||||
@Column
|
||||
public String name;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "author")
|
||||
public List<Book> books = new ArrayList<>();
|
||||
|
||||
@OneToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
public AuthorDetails details;
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "AuthorDetails")
|
||||
@Table(name = "AuthorDetails")
|
||||
public static class AuthorDetails {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long detailsId;
|
||||
|
||||
@Column
|
||||
public String name;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY, mappedBy = "details", optional = false)
|
||||
public Author author;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
public Book favoriteBook;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Book")
|
||||
@Table(name = "Book")
|
||||
public static class Book {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long bookId;
|
||||
|
||||
@Column
|
||||
public String name;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "author_id", nullable = false)
|
||||
public Author author;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue