Skip to content

Commit 5b6cb1e

Browse files
robwgreenjrbeikov
authored andcommitted
HHH-18871 allow collections to be mapped correctly while determining navigation path
1 parent 5b92ae5 commit 5b6cb1e

File tree

2 files changed

+119
-5
lines changed

2 files changed

+119
-5
lines changed

hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Map;
1616
import java.util.Set;
1717

18+
import org.hibernate.AssertionFailure;
1819
import org.hibernate.HibernateException;
1920
import org.hibernate.LockMode;
2021
import org.hibernate.MappingException;
@@ -280,15 +281,21 @@ private void applyFetchBuilder(
280281
}
281282

282283
private NavigablePath determineNavigablePath(DynamicFetchBuilderLegacy fetchBuilder) {
283-
final NativeQuery.ResultNode ownerResult = alias2Return.get( fetchBuilder.getOwnerAlias() );
284+
final var ownerResult = alias2Return.get( fetchBuilder.getOwnerAlias() );
285+
final NavigablePath basePath;
284286
if ( ownerResult instanceof NativeQuery.RootReturn ) {
285-
return ( (NativeQuery.RootReturn) ownerResult ).getNavigablePath()
286-
.append( fetchBuilder.getFetchableName() );
287+
basePath = ((NativeQuery.RootReturn) ownerResult).getNavigablePath();
288+
}
289+
else if ( ownerResult instanceof DynamicFetchBuilderLegacy ) {
290+
basePath = determineNavigablePath( ((DynamicFetchBuilderLegacy) ownerResult) );
287291
}
288292
else {
289-
return determineNavigablePath( ( DynamicFetchBuilderLegacy) ownerResult )
290-
.append( fetchBuilder.getFetchableName() );
293+
throw new AssertionFailure( "Unexpected fetch builder" );
291294
}
295+
final NavigablePath path = alias2CollectionPersister.containsKey( fetchBuilder.getOwnerAlias() )
296+
? basePath.append( CollectionPart.Nature.ELEMENT.getName() )
297+
: basePath;
298+
return path.append( fetchBuilder.getFetchableName() );
292299
}
293300

294301
private DynamicResultBuilderEntityStandard createSuffixedResultBuilder(
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.orm.test.query;
8+
9+
import jakarta.persistence.CascadeType;
10+
import jakarta.persistence.Entity;
11+
import jakarta.persistence.FetchType;
12+
import jakarta.persistence.GeneratedValue;
13+
import jakarta.persistence.Id;
14+
import jakarta.persistence.JoinColumn;
15+
import jakarta.persistence.ManyToOne;
16+
import jakarta.persistence.OneToMany;
17+
import jakarta.persistence.Table;
18+
import org.hibernate.testing.orm.junit.DomainModel;
19+
import org.hibernate.testing.orm.junit.Jira;
20+
import org.hibernate.testing.orm.junit.SessionFactory;
21+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
22+
import org.junit.jupiter.api.Test;
23+
24+
import java.util.HashSet;
25+
import java.util.Set;
26+
27+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
28+
29+
/**
30+
* This reproduces an issue in Hibernate 6 parsing native queries.
31+
*/
32+
@DomainModel(
33+
annotatedClasses = {
34+
NativeQueryNestedTreeTest.Tree.class,
35+
NativeQueryNestedTreeTest.Forest.class
36+
}
37+
)
38+
@SessionFactory
39+
@Jira("https://hibernate.atlassian.net/browse/HHH-18871")
40+
public class NativeQueryNestedTreeTest {
41+
42+
@Test
43+
public void test(SessionFactoryScope scope) {
44+
// We want to make sure 'Could not locate TableGroup' no longer is thrown
45+
assertDoesNotThrow( () -> scope.inTransaction( session ->
46+
session.createNativeQuery(
47+
"select {t.*}, {t2.*}, {t3.*}\n" +
48+
"from tree t\n" +
49+
"inner join tree t2 on t2.parent_id = t.id\n" +
50+
"inner join tree t3 on t3.parent_id = t2.id\n", Tree.class )
51+
.addEntity( "t", Tree.class )
52+
.addJoin( "t2", "t.children" )
53+
.addJoin( "t3", "t2.children" )
54+
.list()
55+
) );
56+
57+
assertDoesNotThrow( () -> scope.inTransaction( session ->
58+
session.createNativeQuery(
59+
"select {t.*}, {t2.*}, {t3.*}, {t4.*}\n" +
60+
"from tree t\n" +
61+
"inner join tree t2 on t2.parent_id = t.id\n" +
62+
"inner join tree t3 on t3.parent_id = t2.id\n" +
63+
"inner join tree t4 on t4.parent_id = t3.id\n", Tree.class )
64+
.addEntity( "t", Tree.class )
65+
.addJoin( "t2", "t.children" )
66+
.addJoin( "t3", "t2.children" )
67+
.addJoin( "t4", "t3.children" )
68+
.list()
69+
) );
70+
71+
assertDoesNotThrow( () -> scope.inTransaction( session ->
72+
session.createNativeQuery(
73+
"select {f.*}, {t.*}, {t2.*}\n" +
74+
"from forest f\n" +
75+
"inner join tree t on t.parent_id is null\n" +
76+
"inner join tree t2 on t2.parent_id = t.id\n", Forest.class )
77+
.addEntity( "f", Forest.class )
78+
.addJoin( "t", "f.trees" )
79+
.addJoin( "t2", "t.children" )
80+
.list()
81+
) );
82+
}
83+
84+
@Entity(name = "Tree")
85+
@Table(name = "tree")
86+
public static class Tree {
87+
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
88+
@JoinColumn(name = "parent_id")
89+
private Tree parent;
90+
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
91+
private Set<Tree> children = new HashSet<>();
92+
@Id
93+
@GeneratedValue
94+
private long id;
95+
}
96+
97+
@Entity(name = "Forest")
98+
@Table(name = "forest")
99+
public static class Forest {
100+
@Id
101+
@GeneratedValue
102+
private Long id;
103+
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
104+
@JoinColumn(name = "forest_id")
105+
private Set<Tree> trees = new HashSet<>();
106+
}
107+
}

0 commit comments

Comments
 (0)