Skip to content

Commit cb84bce

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

File tree

2 files changed

+122
-5
lines changed

2 files changed

+122
-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: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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.*}
48+
from tree t
49+
inner join tree t2 on t2.parent_id = t.id
50+
inner join tree t3 on t3.parent_id = t2.id
51+
""", Tree.class )
52+
.addEntity( "t", Tree.class )
53+
.addJoin( "t2", "t.children" )
54+
.addJoin( "t3", "t2.children" )
55+
.list()
56+
) );
57+
58+
assertDoesNotThrow( () -> scope.inTransaction( session ->
59+
session.createNativeQuery( """
60+
select {t.*}, {t2.*}, {t3.*}, {t4.*}
61+
from tree t
62+
inner join tree t2 on t2.parent_id = t.id
63+
inner join tree t3 on t3.parent_id = t2.id
64+
inner join tree t4 on t4.parent_id = t3.id
65+
""", Tree.class )
66+
.addEntity( "t", Tree.class )
67+
.addJoin( "t2", "t.children" )
68+
.addJoin( "t3", "t2.children" )
69+
.addJoin( "t4", "t3.children" )
70+
.list()
71+
) );
72+
73+
assertDoesNotThrow( () -> scope.inTransaction( session ->
74+
session.createNativeQuery( """
75+
select {f.*}, {t.*}, {t2.*}
76+
from forest f
77+
inner join tree t on t.parent_id is null
78+
inner join tree t2 on t2.parent_id = t.id
79+
""", Forest.class )
80+
.addEntity( "f", Forest.class )
81+
.addJoin( "t", "f.trees" )
82+
.addJoin( "t2", "t.children" )
83+
.list()
84+
) );
85+
}
86+
87+
@Entity(name = "Tree")
88+
@Table(name = "tree")
89+
public static class Tree {
90+
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
91+
@JoinColumn(name = "parent_id")
92+
private Tree parent;
93+
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
94+
private Set<Tree> children = new HashSet<>();
95+
@Id
96+
@GeneratedValue
97+
private long id;
98+
}
99+
100+
@Entity(name = "Forest")
101+
@Table(name = "forest")
102+
public static class Forest {
103+
@Id
104+
@GeneratedValue
105+
private Long id;
106+
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
107+
@JoinColumn(name = "forest_id")
108+
private Set<Tree> trees = new HashSet<>();
109+
}
110+
}

0 commit comments

Comments
 (0)