HHH-16657 Propagate PROJECTION entity name use from subquery to outer table group

This commit is contained in:
Christian Beikov 2023-05-21 14:19:10 +02:00
parent 7f5ebc207e
commit 4795b94f68
3 changed files with 88 additions and 3 deletions

View File

@ -2973,14 +2973,20 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return; return;
} }
final TableGroup actualTableGroup; final TableGroup actualTableGroup;
final EntityNameUse finalEntityNameUse;
if ( tableGroup instanceof PluralTableGroup ) { if ( tableGroup instanceof PluralTableGroup ) {
actualTableGroup = ( (PluralTableGroup) tableGroup ).getElementTableGroup(); actualTableGroup = ( (PluralTableGroup) tableGroup ).getElementTableGroup();
finalEntityNameUse = entityNameUse;
} }
else if ( tableGroup instanceof CorrelatedTableGroup ) { else if ( tableGroup instanceof CorrelatedTableGroup ) {
actualTableGroup = ( (CorrelatedTableGroup) tableGroup ).getCorrelatedTableGroup(); 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 { else {
actualTableGroup = tableGroup; actualTableGroup = tableGroup;
finalEntityNameUse = entityNameUse;
} }
final Map<String, EntityNameUse> entityNameUses = tableGroupEntityNameUses.computeIfAbsent( final Map<String, EntityNameUse> entityNameUses = tableGroupEntityNameUses.computeIfAbsent(
actualTableGroup, actualTableGroup,
@ -2988,13 +2994,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
); );
entityNameUses.compute( entityNameUses.compute(
hibernateEntityName, 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 // Resolve the table reference for all types which we register an entity name use for
actualTableGroup.resolveTableReference( null, persister.getTableName() ); actualTableGroup.resolveTableReference( null, persister.getTableName() );
if ( entityNameUse == EntityNameUse.PROJECTION ) { if ( finalEntityNameUse == EntityNameUse.PROJECTION ) {
// For projections also register uses of all super and subtypes, // For projections also register uses of all super and subtypes,
// as well as resolve the respective table references // as well as resolve the respective table references
EntityMappingType superMappingType = persister; EntityMappingType superMappingType = persister;
@ -3014,7 +3020,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
actualTableGroup.resolveTableReference( null, persister.getSubclassTableName( i ) ); 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. // 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 // We do this here to not have to expand entity name uses during pruning later on
for ( EntityMappingType subType : persister.getSubMappingTypes() ) { for ( EntityMappingType subType : persister.getSubMappingTypes() ) {

View File

@ -15,19 +15,27 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.Root; import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import jakarta.persistence.metamodel.EntityType; import jakarta.persistence.metamodel.EntityType;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.hibernate.orm.test.jpa.metamodel.Thing; 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.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.hibernate.testing.TestForIssue;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -37,6 +45,8 @@ public class TreatKeywordTest extends BaseEntityManagerFunctionalTestCase {
@Override @Override
protected Class<?>[] getAnnotatedClasses() { protected Class<?>[] getAnnotatedClasses() {
return new Class[] { return new Class[] {
JoinedEntity.class, JoinedEntitySubclass.class, JoinedEntitySubSubclass.class,
JoinedEntitySubclass2.class, JoinedEntitySubSubclass2.class,
Animal.class, Elephant.class, Human.class, Thing.class, ThingWithQuantity.class, Animal.class, Elephant.class, Human.class, Thing.class, ThingWithQuantity.class,
TreatAnimal.class, Dog.class, Dachshund.class, Greyhound.class TreatAnimal.class, Dog.class, Dachshund.class, Greyhound.class
}; };
@ -254,6 +264,46 @@ public class TreatKeywordTest extends BaseEntityManagerFunctionalTestCase {
em.close(); 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<String> query = cb.createQuery( String.class );
Root<JoinedEntitySubclass> root = query.from( JoinedEntitySubclass.class );
query.orderBy( cb.asc( root.get( "id" ) ) );
Subquery<String> subquery = query.subquery( String.class );
Root<JoinedEntitySubclass> subqueryRoot = subquery.correlate( root );
Join<Object, Object> other = subqueryRoot.join( "other" );
subquery.select( other.get( "name" ) );
subquery.where( cb.equal( root.type(), cb.literal( JoinedEntitySubclass.class ) ) );
query.select( subquery );
List<String> 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") @Entity(name = "TreatAnimal")
public static abstract class TreatAnimal { public static abstract class TreatAnimal {
@Id @Id

View File

@ -30,6 +30,7 @@ import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
@ -208,6 +209,34 @@ public class TreatKeywordTest extends BaseCoreFunctionalTestCase {
s.close(); 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<String> 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" ) @Entity( name = "JoinedEntity" )
@Table( name = "JoinedEntity" ) @Table( name = "JoinedEntity" )
@Inheritance( strategy = InheritanceType.JOINED ) @Inheritance( strategy = InheritanceType.JOINED )