Fix BiDirectionalFetchImpl CircularFetchAssembler#assemble for collection initialization

This commit is contained in:
Andrea Boriero 2020-02-04 16:56:54 +00:00
parent 7de3be2492
commit 484589c5b4
5 changed files with 94 additions and 6 deletions

View File

@ -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);
}

View File

@ -112,6 +112,7 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial
return keyCollectionValue;
}
@Override
public CollectionKey resolveCollectionKey(RowProcessingState rowProcessingState) {
resolveKey( rowProcessingState );
return collectionKey;

View File

@ -153,5 +153,11 @@ public class DelayedCollectionAssembler implements DomainResultAssembler {
public PersistentCollection getCollectionInstance() {
return instance;
}
@Override
public CollectionKey resolveCollectionKey(RowProcessingState rowProcessingState) {
resolveKey( rowProcessingState );
return collectionKey;
}
}
}

View File

@ -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;

View File

@ -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++ ) {