diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java index 959e3e9e02..8c62bf4436 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java @@ -157,6 +157,9 @@ public class SubselectFetch { } public void addKey(EntityKey key, LoadingEntityEntry entry) { + if ( !entry.getDescriptor().hasSubselectLoadableCollections() ) { + return; + } final SubselectFetch subselectFetch = subselectFetches.computeIfAbsent( entry.getEntityInitializer().getNavigablePath(), navigablePath -> new SubselectFetch( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/SubselectFetchTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/SubselectFetchTest.java new file mode 100644 index 0000000000..18e31f21a4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/SubselectFetchTest.java @@ -0,0 +1,128 @@ +/* + * 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.engine.spi.BatchFetchQueue; +import org.hibernate.persister.entity.EntityPersister; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +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.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel( + annotatedClasses = { + SubselectFetchTest.Employee.class, + SubselectFetchTest.Task.class + } +) +@SessionFactory +public class SubselectFetchTest { + private static final int NUMBER_OF_EMPLOYEES = 8; + + @BeforeEach + public void createData(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + for ( int i = 0; i < NUMBER_OF_EMPLOYEES; i++ ) { + Task mainTask = new Task(); + mainTask.id = 100 + i; + session.persist( mainTask ); + Task task = new Task(); + task.id = i; + task.parentTask = mainTask; + session.persist( task ); + Employee e = new Employee( "employee0" + i ); + e.task = task; + session.persist( e ); + } + } + ); + } + + @AfterEach + public void deleteData(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery( "delete from Employee" ).executeUpdate(); + session.createQuery( "delete from Task where parentTask is not null" ).executeUpdate(); + session.createQuery( "delete from Task" ).executeUpdate(); + } + ); + } + + @Test + @TestForIssue( jiraKey = "HHH-15202") + public void testSubselectFetchOnlyCreatedIfEnabled(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + EntityPersister employeePersister = session.getSessionFactory() + .getRuntimeMetamodels() + .getMappingMetamodel() + .getEntityDescriptor( Employee.class ); + EntityPersister taskPersister = session.getSessionFactory() + .getRuntimeMetamodels() + .getMappingMetamodel() + .getEntityDescriptor( Task.class ); + BatchFetchQueue batchFetchQueue = session.getPersistenceContextInternal().getBatchFetchQueue(); + List results = session.createQuery( "from Employee e order by e.id", Employee.class ).getResultList(); + for ( Employee result : results ) { + assertThat( batchFetchQueue.getSubselect( session.generateEntityKey( result.name, employeePersister ) ) ).isNull(); + Task task = session.createQuery( "from Task t where t.employee = :e", Task.class ) + .setParameter( "e", result ) + .getSingleResult(); + assertThat( batchFetchQueue.getSubselect( session.generateEntityKey( task.id, taskPersister ) ) ).isNull(); + } + } + ); + } + + @Entity(name = "Employee") + public static class Employee { + @Id + private String name; + + @OneToOne(fetch = FetchType.LAZY) + private Task task; + + private Employee() { + } + + private Employee(String name) { + this.name = name; + } + } + + @Entity(name = "Task") + public static class Task { + @Id + private long id; + + @ManyToOne(fetch = FetchType.LAZY) + private Task parentTask; + + @OneToOne(fetch = FetchType.LAZY, mappedBy = "task") + private Employee employee; + + public Task() { + } + } + +}