From a9b955e419ab6975ae425ca53b0b8c833adfa144 Mon Sep 17 00:00:00 2001 From: Rob Green Date: Sat, 4 Oct 2025 09:17:42 -0400 Subject: [PATCH 1/2] HHH-18871 prevent adding duplicate path names while determining navigable path --- .../internal/ResultSetMappingProcessor.java | 6 +- .../orm/test/query/NativeQueryNestedTree.java | 66 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTree.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java index 0ed5efced51c..7cb9ea4d7ca8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java @@ -270,7 +270,11 @@ else if ( ownerResult instanceof DynamicFetchBuilderLegacy dynamicFetchBuilderLe else { throw new AssertionFailure( "Unexpected fetch builder" ); } - return path.append( fetchBuilder.getFetchable().getFetchableName() ); + final String fetchableName = fetchBuilder.getFetchable().getFetchableName(); + if ( path.getLocalName().equals( fetchableName ) ) { + return path; + } + return path.append( fetchableName ); } private DynamicResultBuilderEntityStandard createSuffixedResultBuilder( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTree.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTree.java new file mode 100644 index 000000000000..1cfb763580a7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTree.java @@ -0,0 +1,66 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.query; + +import jakarta.persistence.*; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.testing.orm.junit.*; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +/** + * This reproduces an issue in Hibernate 6 parsing native queries. + */ +@DomainModel( + annotatedClasses = { + NativeQueryNestedTree.TreeNode.class + } +) +@ServiceRegistry( + settings = { + @Setting(name = AvailableSettings.SHOW_SQL, value = "true"), + @Setting(name = AvailableSettings.HBM2DDL_AUTO, value = "create") + } +) +@SessionFactory +@JiraKey(value = "HHH-18871") +public class NativeQueryNestedTree { + + @Test + public void test(SessionFactoryScope scope) { + // We want to make sure 'Could not locate TableGroup' no longer is thrown + assertDoesNotThrow( () -> + scope.inTransaction( session -> + session.createNativeQuery( """ + SELECT {t.*}, {t2.*}, {t3.*} + FROM TREE t + INNER JOIN tree t2 ON t2.parentident = t.ident + INNER JOIN tree t3 ON t3.parentident = t2.ident + """ ) + .addEntity( "t", TreeNode.class ) + .addJoin( "t2", "t.children" ) + .addJoin( "t3", "t2.children" ) + .list() + ) + ); + } + + @Entity(name = "TreeNode") + @Table(name = "TREE") + public static class TreeNode { + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "parentident") + private TreeNode parent; + @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private Set children = new HashSet(); + @Id + @GeneratedValue + private long ident; + } +} From a82f96652dbf748ea79e395d252aab3d1134d7b8 Mon Sep 17 00:00:00 2001 From: Rob Green Date: Sat, 4 Oct 2025 09:22:32 -0400 Subject: [PATCH 2/2] HHH-18871 make use of explicitSqlAliasBase when creating table group --- .../internal/PluralAttributeMappingImpl.java | 5 +-- .../orm/test/query/NativeQueryNestedTree.java | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java index cb7a241a87bf..26545252a3fd 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java @@ -807,7 +807,6 @@ private TableGroup createRootTableGroupJoin( SqlAstCreationState creationState) { final CollectionPersister collectionDescriptor = getCollectionDescriptor(); final SqlAstJoinType joinType = determineSqlJoinType( lhs, requestedJoinType, fetched ); - final SqlAliasBase sqlAliasBase = creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ); final TableGroup tableGroup; if ( collectionDescriptor.isOneToMany() ) { @@ -818,7 +817,7 @@ private TableGroup createRootTableGroupJoin( fetched, addsPredicate, explicitSourceAlias, - sqlAliasBase, + explicitSqlAliasBase, creationState ); } @@ -830,7 +829,7 @@ private TableGroup createRootTableGroupJoin( fetched, addsPredicate, explicitSourceAlias, - sqlAliasBase, + explicitSqlAliasBase, creationState ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTree.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTree.java index 1cfb763580a7..c22d4bd0ccbe 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTree.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTree.java @@ -49,6 +49,43 @@ public void test(SessionFactoryScope scope) { .list() ) ); + + assertDoesNotThrow(() -> + scope.inTransaction(session -> + session.createNativeQuery(""" + SELECT {t.*}, {t2.*}, {t3.*}, {t4.*} + FROM tree t + INNER JOIN tree t2 ON t2.parentident = t.ident + INNER JOIN tree t3 ON t3.parentident = t2.ident + INNER JOIN tree t4 ON t4.parentident = t3.ident + """) + .addEntity("t", TreeNode.class) + .addJoin("t2", "t.children") + .addJoin("t3", "t2.children") + .addJoin("t4", "t3.children") + .list() + ) + ); + + // Let's get crazy + assertDoesNotThrow(() -> + scope.inTransaction(session -> + session.createNativeQuery(""" + SELECT {t.*}, {t2.*}, {t3.*}, {t4.*}, {t5.*} + FROM tree t + INNER JOIN tree t2 ON t2.parentident = t.ident + INNER JOIN tree t3 ON t3.parentident = t2.ident + INNER JOIN tree t4 ON t4.parentident = t3.ident + INNER JOIN tree t5 ON t5.parentident = t4.ident + """) + .addEntity("t", TreeNode.class) + .addJoin("t2", "t.children") + .addJoin("t3", "t2.children") + .addJoin("t4", "t3.children") + .addJoin("t5", "t4.children") + .list() + ) + ); } @Entity(name = "TreeNode")