-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
241 additions
and
1 deletion.
There are no files selected for viewing
236 changes: 236 additions & 0 deletions
236
hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/NestedLazyManyToOneTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters