From 78c9ccf64fa537277dfcb9c2535fdcd03ce82ee8 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 10 Nov 2023 18:48:49 +0100 Subject: [PATCH] HHH-17411 Fix fetch join on treated join leads to not cause owner not selected error --- .../internal/QualifiedJoinPathConsumer.java | 12 +- .../query/sqm/tree/domain/SqmBagJoin.java | 12 +- .../query/sqm/tree/domain/SqmListJoin.java | 12 +- .../query/sqm/tree/domain/SqmMapJoin.java | 12 +- .../query/sqm/tree/domain/SqmSetJoin.java | 12 +- .../sqm/tree/domain/SqmSingularJoin.java | 12 +- .../sqm/tree/domain/SqmTreatedBagJoin.java | 16 +- .../sqm/tree/domain/SqmTreatedListJoin.java | 18 +- .../sqm/tree/domain/SqmTreatedMapJoin.java | 18 +- .../sqm/tree/domain/SqmTreatedSetJoin.java | 18 +- .../tree/domain/SqmTreatedSingularJoin.java | 18 +- .../query/sqm/tree/from/SqmAttributeJoin.java | 10 ++ .../query/sqm/tree/select/SqmQuerySpec.java | 29 +++- .../hql/treat/HqlTreatJoinFetchTest.java | 162 ++++++++++++++++++ 14 files changed, 325 insertions(+), 36 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/treat/HqlTreatJoinFetchTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java index dfc2fc611e..d51e57bddb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java @@ -21,6 +21,7 @@ import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor; +import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; import org.hibernate.query.sqm.tree.from.SqmCteJoin; import org.hibernate.query.sqm.tree.from.SqmEntityJoin; import org.hibernate.query.sqm.tree.from.SqmFrom; @@ -276,9 +277,14 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer { @Override public void consumeTreat(String entityName, boolean isTerminal) { - currentPath = isTerminal - ? currentPath.treatAs( treatTarget( entityName ), alias) - : currentPath.treatAs( treatTarget( entityName ) ); + if ( isTerminal ) { + currentPath = fetch + ? ( (SqmAttributeJoin) currentPath ).treatAs( treatTarget( entityName ), alias, true ) + : currentPath.treatAs( treatTarget( entityName ), alias ); + } + else { + currentPath = currentPath.treatAs( treatTarget( entityName ) ); + } creationState.getCurrentProcessingState().getPathRegistry().register( currentPath ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java index a353b9df6f..3b26d23ea2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java @@ -132,9 +132,19 @@ public class SqmBagJoin extends AbstractSqmPluralJoin, E> @Override public SqmTreatedBagJoin treatAs(EntityDomainType treatTarget, String alias) { + return treatAs( treatTarget, alias, false ); + } + + @Override + public SqmTreatedBagJoin treatAs(Class treatJavaType, String alias, boolean fetch) { + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias, fetch ); + } + + @Override + public SqmTreatedBagJoin treatAs(EntityDomainType treatTarget, String alias, boolean fetch) { final SqmTreatedBagJoin treat = findTreat( treatTarget, alias ); if ( treat == null ) { - return addTreat( new SqmTreatedBagJoin<>( this, treatTarget, alias ) ); + return addTreat( new SqmTreatedBagJoin<>( this, treatTarget, alias, fetch ) ); } return treat; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java index 51a6413f0e..aabb7af34c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java @@ -139,9 +139,19 @@ public class SqmListJoin @Override public SqmTreatedListJoin treatAs(EntityDomainType treatTarget, String alias) { + return treatAs( treatTarget, alias, false ); + } + + @Override + public SqmTreatedListJoin treatAs(Class treatJavaType, String alias, boolean fetch) { + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias, fetch ); + } + + @Override + public SqmTreatedListJoin treatAs(EntityDomainType treatTarget, String alias, boolean fetch) { final SqmTreatedListJoin treat = findTreat( treatTarget, alias ); if ( treat == null ) { - return addTreat( new SqmTreatedListJoin<>( this, treatTarget, alias ) ); + return addTreat( new SqmTreatedListJoin<>( this, treatTarget, alias, fetch ) ); } return treat; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java index 909e15a561..7f3522d738 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java @@ -151,9 +151,19 @@ public class SqmMapJoin @Override public SqmTreatedMapJoin treatAs(EntityDomainType treatTarget, String alias) { + return treatAs( treatTarget, alias, false ); + } + + @Override + public SqmTreatedMapJoin treatAs(Class treatJavaType, String alias, boolean fetch) { + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias, fetch ); + } + + @Override + public SqmTreatedMapJoin treatAs(EntityDomainType treatTarget, String alias, boolean fetch) { final SqmTreatedMapJoin treat = findTreat( treatTarget, alias ); if ( treat == null ) { - return addTreat( new SqmTreatedMapJoin<>( this, treatTarget, alias ) ); + return addTreat( new SqmTreatedMapJoin<>( this, treatTarget, alias, fetch ) ); } return treat; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java index af8d4a489e..2eb31181a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java @@ -132,9 +132,19 @@ public class SqmSetJoin @Override public SqmTreatedSetJoin treatAs(EntityDomainType treatTarget, String alias) { + return treatAs( treatTarget, alias, false ); + } + + @Override + public SqmTreatedSetJoin treatAs(Class treatJavaType, String alias, boolean fetch) { + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias, fetch ); + } + + @Override + public SqmTreatedSetJoin treatAs(EntityDomainType treatTarget, String alias, boolean fetch) { final SqmTreatedSetJoin treat = findTreat( treatTarget, alias ); if ( treat == null ) { - return addTreat( new SqmTreatedSetJoin<>( this, treatTarget, alias ) ); + return addTreat( new SqmTreatedSetJoin<>( this, treatTarget, alias, fetch ) ); } return treat; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java index 0c5302fc89..06d42df773 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java @@ -110,9 +110,19 @@ public class SqmSingularJoin extends AbstractSqmAttributeJoin { @Override public SqmTreatedSingularJoin treatAs(EntityDomainType treatTarget, String alias) { + return treatAs( treatTarget, alias, false ); + } + + @Override + public SqmTreatedSingularJoin treatAs(Class treatJavaType, String alias, boolean fetch) { + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias, fetch ); + } + + @Override + public SqmTreatedSingularJoin treatAs(EntityDomainType treatTarget, String alias, boolean fetch) { final SqmTreatedSingularJoin treat = findTreat( treatTarget, alias ); if ( treat == null ) { - return addTreat( new SqmTreatedSingularJoin<>( this, treatTarget, alias ) ); + return addTreat( new SqmTreatedSingularJoin<>( this, treatTarget, alias, fetch ) ); } return treat; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java index a4a3aedc49..8da507b72c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java @@ -26,6 +26,14 @@ public class SqmTreatedBagJoin extends SqmBagJoin impleme SqmBagJoin wrappedPath, EntityDomainType treatTarget, String alias) { + this( wrappedPath, treatTarget, alias, false ); + } + + public SqmTreatedBagJoin( + SqmBagJoin wrappedPath, + EntityDomainType treatTarget, + String alias, + boolean fetched) { //noinspection unchecked super( wrappedPath.getLhs(), @@ -35,7 +43,7 @@ public class SqmTreatedBagJoin extends SqmBagJoin impleme (BagPersistentAttribute) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), - wrappedPath.isFetched(), + fetched, wrappedPath.nodeBuilder() ); this.treatTarget = treatTarget; @@ -46,7 +54,8 @@ public class SqmTreatedBagJoin extends SqmBagJoin impleme NavigablePath navigablePath, SqmBagJoin wrappedPath, EntityDomainType treatTarget, - String alias) { + String alias, + boolean fetched) { //noinspection unchecked super( wrappedPath.getLhs(), @@ -76,7 +85,8 @@ public class SqmTreatedBagJoin extends SqmBagJoin impleme getNavigablePath(), wrappedPath.copy( context ), treatTarget, - getExplicitAlias() + getExplicitAlias(), + isFetched() ) ); copyTo( path, context ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java index c24f0f76e9..125b680cc1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java @@ -28,6 +28,14 @@ public class SqmTreatedListJoin extends SqmListJoin imple SqmListJoin wrappedPath, EntityDomainType treatTarget, String alias) { + this( wrappedPath, treatTarget, alias, false ); + } + + public SqmTreatedListJoin( + SqmListJoin wrappedPath, + EntityDomainType treatTarget, + String alias, + boolean fetched) { //noinspection unchecked super( wrappedPath.getLhs(), @@ -37,7 +45,7 @@ public class SqmTreatedListJoin extends SqmListJoin imple (ListPersistentAttribute) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), - wrappedPath.isFetched(), + fetched, wrappedPath.nodeBuilder() ); this.treatTarget = treatTarget; @@ -48,7 +56,8 @@ public class SqmTreatedListJoin extends SqmListJoin imple NavigablePath navigablePath, SqmListJoin wrappedPath, EntityDomainType treatTarget, - String alias) { + String alias, + boolean fetched) { //noinspection unchecked super( wrappedPath.getLhs(), @@ -56,7 +65,7 @@ public class SqmTreatedListJoin extends SqmListJoin imple (ListPersistentAttribute) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), - wrappedPath.isFetched(), + fetched, wrappedPath.nodeBuilder() ); this.treatTarget = treatTarget; @@ -75,7 +84,8 @@ public class SqmTreatedListJoin extends SqmListJoin imple getNavigablePath(), wrappedPath.copy( context ), treatTarget, - getExplicitAlias() + getExplicitAlias(), + isFetched() ) ); copyTo( path, context ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java index c89cc28122..7cc1fe3114 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java @@ -24,6 +24,14 @@ public class SqmTreatedMapJoin extends SqmMapJoin SqmMapJoin wrappedPath, EntityDomainType treatTarget, String alias) { + this( wrappedPath, treatTarget, alias, false ); + } + + public SqmTreatedMapJoin( + SqmMapJoin wrappedPath, + EntityDomainType treatTarget, + String alias, + boolean fetched) { //noinspection unchecked super( wrappedPath.getLhs(), @@ -33,7 +41,7 @@ public class SqmTreatedMapJoin extends SqmMapJoin ( (SqmMapJoin) wrappedPath ).getModel(), alias, wrappedPath.getSqmJoinType(), - wrappedPath.isFetched(), + fetched, wrappedPath.nodeBuilder() ); this.treatTarget = treatTarget; @@ -44,7 +52,8 @@ public class SqmTreatedMapJoin extends SqmMapJoin NavigablePath navigablePath, SqmMapJoin wrappedPath, EntityDomainType treatTarget, - String alias) { + String alias, + boolean fetched) { //noinspection unchecked super( wrappedPath.getLhs(), @@ -52,7 +61,7 @@ public class SqmTreatedMapJoin extends SqmMapJoin ( (SqmMapJoin) wrappedPath ).getModel(), alias, wrappedPath.getSqmJoinType(), - wrappedPath.isFetched(), + fetched, wrappedPath.nodeBuilder() ); this.treatTarget = treatTarget; @@ -71,7 +80,8 @@ public class SqmTreatedMapJoin extends SqmMapJoin getNavigablePath(), wrappedPath.copy( context ), treatTarget, - getExplicitAlias() + getExplicitAlias(), + isFetched() ) ); copyTo( path, context ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java index 76388897ac..9d6ce1cbdf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java @@ -26,6 +26,14 @@ public class SqmTreatedSetJoin extends SqmSetJoin impleme SqmSetJoin wrappedPath, EntityDomainType treatTarget, String alias) { + this( wrappedPath, treatTarget, alias, false ); + } + + public SqmTreatedSetJoin( + SqmSetJoin wrappedPath, + EntityDomainType treatTarget, + String alias, + boolean fetched) { //noinspection unchecked super( wrappedPath.getLhs(), @@ -35,7 +43,7 @@ public class SqmTreatedSetJoin extends SqmSetJoin impleme (SetPersistentAttribute) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), - wrappedPath.isFetched(), + fetched, wrappedPath.nodeBuilder() ); this.treatTarget = treatTarget; @@ -46,7 +54,8 @@ public class SqmTreatedSetJoin extends SqmSetJoin impleme NavigablePath navigablePath, SqmSetJoin wrappedPath, EntityDomainType treatTarget, - String alias) { + String alias, + boolean fetched) { //noinspection unchecked super( wrappedPath.getLhs(), @@ -54,7 +63,7 @@ public class SqmTreatedSetJoin extends SqmSetJoin impleme (SetPersistentAttribute) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), - wrappedPath.isFetched(), + fetched, wrappedPath.nodeBuilder() ); this.treatTarget = treatTarget; @@ -73,7 +82,8 @@ public class SqmTreatedSetJoin extends SqmSetJoin impleme getNavigablePath(), wrappedPath.copy( context ), treatTarget, - getExplicitAlias() + getExplicitAlias(), + isFetched() ) ); copyTo( path, context ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java index cc99f78654..b6708a817c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java @@ -25,6 +25,14 @@ public class SqmTreatedSingularJoin extends SqmSingularJoin wrappedPath, EntityDomainType treatTarget, String alias) { + this( wrappedPath, treatTarget, alias, false ); + } + + public SqmTreatedSingularJoin( + SqmSingularJoin wrappedPath, + EntityDomainType treatTarget, + String alias, + boolean fetched) { //noinspection unchecked super( wrappedPath.getLhs(), @@ -35,7 +43,7 @@ public class SqmTreatedSingularJoin extends SqmSingularJoin) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), - wrappedPath.isFetched(), + fetched, wrappedPath.nodeBuilder() ); this.treatTarget = treatTarget; @@ -46,7 +54,8 @@ public class SqmTreatedSingularJoin extends SqmSingularJoin wrappedPath, EntityDomainType treatTarget, - String alias) { + String alias, + boolean fetched) { //noinspection unchecked super( wrappedPath.getLhs(), @@ -54,7 +63,7 @@ public class SqmTreatedSingularJoin extends SqmSingularJoin) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), - wrappedPath.isFetched(), + fetched, wrappedPath.nodeBuilder() ); this.treatTarget = treatTarget; @@ -73,7 +82,8 @@ public class SqmTreatedSingularJoin extends SqmSingularJoin extends SqmQualifiedJoin, JpaFetch SqmAttributeJoin treatAs(EntityDomainType treatTarget); + @Override + SqmAttributeJoin treatAs(Class treatJavaType, String alias); + + @Override + SqmAttributeJoin treatAs(EntityDomainType treatJavaType, String alias); + + SqmAttributeJoin treatAs(Class treatJavaType, String alias, boolean fetch); + + SqmAttributeJoin treatAs(EntityDomainType treatJavaType, String alias, boolean fetch); + /* @deprecated not used anymore */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java index 98280877b0..11fc14ae73 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java @@ -489,7 +489,10 @@ public class SqmQuerySpec extends SqmQueryPart } for ( SqmRoot root : roots ) { - validateFetchOwners( selectedFromSet, root ); + validateFetchOwners( selectedFromSet, root, root ); + for ( SqmFrom sqmTreat : root.getSqmTreats() ) { + validateFetchOwners( selectedFromSet, root, sqmTreat ); + } } } @@ -531,8 +534,8 @@ public class SqmQuerySpec extends SqmQueryPart } } - private void validateFetchOwners(Set> selectedFromSet, SqmFrom owner) { - for ( SqmJoin sqmJoin : owner.getSqmJoins() ) { + private void validateFetchOwners(Set> selectedFromSet, SqmFrom owner, SqmFrom joinContainer) { + for ( SqmJoin sqmJoin : joinContainer.getSqmJoins() ) { if ( sqmJoin instanceof SqmAttributeJoin ) { final SqmAttributeJoin attributeJoin = (SqmAttributeJoin) sqmJoin; if ( attributeJoin.isFetched() ) { @@ -541,19 +544,27 @@ public class SqmQuerySpec extends SqmQueryPart continue; } } - validateFetchOwners( selectedFromSet, sqmJoin ); - } - for ( SqmFrom sqmTreat : owner.getSqmTreats() ) { - validateFetchOwners( selectedFromSet, sqmTreat ); + for ( SqmFrom sqmTreat : sqmJoin.getSqmTreats() ) { + if ( sqmTreat instanceof SqmAttributeJoin ) { + final SqmAttributeJoin attributeJoin = (SqmAttributeJoin) sqmTreat; + if ( attributeJoin.isFetched() ) { + assertFetchOwner( selectedFromSet, owner, attributeJoin ); + // Only need to check the first level + continue; + } + } + validateFetchOwners( selectedFromSet, sqmJoin, sqmTreat ); + } + validateFetchOwners( selectedFromSet, sqmJoin, sqmJoin ); } } - private void assertFetchOwner(Set> selectedFromSet, SqmFrom owner, SqmJoin sqmJoin) { + private void assertFetchOwner(Set> selectedFromSet, SqmFrom owner, SqmJoin fetchJoin) { if ( !selectedFromSet.contains( owner ) ) { throw new SemanticException( "Query specified join fetching, but the owner " + "of the fetched association was not present in the select list " + - "[" + sqmJoin.asLoggableText() + "]" + "[" + fetchJoin.asLoggableText() + "]" ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/treat/HqlTreatJoinFetchTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/treat/HqlTreatJoinFetchTest.java new file mode 100644 index 0000000000..81b904ee06 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/treat/HqlTreatJoinFetchTest.java @@ -0,0 +1,162 @@ +package org.hibernate.orm.test.query.hql.treat; + +import java.util.List; + +import org.hibernate.query.spi.QueryImplementor; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.BeforeAll; +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.MappedSuperclass; +import jakarta.persistence.OneToOne; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { + HqlTreatJoinFetchTest.TestEntity.class, + HqlTreatJoinFetchTest.BaseEntity.class, + HqlTreatJoinFetchTest.JoinedEntity.class + } +) +@SessionFactory +@JiraKey("HHH-17411") +public class HqlTreatJoinFetchTest { + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + JoinedEntity joined = new JoinedEntity( 1, "joined" ); + TestEntity testEntity = new TestEntity( 2, "test", joined ); + + session.persist( testEntity ); + session.persist( joined ); + + } + ); + + } + + @Test + public void testTreatJoinFetch(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + QueryImplementor query = session.createQuery( + "select t from TestEntity t join fetch treat(t.joined as JoinedEntity) j left join fetch j.testEntity e", + TestEntity.class + ); + List result = query.list(); + assertThat( result.size() ).isEqualTo( 1 ); + } + ); + } + + @Test + public void testJoinFetchRootTreat(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + QueryImplementor query = session.createQuery( + "select t from BaseEntity t join fetch treat(t as JoinedEntity).testEntity j left join fetch j.joined2 e", + TestEntity.class + ); + query.list(); + } + ); + } + + @MappedSuperclass + public static abstract class AbstractEntity { + + public AbstractEntity() { + } + + public AbstractEntity(BaseEntity joined) { + this.joined = joined; + } + + @OneToOne + private BaseEntity joined; + + public BaseEntity getJoined() { + return joined; + } + } + + @Entity(name = "TestEntity") + public static class TestEntity extends AbstractEntity { + @Id + private long id; + + private String name; + @ManyToOne(fetch = FetchType.LAZY) + private JoinedEntity joined2; + + public TestEntity() { + } + + public TestEntity(long id, String name, JoinedEntity joined) { + super( joined ); + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + } + + @Entity(name = "BaseEntity") + public static abstract class BaseEntity { + + @Id + private long id; + + private String name; + + public BaseEntity() { + } + + public BaseEntity(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + } + + @Entity(name = "JoinedEntity") + public static class JoinedEntity extends BaseEntity { + @ManyToOne(fetch = FetchType.LAZY) + private TestEntity testEntity; + + public JoinedEntity() { + } + + public JoinedEntity(long id, String name) { + super( id, name ); + } + + public TestEntity getTestEntity() { + return testEntity; + } + } +}