Fix BiDirectionalFetchImpl CircularFetchAssembler#assemble for collection initialization
This commit is contained in:
parent
7de3be2492
commit
484589c5b4
|
@ -7,10 +7,12 @@
|
|||
package org.hibernate.sql.results.graph.collection;
|
||||
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.CollectionKey;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
/**
|
||||
* Initializer implementation for initializing collections (plural attributes)
|
||||
|
@ -38,4 +40,6 @@ public interface CollectionInitializer extends Initializer {
|
|||
default void endLoading(ExecutionContext context) {
|
||||
// by default - nothing to do
|
||||
}
|
||||
|
||||
CollectionKey resolveCollectionKey(RowProcessingState rowProcessingState);
|
||||
}
|
||||
|
|
|
@ -112,6 +112,7 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
|
|||
return keyCollectionValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionKey resolveCollectionKey(RowProcessingState rowProcessingState) {
|
||||
resolveKey( rowProcessingState );
|
||||
return collectionKey;
|
||||
|
|
|
@ -153,5 +153,11 @@ public class DelayedCollectionAssembler implements DomainResultAssembler {
|
|||
public PersistentCollection getCollectionInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionKey resolveCollectionKey(RowProcessingState rowProcessingState) {
|
||||
resolveKey( rowProcessingState );
|
||||
return collectionKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,19 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.FetchStrategy;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.CollectionKey;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.mapping.Association;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.SingularAssociationAttributeMapping;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.BiDirectionalFetch;
|
||||
|
@ -28,7 +35,11 @@ import org.hibernate.sql.results.graph.FetchParent;
|
|||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
import org.hibernate.sql.results.graph.collection.internal.AbstractCollectionInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
|
||||
import org.hibernate.sql.results.graph.entity.internal.EntityResultImpl;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
@ -93,6 +104,7 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch, Association {
|
|||
Consumer<Initializer> collector,
|
||||
AssemblerCreationState creationState) {
|
||||
return new CircularFetchAssembler(
|
||||
fetchable,
|
||||
getReferencedPath(),
|
||||
fetchable.getJavaTypeDescriptor()
|
||||
);
|
||||
|
@ -170,10 +182,13 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch, Association {
|
|||
private static class CircularFetchAssembler implements DomainResultAssembler {
|
||||
private final NavigablePath circularPath;
|
||||
private final JavaTypeDescriptor javaTypeDescriptor;
|
||||
private final Fetchable fetchable;
|
||||
|
||||
public CircularFetchAssembler(
|
||||
Fetchable fetchable,
|
||||
NavigablePath circularPath,
|
||||
JavaTypeDescriptor javaTypeDescriptor) {
|
||||
this.fetchable = fetchable;
|
||||
this.circularPath = circularPath;
|
||||
this.javaTypeDescriptor = javaTypeDescriptor;
|
||||
}
|
||||
|
@ -181,6 +196,24 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch, Association {
|
|||
@Override
|
||||
public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
|
||||
final EntityInitializer initializer = resolveCircularInitializer( rowProcessingState );
|
||||
if ( initializer == null ) {
|
||||
|
||||
final Initializer parentInitializer = rowProcessingState.resolveInitializer(
|
||||
circularPath.getParent() );
|
||||
assert parentInitializer instanceof CollectionInitializer;
|
||||
final CollectionInitializer circ = (CollectionInitializer) parentInitializer;
|
||||
final CollectionKey collectionKey = circ.resolveCollectionKey( rowProcessingState );
|
||||
final EntityKey entityKey = new EntityKey(
|
||||
collectionKey.getKey(),
|
||||
(EntityPersister) ( (AttributeMapping) fetchable ).getMappedTypeDescriptor()
|
||||
);
|
||||
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getJdbcValuesSourceProcessingState()
|
||||
.getSession();
|
||||
return session.getPersistenceContext()
|
||||
.getEntity( entityKey );
|
||||
|
||||
}
|
||||
if ( initializer.getInitializedInstance() == null ) {
|
||||
initializer.resolveKey( rowProcessingState );
|
||||
initializer.resolveInstance( rowProcessingState );
|
||||
|
@ -199,9 +232,14 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch, Association {
|
|||
|
||||
NavigablePath path = circularPath.getParent();
|
||||
Initializer parentInitializer = rowProcessingState.resolveInitializer( path );
|
||||
while ( ! ( parentInitializer instanceof EntityInitializer ) ) {
|
||||
while ( !( parentInitializer instanceof EntityInitializer) && path.getParent() != null ) {
|
||||
path = path.getParent();
|
||||
parentInitializer = rowProcessingState.resolveInitializer( path );
|
||||
|
||||
}
|
||||
|
||||
if ( !( parentInitializer instanceof EntityInitializer ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (EntityInitializer) parentInitializer;
|
||||
|
|
|
@ -48,13 +48,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
MultiLoadSubSelectCollectionTest.Child.class
|
||||
})
|
||||
@ServiceRegistry
|
||||
@SessionFactory
|
||||
@SessionFactory(generateStatistics = true)
|
||||
public class MultiLoadSubSelectCollectionTest {
|
||||
|
||||
protected void addSettings(Map settings) {
|
||||
settings.put( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void before(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
|
@ -118,6 +114,49 @@ public class MultiLoadSubSelectCollectionTest {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12740")
|
||||
public void testSubselect_2(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
List<Parent> list = session.byMultipleIds( Parent.class ).multiLoad( ids( 1 ) );
|
||||
assertEquals( 1, list.size() );
|
||||
|
||||
// None of the collections should be loaded yet
|
||||
for ( Parent p : list ) {
|
||||
assertFalse( Hibernate.isInitialized( p.children ) );
|
||||
}
|
||||
|
||||
Hibernate.initialize( list.get( 0 ).children );
|
||||
|
||||
|
||||
// // When the first collection is loaded, the full batch of 50 collections
|
||||
// // should be loaded.
|
||||
// Hibernate.initialize( list.get( 0 ).children );
|
||||
//
|
||||
// for ( int i = 0; i < 50; i++ ) {
|
||||
// assertTrue( Hibernate.isInitialized( list.get( i ).children ) );
|
||||
// assertEquals( i + 1, list.get( i ).children.size() );
|
||||
// }
|
||||
//
|
||||
// // The collections for the 51st through 56th entities should still be uninitialized
|
||||
// for ( int i = 50; i < 56; i++ ) {
|
||||
// assertFalse( Hibernate.isInitialized( list.get( i ).children ) );
|
||||
// }
|
||||
//
|
||||
// // When the 51st collection gets initialized, the remaining collections should
|
||||
// // also be initialized.
|
||||
// Hibernate.initialize( list.get( 50 ).children );
|
||||
//
|
||||
// for ( int i = 50; i < 56; i++ ) {
|
||||
// assertTrue( Hibernate.isInitialized( list.get( i ).children ) );
|
||||
// assertEquals( i + 1, list.get( i ).children.size() );
|
||||
// }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private Integer[] ids(int count) {
|
||||
Integer[] ids = new Integer[count];
|
||||
for ( int i = 1; i <= count; i++ ) {
|
||||
|
|
Loading…
Reference in New Issue