HHH-14124 Test that the FETCH entitygraph semantic overrides EAGER associations to LAZY for all results

This commit is contained in:
Yoann Rodière 2020-07-29 11:38:51 +02:00 committed by Andrea Boriero
parent b15835788b
commit 00989d28d8
2 changed files with 181 additions and 85 deletions

View File

@ -441,8 +441,10 @@ public abstract class Loader {
loadedKeys, loadedKeys,
returnProxies returnProxies
); );
// Signal that a new row starts. Used in initializeEntitiesAndCollections if ( nullSeparatedHydratedObjects != null ) {
nullSeparatedHydratedObjects.add( null ); // Signal that a new row starts. Used in initializeEntitiesAndCollections
nullSeparatedHydratedObjects.add( null );
}
if ( !keyToRead.equals( loadedKeys[0] ) ) { if ( !keyToRead.equals( loadedKeys[0] ) ) {
throw new AssertionFailure( throw new AssertionFailure(
String.format( String.format(
@ -1051,8 +1053,10 @@ public abstract class Loader {
forcedResultTransformer forcedResultTransformer
); );
results.add( result ); results.add( result );
// Signal that a new row starts. Used in initializeEntitiesAndCollections if ( nullSeparatedHydratedObjects != null ) {
nullSeparatedHydratedObjects.add( null ); // Signal that a new row starts. Used in initializeEntitiesAndCollections
nullSeparatedHydratedObjects.add( null );
}
if ( createSubselects ) { if ( createSubselects ) {
subselectResultKeys.add( keys ); subselectResultKeys.add( keys );
keys = new EntityKey[entitySpan]; //can't reuse in this case keys = new EntityKey[entitySpan]; //can't reuse in this case

View File

@ -7,6 +7,7 @@
package org.hibernate.jpa.test.graphs; package org.hibernate.jpa.test.graphs;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
@ -17,6 +18,7 @@ import javax.persistence.Id;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
@ -62,110 +64,119 @@ public class LoadAndFetchGraphTest extends BaseEntityManagerFunctionalTestCase {
@Before @Before
public void setUp() { public void setUp() {
doInJPA( doInJPA( this::entityManagerFactory, entityManager -> {
this::entityManagerFactory, entityManager -> { // Create the model twice, with different IDs,
AEntity a1 = new AEntity(); // because we also need to test what happens when multiple results are loaded by a query.
a1.setId( 1 ); for ( int offset : new int[]{ 0, 10000 } ) {
a1.setLabel( "A1" ); AEntity a1 = new AEntity();
a1.setId( offset + 1 );
a1.setLabel( "A1" );
AEntity a2 = new AEntity(); AEntity a2 = new AEntity();
a2.setId( 2 ); a2.setId( offset + 2 );
a2.setLabel( "A2" ); a2.setLabel( "A2" );
entityManager.persist( a1 ); entityManager.persist( a1 );
entityManager.persist( a2 ); entityManager.persist( a2 );
BEntity b1 = new BEntity(); BEntity b1 = new BEntity();
b1.setId( 1 ); b1.setId( offset + 1 );
b1.setLabel( "B1" ); b1.setLabel( "B1" );
BEntity b2 = new BEntity(); BEntity b2 = new BEntity();
b2.setId( 2 ); b2.setId( offset + 2 );
b2.setLabel( "B2" ); b2.setLabel( "B2" );
entityManager.persist( b1 ); entityManager.persist( b1 );
entityManager.persist( b2 ); entityManager.persist( b2 );
EEntity e1 = new EEntity(); EEntity e1 = new EEntity();
e1.setId( 1 ); e1.setId( offset + 1 );
e1.setLabel( "E1" ); e1.setLabel( "E1" );
EEntity e2 = new EEntity(); EEntity e2 = new EEntity();
e2.setId( 2 ); e2.setId( offset + 2 );
e2.setLabel( "E2" ); e2.setLabel( "E2" );
EEntity e3 = new EEntity(); EEntity e3 = new EEntity();
e3.setId( 3 ); e3.setId( offset + 3 );
e3.setLabel( "E3" ); e3.setLabel( "E3" );
EEntity e4 = new EEntity(); EEntity e4 = new EEntity();
e4.setId( 4 ); e4.setId( offset + 4 );
e4.setLabel( "E4" ); e4.setLabel( "E4" );
entityManager.persist( e1 ); entityManager.persist( e1 );
entityManager.persist( e2 ); entityManager.persist( e2 );
entityManager.persist( e3 ); entityManager.persist( e3 );
entityManager.persist( e4 ); entityManager.persist( e4 );
DEntity d1 = new DEntity(); DEntity d1 = new DEntity();
d1.setId( 1 ); d1.setId( offset + 1 );
d1.setLabel( "D1" ); d1.setLabel( "D1" );
d1.setE( e1 ); d1.setE( e1 );
DEntity d2 = new DEntity(); DEntity d2 = new DEntity();
d2.setId( 2 ); d2.setId( offset + 2 );
d2.setLabel( "D2" ); d2.setLabel( "D2" );
d2.setE( e2 ); d2.setE( e2 );
CEntity c1 = new CEntity(); CEntity c1 = new CEntity();
c1.setId( 1 ); c1.setId( offset + 1 );
c1.setLabel( "C1" ); c1.setLabel( "C1" );
c1.setA( a1 ); c1.setA( a1 );
c1.setB( b1 ); c1.setB( b1 );
c1.addD( d1 ); c1.addD( d1 );
c1.addD( d2 ); c1.addD( d2 );
entityManager.persist( c1 ); entityManager.persist( c1 );
DEntity d3 = new DEntity(); DEntity d3 = new DEntity();
d3.setId( 3 ); d3.setId( offset + 3 );
d3.setLabel( "D3" ); d3.setLabel( "D3" );
d3.setE( e3 ); d3.setE( e3 );
DEntity d4 = new DEntity(); DEntity d4 = new DEntity();
d4.setId( 4 ); d4.setId( offset + 4 );
d4.setLabel( "D4" ); d4.setLabel( "D4" );
d4.setE( e4 ); d4.setE( e4 );
CEntity c2 = new CEntity(); CEntity c2 = new CEntity();
c2.setId( 2 ); c2.setId( offset + 2 );
c2.setLabel( "C2" ); c2.setLabel( "C2" );
c2.setA( a2 ); c2.setA( a2 );
c2.setB( b2 ); c2.setB( b2 );
c2.addD( d3 ); c2.addD( d3 );
c2.addD( d4 ); c2.addD( d4 );
entityManager.persist( c2 ); entityManager.persist( c2 );
CEntity c3 = new CEntity(); CEntity c3 = new CEntity();
c3.setId( 3 ); c3.setId( offset + 3 );
c3.setLabel( "C3" ); c3.setLabel( "C3" );
entityManager.persist( c3 ); entityManager.persist( c3 );
c1.setC( c2 ); CEntity c4 = new CEntity();
c2.setC( c3 ); c4.setId( offset + 4 );
c4.setLabel( "C4" );
int id = 5; entityManager.persist( c4 );
for ( int i = 0; i < 10; i++ ) {
DEntity dn = new DEntity();
dn.setId( id++ );
dn.setLabel( "label" );
dn.setE( e3 );
entityManager.persist( dn );
}
} ); c1.setC( c2 );
c2.setC( c3 );
c1.setEagerC( c4 );
int id = 5;
for ( int i = 0; i < 10; i++ ) {
DEntity dn = new DEntity();
dn.setId( offset + id++ );
dn.setLabel( "label" );
dn.setE( e3 );
entityManager.persist( dn );
}
}
} );
} }
@Test @Test
@ -284,10 +295,91 @@ public class LoadAndFetchGraphTest extends BaseEntityManagerFunctionalTestCase {
assertTrue( Hibernate.isInitialized( cEntity.getC().getA() ) ); assertTrue( Hibernate.isInitialized( cEntity.getC().getA() ) );
assertFalse( Hibernate.isInitialized( cEntity.getC().getC() ) ); assertFalse( Hibernate.isInitialized( cEntity.getC().getC() ) );
// With FETCH semantic, attributes that are not mentioned in the graph are LAZY,
// even if they were EAGER in the mapping.
assertFalse( Hibernate.isInitialized( cEntity.getEagerC() ) );
assertEquals( 1L, statistics.getPrepareStatementCount() ); assertEquals( 1L, statistics.getPrepareStatementCount() );
} ); } );
} }
@Test
@TestForIssue(jiraKey = "HHH-14124")
public void testQueryByIdWithLoadGraphMultipleResults() {
Statistics statistics = entityManagerFactory().unwrap( SessionFactory.class ).getStatistics();
statistics.clear();
doInJPA( this::entityManagerFactory, entityManager -> {
EntityGraph<CEntity> entityGraph = entityManager.createEntityGraph( CEntity.class );
entityGraph.addAttributeNodes( "a", "b" );
entityGraph.addSubgraph( "dList" ).addAttributeNodes( "e" );
TypedQuery<CEntity> query = entityManager.createQuery(
"select c from CEntity as c where c.id in :cid ",
CEntity.class
);
query.setHint( GraphSemantic.LOAD.getJpaHintName(), entityGraph );
query.setParameter( "cid", Arrays.asList( 1, 10001 ) );
List<CEntity> cEntityList = query.getResultList();
assertEquals( 2, cEntityList.size() );
for ( CEntity cEntity : cEntityList ) {
assertTrue( Hibernate.isInitialized( cEntity.getA() ) );
assertTrue( Hibernate.isInitialized( cEntity.getB() ) );
assertFalse( Hibernate.isInitialized( cEntity.getC() ) );
assertTrue( Hibernate.isInitialized( cEntity.getdList() ) );
cEntity.getdList().forEach( dEntity -> {
assertTrue( Hibernate.isInitialized( dEntity.getE() ) );
} );
// With LOAD semantic, attributes that are not mentioned in the graph are LAZY or EAGER,
// depending on the mapping.
assertTrue( Hibernate.isInitialized( cEntity.getEagerC() ) );
}
// 1 + 2 for the eager C
assertEquals( 3L, statistics.getPrepareStatementCount() );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-14124")
public void testQueryByIdWithFetchGraphMultipleResults() {
Statistics statistics = entityManagerFactory().unwrap( SessionFactory.class ).getStatistics();
statistics.clear();
doInJPA( this::entityManagerFactory, entityManager -> {
EntityGraph<CEntity> entityGraph = entityManager.createEntityGraph( CEntity.class );
entityGraph.addAttributeNodes( "a", "b" );
entityGraph.addSubgraph( "dList" ).addAttributeNodes( "e" );
TypedQuery<CEntity> query = entityManager.createQuery(
"select c from CEntity as c where c.id in :cid ",
CEntity.class
);
query.setHint( GraphSemantic.FETCH.getJpaHintName(), entityGraph );
query.setParameter( "cid", Arrays.asList( 1, 10001 ) );
List<CEntity> cEntityList = query.getResultList();
assertEquals( 2, cEntityList.size() );
for ( CEntity cEntity : cEntityList ) {
assertTrue( Hibernate.isInitialized( cEntity.getA() ) );
assertTrue( Hibernate.isInitialized( cEntity.getB() ) );
assertFalse( Hibernate.isInitialized( cEntity.getC() ) );
assertTrue( Hibernate.isInitialized( cEntity.getdList() ) );
cEntity.getdList().forEach( dEntity -> {
assertTrue( Hibernate.isInitialized( dEntity.getE() ) );
} );
// With FETCH semantic, attributes that are not mentioned in the graph are LAZY,
// even if they were EAGER in the mapping.
assertFalse( Hibernate.isInitialized( cEntity.getEagerC() ) );
}
assertEquals( 1L, statistics.getPrepareStatementCount() );
} );
}
@Entity(name = "AEntity") @Entity(name = "AEntity")
@Table(name = "A") @Table(name = "A")
public static class AEntity { public static class AEntity {