diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 240df2dd7b..2b95e30f0a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -2975,14 +2975,20 @@ public abstract class BaseSqmToSqlAstConverter extends Base return; } final TableGroup actualTableGroup; + final EntityNameUse finalEntityNameUse; if ( tableGroup instanceof PluralTableGroup ) { actualTableGroup = ( (PluralTableGroup) tableGroup ).getElementTableGroup(); + finalEntityNameUse = entityNameUse; } else if ( tableGroup instanceof CorrelatedTableGroup ) { actualTableGroup = ( (CorrelatedTableGroup) tableGroup ).getCorrelatedTableGroup(); + // For correlated table groups we can't apply filters, + // as the context is in which the use happens may only affect the result of the subquery + finalEntityNameUse = entityNameUse == EntityNameUse.EXPRESSION ? entityNameUse : EntityNameUse.PROJECTION; } else { actualTableGroup = tableGroup; + finalEntityNameUse = entityNameUse; } final Map entityNameUses = tableGroupEntityNameUses.computeIfAbsent( actualTableGroup, @@ -2990,13 +2996,13 @@ public abstract class BaseSqmToSqlAstConverter extends Base ); entityNameUses.compute( hibernateEntityName, - (s, existingUse) -> entityNameUse.stronger( existingUse ) + (s, existingUse) -> finalEntityNameUse.stronger( existingUse ) ); // Resolve the table reference for all types which we register an entity name use for actualTableGroup.resolveTableReference( null, persister.getTableName() ); - if ( entityNameUse == EntityNameUse.PROJECTION ) { + if ( finalEntityNameUse == EntityNameUse.PROJECTION ) { // For projections also register uses of all super and subtypes, // as well as resolve the respective table references EntityMappingType superMappingType = persister; @@ -3016,7 +3022,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base actualTableGroup.resolveTableReference( null, persister.getSubclassTableName( i ) ); } } - else if ( entityNameUse == EntityNameUse.TREAT ) { + else if ( finalEntityNameUse == EntityNameUse.TREAT ) { // If we encounter a treat use, we also want register the use for all subtypes. // We do this here to not have to expand entity name uses during pruning later on for ( EntityMappingType subType : persister.getSubMappingTypes() ) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/TreatKeywordTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/TreatKeywordTest.java index 51e8c3e4bb..661e80b77e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/TreatKeywordTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/TreatKeywordTest.java @@ -15,19 +15,27 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.Subquery; import jakarta.persistence.metamodel.EntityType; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; import org.hibernate.orm.test.jpa.metamodel.Thing; import org.hibernate.orm.test.jpa.metamodel.ThingWithQuantity; import org.hibernate.orm.test.jpa.metamodel.ThingWithQuantity_; +import org.hibernate.orm.test.jpa.ql.TreatKeywordTest.JoinedEntity; +import org.hibernate.orm.test.jpa.ql.TreatKeywordTest.JoinedEntitySubSubclass; +import org.hibernate.orm.test.jpa.ql.TreatKeywordTest.JoinedEntitySubSubclass2; +import org.hibernate.orm.test.jpa.ql.TreatKeywordTest.JoinedEntitySubclass; +import org.hibernate.orm.test.jpa.ql.TreatKeywordTest.JoinedEntitySubclass2; import org.hibernate.testing.TestForIssue; import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; /** * @author Steve Ebersole @@ -37,6 +45,8 @@ public class TreatKeywordTest extends BaseEntityManagerFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { return new Class[] { + JoinedEntity.class, JoinedEntitySubclass.class, JoinedEntitySubSubclass.class, + JoinedEntitySubclass2.class, JoinedEntitySubSubclass2.class, Animal.class, Elephant.class, Human.class, Thing.class, ThingWithQuantity.class, TreatAnimal.class, Dog.class, Dachshund.class, Greyhound.class }; @@ -254,6 +264,46 @@ public class TreatKeywordTest extends BaseEntityManagerFunctionalTestCase { em.close(); } + @Test + @TestForIssue(jiraKey = "HHH-16657") + public void testTypeFilterInSubquery() { + EntityManager em = getOrCreateEntityManager(); + EntityTransaction entityTransaction = em.getTransaction(); + entityTransaction.begin(); + + JoinedEntitySubclass2 child1 = new JoinedEntitySubclass2( 3, "child1"); + JoinedEntitySubSubclass2 child2 = new JoinedEntitySubSubclass2( 4, "child2"); + JoinedEntitySubclass root1 = new JoinedEntitySubclass( 1, "root1", child1); + JoinedEntitySubSubclass root2 = new JoinedEntitySubSubclass( 2, "root2", child2); + em.persist( child1 ); + em.persist( child2 ); + em.persist( root1 ); + em.persist( root2 ); + + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery( String.class ); + Root root = query.from( JoinedEntitySubclass.class ); + query.orderBy( cb.asc( root.get( "id" ) ) ); + Subquery subquery = query.subquery( String.class ); + Root subqueryRoot = subquery.correlate( root ); + Join other = subqueryRoot.join( "other" ); + subquery.select( other.get( "name" ) ); + subquery.where( cb.equal( root.type(), cb.literal( JoinedEntitySubclass.class ) ) ); + query.select( subquery ); + + List results = em.createQuery( + "select (select o.name from j.other o where type(j) = JoinedEntitySubSubclass) from JoinedEntitySubclass j order by j.id", + String.class + ).getResultList(); + + assertEquals( 2, results.size() ); + assertNull( results.get( 0 ) ); + assertEquals( "child2", results.get( 1 ) ); + + entityTransaction.commit(); + em.close(); + } + @Entity(name = "TreatAnimal") public static abstract class TreatAnimal { @Id diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/TreatKeywordTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/TreatKeywordTest.java index d48fc2b471..bff76991e1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/TreatKeywordTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/TreatKeywordTest.java @@ -30,6 +30,7 @@ import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** @@ -208,6 +209,34 @@ public class TreatKeywordTest extends BaseCoreFunctionalTestCase { s.close(); } + @Test + @TestForIssue(jiraKey = "HHH-16657") + public void testTypeFilterInSubquery() { + Session s = openSession(); + Transaction tx = s.beginTransaction(); + + JoinedEntitySubclass2 child1 = new JoinedEntitySubclass2(3, "child1"); + JoinedEntitySubSubclass2 child2 = new JoinedEntitySubSubclass2(4, "child2"); + JoinedEntitySubclass root1 = new JoinedEntitySubclass(1, "root1", child1); + JoinedEntitySubSubclass root2 = new JoinedEntitySubSubclass(2, "root2", child2); + s.persist( child1 ); + s.persist( child2 ); + s.persist( root1 ); + s.persist( root2 ); + + List results = s.createSelectionQuery( + "select (select o.name from j.other o where type(j) = JoinedEntitySubSubclass) from JoinedEntitySubclass j order by j.id", + String.class + ).list(); + + assertEquals( 2, results.size() ); + assertNull( results.get( 0 ) ); + assertEquals( "child2", results.get( 1 ) ); + + tx.commit(); + s.close(); + } + @Entity( name = "JoinedEntity" ) @Table( name = "JoinedEntity" ) @Inheritance( strategy = InheritanceType.JOINED )