From e17590a18f42c6bac0d1d1ee1d0aca27a6a95c8d Mon Sep 17 00:00:00 2001 From: The-Huginn Date: Tue, 26 Sep 2023 13:38:05 +0200 Subject: [PATCH] HHH-17192 Register entity name usage for entity graph/fetch profile related join fetches --- .../sqm/sql/BaseSqmToSqlAstConverter.java | 109 ++++++++++-------- .../orm/test/jpa/graphs/EntityGraphTest.java | 45 +++++++- 2 files changed, 107 insertions(+), 47 deletions(-) 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 489e162336..b16705fdb8 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 @@ -6,35 +6,15 @@ */ package org.hibernate.query.sqm.sql; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - +import jakarta.persistence.TemporalType; +import jakarta.persistence.metamodel.SingularAttribute; +import jakarta.persistence.metamodel.Type; import org.hibernate.HibernateException; import org.hibernate.Internal; import org.hibernate.LockMode; import org.hibernate.boot.model.process.internal.InferredBasicValueResolver; -import org.hibernate.dialect.DmlTargetColumnQualifierSupport; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.DmlTargetColumnQualifierSupport; import org.hibernate.dialect.function.TimestampaddFunction; import org.hibernate.dialect.function.TimestampdiffFunction; import org.hibernate.engine.FetchStyle; @@ -79,6 +59,7 @@ import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.MappingModelExpressible; +import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.PluralAttributeMapping; @@ -105,8 +86,8 @@ import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath; import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPathSource; import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource; import org.hibernate.metamodel.model.domain.internal.CompositeSqmPathSource; -import org.hibernate.metamodel.model.domain.internal.EntityDiscriminatorSqmPath; import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource; +import org.hibernate.metamodel.model.domain.internal.EntityDiscriminatorSqmPath; import org.hibernate.metamodel.model.domain.internal.EntityTypeImpl; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.persister.entity.AbstractEntityPersister; @@ -117,6 +98,7 @@ import org.hibernate.query.BindableType; import org.hibernate.query.QueryLogging; import org.hibernate.query.ReturnableType; import org.hibernate.query.SemanticException; +import org.hibernate.query.SortDirection; import org.hibernate.query.criteria.JpaCteCriteriaAttribute; import org.hibernate.query.criteria.JpaPath; import org.hibernate.query.criteria.JpaSearchOrder; @@ -134,7 +116,6 @@ import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.DynamicInstantiationNature; import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.sqm.InterpretationException; -import org.hibernate.query.SortDirection; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.SqmQuerySource; @@ -412,12 +393,30 @@ import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.usertype.UserVersionType; import org.hibernate.usertype.internal.AbstractTimeZoneStorageCompositeUserType; - import org.jboss.logging.Logger; -import jakarta.persistence.TemporalType; -import jakarta.persistence.metamodel.SingularAttribute; -import jakarta.persistence.metamodel.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; import static java.util.Collections.singletonList; import static org.hibernate.generator.EventType.INSERT; @@ -7962,6 +7961,8 @@ public abstract class BaseSqmToSqlAstConverter extends Base boolean explicitFetch = false; EntityGraphTraversalState.TraversalResult traversalResult = null; + TableGroup joinedTableGroup = null; + if ( fetchedJoin != null ) { fetchablePath = fetchedJoin.getNavigablePath(); // there was an explicit fetch in the SQM @@ -8043,7 +8044,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base } if ( joined && fetchable instanceof TableGroupJoinProducer ) { - fromClauseIndex.resolveTableGroup( + joinedTableGroup = fromClauseIndex.resolveTableGroup( fetchablePath, np -> { // generate the join @@ -8099,22 +8100,40 @@ public abstract class BaseSqmToSqlAstConverter extends Base sqlSelectionsToTrack.getValue().addAll( selections.subList( sqlSelectionStartIndexForFetch, selections.size() ) ); } - if ( fetch != null ) { - if ( fetch.getTiming() == FetchTiming.IMMEDIATE && fetchable instanceof PluralAttributeMapping ) { - final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) fetchable; - final CollectionClassification collectionClassification = pluralAttributeMapping.getMappedType() - .getCollectionSemantics() - .getCollectionClassification(); - if ( collectionClassification == CollectionClassification.BAG ) { - if ( currentBagRole != null ) { - throw new MultipleBagFetchException( - Arrays.asList( - currentBagRole, - fetchable.getNavigableRole().getNavigableName() - ) + if ( fetch != null && fetch.getTiming() == FetchTiming.IMMEDIATE ) { + if ( fetchable instanceof TableGroupJoinProducer ) { + if ( joinedTableGroup != null ) { + final TableGroup actualTableGroup = joinedTableGroup instanceof PluralTableGroup ? + ( (PluralTableGroup) joinedTableGroup ).getElementTableGroup() : + joinedTableGroup; + final MappingType entityMappingType = actualTableGroup == null + ? null + : actualTableGroup.getModelPart().getPartMappingType(); + if ( entityMappingType instanceof EntityMappingType ) { + registerEntityNameUsage( + actualTableGroup, + EntityNameUse.PROJECTION, + ( (EntityMappingType) entityMappingType ).getEntityName(), + true ); } - currentBagRole = fetchable.getNavigableRole().getNavigableName(); + } + if ( fetchable instanceof PluralAttributeMapping ) { + final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) fetchable; + final CollectionClassification collectionClassification = pluralAttributeMapping.getMappedType() + .getCollectionSemantics() + .getCollectionClassification(); + if ( collectionClassification == CollectionClassification.BAG ) { + if ( currentBagRole != null ) { + throw new MultipleBagFetchException( + Arrays.asList( + currentBagRole, + fetchable.getNavigableRole().getNavigableName() + ) + ); + } + currentBagRole = fetchable.getNavigableRole().getNavigableName(); + } } } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/graphs/EntityGraphTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/graphs/EntityGraphTest.java index da9addd06c..c9bf3999f3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/graphs/EntityGraphTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/graphs/EntityGraphTest.java @@ -40,6 +40,7 @@ import org.hibernate.testing.util.uuid.SafeRandomUUIDGenerator; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.JiraKey; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -55,8 +56,8 @@ public class EntityGraphTest extends BaseEntityManagerFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { return new Class[] { - Foo.class, Bar.class, Baz.class, Author.class, Book.class, Prize.class, - Company.class, Employee.class, Manager.class, Location.class, Animal.class, Dog.class, Cat.class + Foo.class, Bar.class, Baz.class, Author.class, Book.class, Prize.class, Company.class, + Employee.class, Manager.class, Location.class, AnimalOwner.class, Animal.class, Dog.class, Cat.class }; } @@ -454,6 +455,36 @@ public class EntityGraphTest extends BaseEntityManagerFunctionalTestCase { em.close(); } + @Test + @JiraKey("HHH-17192") + public void joinedInheritanceWithSubEntityAttributeFiltering() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + Dog dog = new Dog(); + em.persist( dog ); + AnimalOwner animalOwner = new AnimalOwner(); + animalOwner.animal = dog; + em.persist( animalOwner ); + em.flush(); + em.clear(); + + EntityGraph entityGraph = em.createEntityGraph( AnimalOwner.class ); + entityGraph.addAttributeNodes( "animal" ); + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery( AnimalOwner.class ); + Root root = query.from( AnimalOwner.class ); + query.where( cb.equal( root.get( "animal" ).get( "id" ), dog.id ) ); + AnimalOwner owner = em.createQuery( query ) + .setHint( "jakarta.persistence.loadgraph", entityGraph ) + .getResultList() + .get( 0 ); + assertTrue( Hibernate.isInitialized( owner.animal ) ); + assertTrue( owner.animal instanceof Dog ); + + em.getTransaction().commit(); + em.close(); + } + @Entity @Table(name = "foo") public static class Foo { @@ -557,6 +588,16 @@ public class EntityGraphTest extends BaseEntityManagerFunctionalTestCase { } } + @Entity + public static class AnimalOwner { + @Id + @GeneratedValue + public Integer id; + + @ManyToOne(fetch = FetchType.LAZY) + public Animal animal; + } + @Entity @Inheritance(strategy = InheritanceType.JOINED) public static abstract class Animal {