diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/EagerManyToOneStreamTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/EagerManyToOneStreamTest.java new file mode 100644 index 0000000000..30af36fd78 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/EagerManyToOneStreamTest.java @@ -0,0 +1,220 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.batchfetch; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hibernate.Hibernate; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.query.spi.QueryImplementor; + +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.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { EagerManyToOneStreamTest.Child.class, EagerManyToOneStreamTest.Parent.class } +) +@SessionFactory(statementInspectorClass = SQLStatementInspector.class) +@ServiceRegistry(settings = @Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "2")) +@JiraKey("HHH-15449") +public class EagerManyToOneStreamTest { + + public static final String FIELD_VALUE = "a field"; + public static final String FIELD_VALUE_2 = "a second field"; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( FIELD_VALUE ); + session.persist( parent ); + session.persist( new Child( parent ) ); + Parent parent2 = new Parent( FIELD_VALUE_2 ); + session.persist( parent2 ); + session.persist( new Child( parent2 ) ); + } + ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Child" ).executeUpdate(); + session.createMutationQuery( "delete from Parent" ).executeUpdate(); + } + ); + } + + @Test + public void testGetResultStreamCollectSingleResult(SessionFactoryScope scope) { + final SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector(); + sqlStatementInterceptor.clear(); + scope.inTransaction( + session -> { + QueryImplementor query = session + .createQuery( "select c from Child as c where c.parent.someField=:someField", Child.class ) + .setParameter( "someField", FIELD_VALUE ); + Stream resultStream = query.getResultStream(); + + List children = resultStream.collect( Collectors.toList() ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + + assertThat( children.size() ).isEqualTo( 1 ); + + Parent parent = children.get( 0 ).getParent(); + assertThat( parent ).isNotNull(); + + assertThat( Hibernate.isInitialized( parent ) ).isTrue(); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + + assertThat( parent.getSomeField() ).isEqualTo( FIELD_VALUE ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + } + ); + } + + @Test + public void testGetResultStreamCollect(SessionFactoryScope scope) { + final SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector(); + sqlStatementInterceptor.clear(); + scope.inTransaction( + session -> { + QueryImplementor query = session + .createQuery( "select c from Child as c ", Child.class ); + Stream resultStream = query.getResultStream(); + + List children = resultStream.collect( Collectors.toList() ); + // with Stream the association is not batch loaded + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 3 ); + + assertThat( children.size() ).isEqualTo( 2 ); + + Parent parent = children.get( 0 ).getParent(); + assertThat( parent ).isNotNull(); + assertThat( Hibernate.isInitialized( parent ) ).isTrue(); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 3 ); + + assertThat( parent.getSomeField() ).isEqualTo( FIELD_VALUE ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 3 ); + + Parent parent1 = children.get( 1 ).getParent(); + assertThat( parent1 ).isNotNull(); + assertThat( Hibernate.isInitialized( parent1 ) ).isTrue(); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 3 ); + + assertThat( parent1.getSomeField() ).isEqualTo( FIELD_VALUE_2 ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 3 ); + } + ); + } + + @Test + public void testGetResultStreamForEach(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + QueryImplementor query = session + .createQuery( "select c from Child as c", Child.class ); + + query.getResultStream().forEach( + child -> assertThat( child.getParent() ).isNotNull() + ); + } + ); + } + + @Test + public void testGetResultStreamFindFirst(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + QueryImplementor query = session + .createQuery( "select c from Child as c where c.parent.someField=:someField", Child.class ) + .setParameter( "someField", FIELD_VALUE ); + Stream resultStream = query.getResultStream(); + Optional child = resultStream.findFirst(); + assertThat( child.isEmpty() ).isFalse(); + assertThat( child.get().getParent() ).isNotNull(); + } + ); + } + + @Entity(name = "Child") + @Table(name = "CHILD_TABLE") + public static class Child { + + @Id + @GeneratedValue + private Long id; + + @ManyToOne + @JoinColumn(name = "parent_id", nullable = false, updatable = false) + private Parent parent; + + public Child() { + } + + public Child(Parent parent) { + this.parent = parent; + } + + public Parent getParent() { + return parent; + } + + public Long getId() { + return id; + } + } + + @Entity(name = "Parent") + @Table(name = "PARENT_TABLE") + public static class Parent { + @Id + @GeneratedValue + private Long id; + + private String someField; + + public Parent() { + } + + public Parent(String someField) { + this.someField = someField; + } + + public String getSomeField() { + return someField; + } + + public Long getId() { + return id; + } + + } + + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/EagerManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/EagerManyToOneTest.java new file mode 100644 index 0000000000..758b8c563d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/EagerManyToOneTest.java @@ -0,0 +1,157 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.batchfetch; + +import java.util.List; + +import org.hibernate.Hibernate; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.query.Query; + +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.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { EagerManyToOneTest.Child.class, EagerManyToOneTest.Parent.class } +) +@SessionFactory(statementInspectorClass = SQLStatementInspector.class) +@ServiceRegistry(settings = @Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "2")) +@JiraKey("HHH-15449") +public class EagerManyToOneTest { + + public static final String FIELD_VALUE = "a field"; + public static final String FIELD_VALUE_2 = "a second field"; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( FIELD_VALUE ); + session.persist( parent ); + session.persist( new Child( parent ) ); + Parent parent2 = new Parent( FIELD_VALUE_2 ); + session.persist( parent2 ); + session.persist( new Child( parent2 ) ); + } + ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Child" ).executeUpdate(); + session.createMutationQuery( "delete from Parent" ).executeUpdate(); + } + ); + } + + @Test + public void testGetResultList(SessionFactoryScope scope) { + final SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector(); + sqlStatementInterceptor.clear(); + scope.inTransaction( + session -> { + Query query = session + .createQuery( "select c from Child as c ", Child.class ); + List resultList = query.getResultList(); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + + + Parent parent = resultList.get( 0 ).getParent(); + assertThat( parent ).isNotNull(); + Parent parent1 = resultList.get( 1 ).getParent(); + assertThat( parent1 ).isNotNull(); + + assertThat( Hibernate.isInitialized( parent ) ).isTrue(); + assertThat( Hibernate.isInitialized( parent1 ) ).isTrue(); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + + + assertThat( parent.getSomeField() ).isEqualTo( FIELD_VALUE ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + + assertThat( parent1.getSomeField() ).isEqualTo( FIELD_VALUE_2 ); + // parent2 has been batch loaded + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + } + ); + } + + @Entity(name = "Child") + @Table(name = "CHILD_TABLE") + public static class Child { + + @Id + @GeneratedValue + private Long id; + + @ManyToOne + @JoinColumn(name = "parent_id", nullable = false, updatable = false) + private Parent parent; + + public Child() { + } + + public Child(Parent parent) { + this.parent = parent; + } + + public Parent getParent() { + return parent; + } + + public Long getId() { + return id; + } + } + + @Entity(name = "Parent") + @Table(name = "PARENT_TABLE") + public static class Parent { + @Id + @GeneratedValue + private Long id; + + private String someField; + + public Parent() { + } + + public Parent(String someField) { + this.someField = someField; + } + + public String getSomeField() { + return someField; + } + + public Long getId() { + return id; + } + + } + + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/LazyManyToOneStreamTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/LazyManyToOneStreamTest.java new file mode 100644 index 0000000000..3b46540fc3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/LazyManyToOneStreamTest.java @@ -0,0 +1,211 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.batchfetch; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hibernate.Hibernate; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.query.spi.QueryImplementor; + +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.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { LazyManyToOneStreamTest.Child.class, LazyManyToOneStreamTest.Parent.class } +) +@SessionFactory( statementInspectorClass = SQLStatementInspector.class) +@ServiceRegistry(settings = @Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "2")) +@JiraKey("HHH-15449") +public class LazyManyToOneStreamTest { + + public static final String FIELD_VALUE = "a field"; + public static final String FIELD_VALUE_2 = "a second field"; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( FIELD_VALUE ); + session.persist( parent ); + session.persist( new Child( parent ) ); + Parent parent2 = new Parent( FIELD_VALUE_2 ); + session.persist( parent2 ); + session.persist( new Child( parent2 ) ); + } + ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Child" ).executeUpdate(); + session.createMutationQuery( "delete from Parent" ).executeUpdate(); + } + ); + } + + @Test + public void testGetResultStreamCollectSingleResult(SessionFactoryScope scope) { + final SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector(); + sqlStatementInterceptor.clear(); + scope.inTransaction( + session -> { + QueryImplementor query = session + .createQuery( "select c from Child as c where c.parent.someField=:someField", Child.class ) + .setParameter( "someField", FIELD_VALUE ); + Stream resultStream = query.getResultStream(); + + List children = resultStream.collect( Collectors.toList() ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 1 ); + assertThat( children.size() ).isEqualTo( 1 ); + + Parent parent = children.get( 0 ).getParent(); + assertThat( parent ).isNotNull(); + + assertThat( Hibernate.isInitialized( parent ) ).isFalse(); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 1 ); + + assertThat( parent.getSomeField() ).isEqualTo( FIELD_VALUE ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + } + ); + } + + @Test + public void testGetResultStreamCollect(SessionFactoryScope scope) { + final SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector(); + sqlStatementInterceptor.clear(); + scope.inTransaction( + session -> { + QueryImplementor query = session + .createQuery( "select c from Child as c order by id", Child.class ); + Stream resultStream = query.getResultStream(); + + List children = resultStream.collect( Collectors.toList() ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 1 ); + + assertThat( children.size() ).isEqualTo( 2 ); + + Parent parent = children.get( 0 ).getParent(); + assertThat( parent ).isNotNull(); + assertThat( Hibernate.isInitialized( parent ) ).isFalse(); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 1 ); + + assertThat( parent.getSomeField() ).isEqualTo( FIELD_VALUE ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + + Parent parent1 = children.get( 1 ).getParent(); + assertThat( parent1 ).isNotNull(); + // parent2 has been batch loaded + assertThat( Hibernate.isInitialized( parent1 ) ).isTrue(); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + + assertThat( parent1.getSomeField() ).isEqualTo( FIELD_VALUE_2 ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + } + ); + } + + @Test + public void testGetResultStreamFindFirst(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + QueryImplementor query = session + .createQuery( "select c from Child as c where c.parent.someField=:someField", Child.class ) + .setParameter( "someField", FIELD_VALUE ); + Stream resultStream = query.getResultStream(); + + Optional child = resultStream.findFirst(); + assertThat( child.isEmpty() ).isFalse(); + + Parent parent = child.get().getParent(); + assertThat( parent ).isNotNull(); + + assertThat( Hibernate.isInitialized( parent ) ).isFalse(); + } + ); + } + + @Entity(name = "Child") + @Table(name = "CHILD_TABLE") + public static class Child { + + @Id + @GeneratedValue + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parent_id", nullable = false, updatable = false) + private Parent parent; + + public Child() { + } + + public Child(Parent parent) { + this.parent = parent; + } + + public Parent getParent() { + return parent; + } + + public Long getId() { + return id; + } + } + + @Entity(name = "Parent") + @Table(name = "PARENT_TABLE") + public static class Parent { + @Id + @GeneratedValue + private Long id; + + private String someField; + + public Parent() { + } + + public Parent(String someField) { + this.someField = someField; + } + + public String getSomeField() { + return someField; + } + + public Long getId() { + return id; + } + + } + + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/LazyManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/LazyManyToOneTest.java new file mode 100644 index 0000000000..6cb36cb6da --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/LazyManyToOneTest.java @@ -0,0 +1,157 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.batchfetch; + +import java.util.List; + +import org.hibernate.Hibernate; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.query.Query; + +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.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { LazyManyToOneTest.Child.class, LazyManyToOneTest.Parent.class } +) +@SessionFactory(statementInspectorClass = SQLStatementInspector.class) +@ServiceRegistry(settings = @Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "2")) +@JiraKey("HHH-15449") +public class LazyManyToOneTest { + + public static final String FIELD_VALUE = "a field"; + public static final String FIELD_VALUE_2 = "a second field"; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( FIELD_VALUE ); + session.persist( parent ); + session.persist( new Child( parent ) ); + Parent parent2 = new Parent( FIELD_VALUE_2 ); + session.persist( parent2 ); + session.persist( new Child( parent2 ) ); + } + ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Child" ).executeUpdate(); + session.createMutationQuery( "delete from Parent" ).executeUpdate(); + } + ); + } + + @Test + public void testGetResultList(SessionFactoryScope scope) { + final SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector(); + sqlStatementInterceptor.clear(); + scope.inTransaction( + session -> { + Query query = session + .createQuery( "select c from Child as c ", Child.class ); + List resultList = query.getResultList(); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 1 ); + + + Parent parent = resultList.get( 0 ).getParent(); + assertThat( parent ).isNotNull(); + Parent parent1 = resultList.get( 1 ).getParent(); + assertThat( parent1 ).isNotNull(); + assertThat( Hibernate.isInitialized( parent ) ).isFalse(); + assertThat( Hibernate.isInitialized( parent1 ) ).isFalse(); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 1 ); + + + assertThat( parent.getSomeField() ).isEqualTo( FIELD_VALUE ); + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + + assertThat( parent1.getSomeField() ).isEqualTo( FIELD_VALUE_2 ); + // parent2 has been batch loaded + assertThat( sqlStatementInterceptor.getSqlQueries().size() ).isEqualTo( 2 ); + } + ); + } + + @Entity(name = "Child") + @Table(name = "CHILD_TABLE") + public static class Child { + + @Id + @GeneratedValue + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parent_id", nullable = false, updatable = false) + private Parent parent; + + public Child() { + } + + public Child(Parent parent) { + this.parent = parent; + } + + public Parent getParent() { + return parent; + } + + public Long getId() { + return id; + } + } + + @Entity(name = "Parent") + @Table(name = "PARENT_TABLE") + public static class Parent { + @Id + @GeneratedValue + private Long id; + + private String someField; + + public Parent() { + } + + public Parent(String someField) { + this.someField = someField; + } + + public String getSomeField() { + return someField; + } + + public Long getId() { + return id; + } + + } + + +}