HHH-16043 Add test for issue
This commit is contained in:
parent
2d7774b9ab
commit
17506b7f80
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* 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.batchfetch;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OrderBy;
|
||||
|
||||
import static jakarta.persistence.CascadeType.ALL;
|
||||
import static jakarta.persistence.FetchType.LAZY;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
@DomainModel(
|
||||
annotatedClasses = {
|
||||
NestedLazyManyToOneTest.AbstractEntity.class,
|
||||
NestedLazyManyToOneTest.Entity1.class,
|
||||
NestedLazyManyToOneTest.Entity2.class,
|
||||
NestedLazyManyToOneTest.Entity3.class
|
||||
}
|
||||
)
|
||||
@SessionFactory(useCollectingStatementInspector = true)
|
||||
@ServiceRegistry(settings = {
|
||||
@Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "5")
|
||||
})
|
||||
@JiraKey("HHH-16043")
|
||||
public class NestedLazyManyToOneTest {
|
||||
@BeforeAll
|
||||
public void prepareData(SessionFactoryScope scope) {
|
||||
final Entity1 entity1 = new Entity1();
|
||||
entity1.setId( "0" );
|
||||
|
||||
final Set<Entity2> entities2 = new HashSet<>();
|
||||
for ( int i = 0; i < 8; i++ ) {
|
||||
final Entity2 entity2 = new Entity2();
|
||||
entity2.setId( entity1.getId() + "_" + i );
|
||||
entity2.setParent( entity1 );
|
||||
entities2.add( entity2 );
|
||||
|
||||
// add nested children only to first and last entity
|
||||
if ( i == 0 || i == 7 ) {
|
||||
final Set<Entity3> entities3 = new HashSet<>();
|
||||
for ( int j = 0; j < 5; j++ ) {
|
||||
final Entity3 entity3 = new Entity3();
|
||||
entity3.setId( entity2.getId() + "_" + j );
|
||||
entity3.setParent( entity2 );
|
||||
entities3.add( entity3 );
|
||||
}
|
||||
entity2.setChildren( entities3 );
|
||||
}
|
||||
}
|
||||
entity1.setChildren( entities2 );
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
session.persist( entity1 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFirstLevelChildren(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction( session -> {
|
||||
Entity1 fromDb = session.find( Entity1.class, "0" );
|
||||
Set<Entity2> children = fromDb.getChildren();
|
||||
|
||||
assertEquals( 8, children.size() );
|
||||
statementInspector.assertExecutedCount( 2 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 1, "\\?", 1 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNestedChildrenLessThanBatchSize(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
Entity1 entity1 = session.find( Entity1.class, "0" );
|
||||
int i = 0;
|
||||
for ( Entity2 child2 : entity1.getChildren() ) {
|
||||
// get only first 5 (< batch size) elements
|
||||
// this doesn't trigger an additional query only because entity1.children
|
||||
// are ordered with @OrderBy, and we always get the first 5 first
|
||||
if ( i++ >= 5 ) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
Set<Entity3> children3 = child2.getChildren();
|
||||
if ( child2.getId().equals( "0_0" ) ) {
|
||||
assertEquals( 5, children3.size() );
|
||||
}
|
||||
else {
|
||||
assertEquals( 0, children3.size() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals( 8, entity1.getChildren().size() );
|
||||
statementInspector.assertExecutedCount( 3 ); // 1 for Entity1, 1 for Entity2, 1 for Entity3
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 2, "\\?", 5 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNestedChildrenMoreThanBatchSize(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
Entity1 entity1 = session.find( Entity1.class, "0" );
|
||||
for ( Entity2 child2 : entity1.getChildren() ) {
|
||||
Set<Entity3> children3 = child2.getChildren();
|
||||
if ( child2.getId().equals( "0_0" ) || child2.getId().equals( "0_7" ) ) {
|
||||
assertEquals( 5, children3.size() );
|
||||
}
|
||||
else {
|
||||
assertEquals( 0, children3.size() );
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals( 8, entity1.getChildren().size() );
|
||||
statementInspector.assertExecutedCount( 4 ); // 1 for Entity1, 1 for Entity2, 2 for Entity3
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 2, "\\?", 5 );
|
||||
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 3, "\\?", 3 );
|
||||
} );
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static class AbstractEntity {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "#" + getId();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Entity(name = "Entity1")
|
||||
public static class Entity1 extends AbstractEntity {
|
||||
@OneToMany(mappedBy = "parent", cascade = ALL, orphanRemoval = true)
|
||||
@OrderBy("id")
|
||||
private Set<Entity2> children = new HashSet<>();
|
||||
|
||||
public Set<Entity2> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(Set<Entity2> children) {
|
||||
this.children = children;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Entity2")
|
||||
public static class Entity2 extends AbstractEntity {
|
||||
@ManyToOne(fetch = LAZY)
|
||||
private Entity1 parent;
|
||||
|
||||
@OneToMany(mappedBy = "parent", cascade = ALL, orphanRemoval = true)
|
||||
private Set<Entity3> children = new HashSet<>();
|
||||
|
||||
public Entity1 getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(Entity1 parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Set<Entity3> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(Set<Entity3> children) {
|
||||
this.children = children;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Entity3")
|
||||
public static class Entity3 extends AbstractEntity {
|
||||
@ManyToOne(fetch = LAZY)
|
||||
private Entity2 parent;
|
||||
|
||||
public Entity2 getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(Entity2 parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -93,8 +93,12 @@ public class SQLStatementInspector implements StatementInspector {
|
|||
}
|
||||
|
||||
public void assertNumberOfOccurrenceInQuery(int queryNumber, String toCheck, int expectedNumberOfOccurrences) {
|
||||
assertNumberOfOccurrenceInQueryNoSpace( queryNumber, " " + toCheck + " ", expectedNumberOfOccurrences );
|
||||
}
|
||||
|
||||
public void assertNumberOfOccurrenceInQueryNoSpace(int queryNumber, String toCheck, int expectedNumberOfOccurrences) {
|
||||
String query = sqlQueries.get( queryNumber );
|
||||
int actual = query.split( " " + toCheck + " ", -1 ).length - 1;
|
||||
int actual = query.split( toCheck, -1 ).length - 1;
|
||||
assertThat( "number of " + toCheck, actual, is( expectedNumberOfOccurrences ) );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue