Skip to content

Commit

Permalink
HHH-17102 Fix @SqlResultSetMapping issue for joined inheritance entit…
Browse files Browse the repository at this point in the history
…y result
  • Loading branch information
beikov committed Dec 1, 2023
1 parent 47e8272 commit f7709e7
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,7 @@ private static FetchMementoBasic resolveDiscriminatorMemento(
String discriminatorColumn,
NavigablePath entityPath) {
final EntityDiscriminatorMapping discriminatorMapping = entityMapping.getDiscriminatorMapping();
if ( discriminatorMapping == null ) {
return null;
}

if ( discriminatorColumn == null ) {
if ( discriminatorMapping == null || discriminatorColumn == null || !entityMapping.hasSubclasses() ) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,10 @@ public boolean hasPartitionedSelectionMapping() {

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

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.type.BasicType;

import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;

/**
* @author Steve Ebersole
*/
Expand Down Expand Up @@ -80,7 +83,15 @@ public Expression resolveSqlExpression(
SqlAstCreationState creationState) {
final SqlExpressionResolver expressionResolver = creationState.getSqlExpressionResolver();
final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, tableExpression );
return expressionResolver.resolveSqlExpression( tableReference, this );

return expressionResolver.resolveSqlExpression(
createColumnReferenceKey(
tableGroup.getPrimaryTableReference(),
getSelectionExpression(),
jdbcMappingToUse
),
processingState -> new ColumnReference( tableReference, this )
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public ResultBuilderEntityValued resolve(
ResultSetMappingResolutionContext context) {
final EntityDiscriminatorMapping discriminatorMapping = entityDescriptor.getDiscriminatorMapping();
final BasicValuedFetchBuilder discriminatorFetchBuilder;
if ( discriminatorMapping == null ) {
if ( discriminatorMapping == null || !entityDescriptor.hasSubclasses() ) {
assert discriminatorMemento == null;
discriminatorFetchBuilder = null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.metamodel.mapping.DiscriminatorMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.spi.NavigablePath;
import org.hibernate.query.results.BasicValuedFetchBuilder;
import org.hibernate.query.results.DomainResultCreationStateImpl;
Expand Down Expand Up @@ -112,9 +113,17 @@ public BasicFetch<?> buildFetch(

final int valuesArrayPosition = jdbcPositionToValuesArrayPosition( jdbcPosition );

final JdbcMapping jdbcMapping;
if ( referencedModelPart instanceof DiscriminatorMapping ) {
jdbcMapping = ( (DiscriminatorMapping) referencedModelPart ).getUnderlyingJdbcMapping();
}
else {
jdbcMapping = referencedModelPart.getJdbcMapping();
}

// we just care about the registration here. The ModelPart will find it later
creationStateImpl.resolveSqlExpression(
createColumnReferenceKey( tableReference, referencedModelPart ),
createColumnReferenceKey( tableReference, referencedModelPart.getSelectablePath(), jdbcMapping ),
processingState -> new ResultSetMappingSqlSelection( valuesArrayPosition, referencedModelPart )
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public CompleteResultBuilderEntityJpa(
}
else {
// discriminated
assert discriminatorFetchBuilder != null;
assert !entityDescriptor.hasSubclasses() || discriminatorFetchBuilder != null;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.query.resultmapping;

import java.util.List;

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.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityResult;
import jakarta.persistence.FieldResult;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.NamedNativeQuery;
import jakarta.persistence.SqlResultSetMapping;
import jakarta.persistence.Table;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNull;

@DomainModel( annotatedClasses = {
InheritanceTests.Parent.class,
InheritanceTests.Child.class
} )
@SessionFactory
@JiraKey("HHH-17102")
public class InheritanceTests {

@BeforeEach
public void prepareData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.save( new Child( 1L, "test", 123 ) );
}
);
}

@AfterEach
public void cleanUpData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete Child" ).executeUpdate();
}
);
}

@Test
public void testResultSetMappingForParentEntity(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final List<Parent> children = session.createNamedQuery( "Parent", Parent.class ).getResultList();
assertEquals( 1, children.size() );
assertEquals( 1L, children.get( 0 ).id );
assertEquals( "test", children.get( 0 ).name );
assertInstanceOf( Child.class, children.get( 0 ) );
assertEquals( 123, ( (Child) children.get( 0 ) ).test );
}
);
}

@Test
public void testResultSetMappingForChildEntity(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final List<Child> children = session.createNamedQuery( "Child", Child.class ).getResultList();
assertEquals( 1, children.size() );
assertEquals( 1L, children.get( 0 ).id );
assertEquals( "test", children.get( 0 ).name );
assertEquals( 123, children.get( 0 ).test );
}
);
}

@SqlResultSetMapping(
name = "ParentResult",
entities = {
@EntityResult(
entityClass = Parent.class,
discriminatorColumn = "discr",
fields = {
@FieldResult(name = "id", column = "id"),
@FieldResult(name = "name", column = "name"),
@FieldResult(name = "test", column = "test")
}
)
}
)
@NamedNativeQuery(
name = "Parent",
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",
resultSetMapping = "ParentResult"
)
@Entity(name = "Parent")
@Table(name = "parent")
@Inheritance(strategy = InheritanceType.JOINED)
public static class Parent {
@Id
@Column(name = "id")
Long id;
String name;

public Parent() {
}

public Parent(Long id, String name) {
this.id = id;
this.name = name;
}
}

@SqlResultSetMapping(
name = "ChildResult",
entities = {
@EntityResult(
entityClass = Child.class,
fields = {
@FieldResult(name = "id", column = "id"),
@FieldResult(name = "name", column = "name"),
@FieldResult(name = "test", column = "test")
}
)
}
)
@NamedNativeQuery(
name = "Child",
query = "SELECT c.id as id, 'test' as name, c.test FROM child c",
resultSetMapping = "ChildResult"
)
@Entity(name = "Child")
@Table(name = "child")
public static class Child extends Parent {
@Column(name = "test")
int test;

public Child() {
}

public Child(Long id, String name, int test) {
super( id, name );
this.test = test;
}
}
}

0 comments on commit f7709e7

Please sign in to comment.