Skip to content

Commit 00c7493

Browse files
committed
HHH-17102 Fix @SqlResultSetMapping issue for joined inheritance entity result
1 parent 46545bb commit 00c7493

File tree

7 files changed

+184
-12
lines changed

7 files changed

+184
-12
lines changed

hibernate-core/src/main/java/org/hibernate/boot/query/SqlResultSetMappingDescriptor.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,7 @@ private static FetchMementoBasic resolveDiscriminatorMemento(
276276
String discriminatorColumn,
277277
NavigablePath entityPath) {
278278
final EntityDiscriminatorMapping discriminatorMapping = entityMapping.getDiscriminatorMapping();
279-
if ( discriminatorMapping == null ) {
280-
return null;
281-
}
282-
283-
if ( discriminatorColumn == null ) {
279+
if ( discriminatorMapping == null || discriminatorColumn == null || !entityMapping.hasSubclasses() ) {
284280
return null;
285281
}
286282

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CaseStatementDiscriminatorMappingImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,10 @@ public boolean hasPartitionedSelectionMapping() {
178178

179179
@Override
180180
public String getContainingTableExpression() {
181-
throw new UnsupportedOperationException();
181+
// throw new UnsupportedOperationException();
182182
// // this *should* only be used to create the sql-expression key, so just
183183
// // using the primary table expr should be fine
184-
// return entityDescriptor.getRootTableName();
184+
return getEntityDescriptor().getMappedTableDetails().getTableName();
185185
}
186186

187187
@Override

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ExplicitColumnDiscriminatorMappingImpl.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@
1414
import org.hibernate.spi.NavigablePath;
1515
import org.hibernate.sql.ast.spi.SqlAstCreationState;
1616
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
17+
import org.hibernate.sql.ast.tree.expression.ColumnReference;
1718
import org.hibernate.sql.ast.tree.expression.Expression;
1819
import org.hibernate.sql.ast.tree.from.TableGroup;
1920
import org.hibernate.sql.ast.tree.from.TableReference;
2021
import org.hibernate.type.BasicType;
2122

23+
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
24+
2225
/**
2326
* @author Steve Ebersole
2427
*/
@@ -80,7 +83,15 @@ public Expression resolveSqlExpression(
8083
SqlAstCreationState creationState) {
8184
final SqlExpressionResolver expressionResolver = creationState.getSqlExpressionResolver();
8285
final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, tableExpression );
83-
return expressionResolver.resolveSqlExpression( tableReference, this );
86+
87+
return expressionResolver.resolveSqlExpression(
88+
createColumnReferenceKey(
89+
tableGroup.getPrimaryTableReference(),
90+
getSelectionExpression(),
91+
jdbcMappingToUse
92+
),
93+
processingState -> new ColumnReference( tableReference, this )
94+
);
8495
}
8596

8697
@Override

hibernate-core/src/main/java/org/hibernate/query/internal/ResultMementoEntityJpa.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public ResultBuilderEntityValued resolve(
5959
ResultSetMappingResolutionContext context) {
6060
final EntityDiscriminatorMapping discriminatorMapping = entityDescriptor.getDiscriminatorMapping();
6161
final BasicValuedFetchBuilder discriminatorFetchBuilder;
62-
if ( discriminatorMapping == null ) {
62+
if ( discriminatorMapping == null || !entityDescriptor.hasSubclasses() ) {
6363
assert discriminatorMemento == null;
6464
discriminatorFetchBuilder = null;
6565
}

hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderBasicPart.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313

1414
import org.hibernate.engine.FetchTiming;
1515
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
16-
import org.hibernate.query.results.ResultsHelper;
16+
import org.hibernate.metamodel.mapping.DiscriminatorMapping;
17+
import org.hibernate.metamodel.mapping.JdbcMapping;
1718
import org.hibernate.spi.NavigablePath;
1819
import org.hibernate.query.results.BasicValuedFetchBuilder;
1920
import org.hibernate.query.results.DomainResultCreationStateImpl;
@@ -112,9 +113,17 @@ public BasicFetch<?> buildFetch(
112113

113114
final int valuesArrayPosition = jdbcPositionToValuesArrayPosition( jdbcPosition );
114115

116+
final JdbcMapping jdbcMapping;
117+
if ( referencedModelPart instanceof DiscriminatorMapping ) {
118+
jdbcMapping = ( (DiscriminatorMapping) referencedModelPart ).getUnderlyingJdbcMapping();
119+
}
120+
else {
121+
jdbcMapping = referencedModelPart.getJdbcMapping();
122+
}
123+
115124
// we just care about the registration here. The ModelPart will find it later
116125
creationStateImpl.resolveSqlExpression(
117-
createColumnReferenceKey( tableReference, referencedModelPart ),
126+
createColumnReferenceKey( tableReference, referencedModelPart.getSelectablePath(), jdbcMapping ),
118127
processingState -> new ResultSetMappingSqlSelection( valuesArrayPosition, referencedModelPart )
119128
);
120129

hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderEntityJpa.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public CompleteResultBuilderEntityJpa(
6060
}
6161
else {
6262
// discriminated
63-
assert discriminatorFetchBuilder != null;
63+
assert !entityDescriptor.hasSubclasses() || discriminatorFetchBuilder != null;
6464
}
6565
}
6666

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
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.resultmapping;
8+
9+
import java.util.List;
10+
11+
import org.hibernate.testing.orm.junit.DomainModel;
12+
import org.hibernate.testing.orm.junit.JiraKey;
13+
import org.hibernate.testing.orm.junit.SessionFactory;
14+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
15+
import org.junit.jupiter.api.AfterEach;
16+
import org.junit.jupiter.api.BeforeEach;
17+
import org.junit.jupiter.api.Test;
18+
19+
import jakarta.persistence.Column;
20+
import jakarta.persistence.Entity;
21+
import jakarta.persistence.EntityResult;
22+
import jakarta.persistence.FieldResult;
23+
import jakarta.persistence.Id;
24+
import jakarta.persistence.Inheritance;
25+
import jakarta.persistence.InheritanceType;
26+
import jakarta.persistence.NamedNativeQuery;
27+
import jakarta.persistence.SqlResultSetMapping;
28+
import jakarta.persistence.Table;
29+
30+
import static org.junit.jupiter.api.Assertions.assertEquals;
31+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
32+
import static org.junit.jupiter.api.Assertions.assertNull;
33+
34+
@DomainModel( annotatedClasses = {
35+
InheritanceTests.Parent.class,
36+
InheritanceTests.Child.class
37+
} )
38+
@SessionFactory
39+
@JiraKey("HHH-17102")
40+
public class InheritanceTests {
41+
42+
@BeforeEach
43+
public void prepareData(SessionFactoryScope scope) {
44+
scope.inTransaction(
45+
session -> {
46+
session.save( new Child( 1L, "test", 123 ) );
47+
}
48+
);
49+
}
50+
51+
@AfterEach
52+
public void cleanUpData(SessionFactoryScope scope) {
53+
scope.inTransaction(
54+
session -> {
55+
session.createQuery( "delete Child" ).executeUpdate();
56+
}
57+
);
58+
}
59+
60+
@Test
61+
public void testResultSetMappingForParentEntity(SessionFactoryScope scope) {
62+
scope.inTransaction(
63+
session -> {
64+
final List<Parent> children = session.createNamedQuery( "Parent", Parent.class ).getResultList();
65+
assertEquals( 1, children.size() );
66+
assertEquals( 1L, children.get( 0 ).id );
67+
assertEquals( "test", children.get( 0 ).name );
68+
assertInstanceOf( Child.class, children.get( 0 ) );
69+
assertEquals( 123, ( (Child) children.get( 0 ) ).test );
70+
}
71+
);
72+
}
73+
74+
@Test
75+
public void testResultSetMappingForChildEntity(SessionFactoryScope scope) {
76+
scope.inTransaction(
77+
session -> {
78+
final List<Child> children = session.createNamedQuery( "Child", Child.class ).getResultList();
79+
assertEquals( 1, children.size() );
80+
assertEquals( 1L, children.get( 0 ).id );
81+
assertEquals( "test", children.get( 0 ).name );
82+
assertEquals( 123, children.get( 0 ).test );
83+
}
84+
);
85+
}
86+
87+
@SqlResultSetMapping(
88+
name = "ParentResult",
89+
entities = {
90+
@EntityResult(
91+
entityClass = Parent.class,
92+
discriminatorColumn = "discr",
93+
fields = {
94+
@FieldResult(name = "id", column = "id"),
95+
@FieldResult(name = "name", column = "name"),
96+
@FieldResult(name = "test", column = "test")
97+
}
98+
)
99+
}
100+
)
101+
@NamedNativeQuery(
102+
name = "Parent",
103+
query = "SELECT p.id as id, case when c.id is not null then 1 else 0 end as discr, p.name as name, c.test as test FROM parent p left join child c on p.id = c.id",
104+
resultSetMapping = "ParentResult"
105+
)
106+
@Entity(name = "Parent")
107+
@Table(name = "parent")
108+
@Inheritance(strategy = InheritanceType.JOINED)
109+
public static class Parent {
110+
@Id
111+
@Column(name = "id")
112+
Long id;
113+
String name;
114+
115+
public Parent() {
116+
}
117+
118+
public Parent(Long id, String name) {
119+
this.id = id;
120+
this.name = name;
121+
}
122+
}
123+
124+
@SqlResultSetMapping(
125+
name = "ChildResult",
126+
entities = {
127+
@EntityResult(
128+
entityClass = Child.class,
129+
fields = {
130+
@FieldResult(name = "id", column = "id"),
131+
@FieldResult(name = "name", column = "name"),
132+
@FieldResult(name = "test", column = "test")
133+
}
134+
)
135+
}
136+
)
137+
@NamedNativeQuery(
138+
name = "Child",
139+
query = "SELECT c.id as id, 'test' as name, c.test FROM child c",
140+
resultSetMapping = "ChildResult"
141+
)
142+
@Entity(name = "Child")
143+
@Table(name = "child")
144+
public static class Child extends Parent {
145+
@Column(name = "test")
146+
int test;
147+
148+
public Child() {
149+
}
150+
151+
public Child(Long id, String name, int test) {
152+
super( id, name );
153+
this.test = test;
154+
}
155+
}
156+
}

0 commit comments

Comments
 (0)