HHH-16180 - Add test and fix (AssertionError when using using native query on table with InheritanceStrategy.JOINED)
Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
parent
c67dbc0013
commit
2b4201e413
|
@ -119,60 +119,7 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression createCaseSearchedExpression(TableGroup entityTableGroup) {
|
private Expression createCaseSearchedExpression(TableGroup entityTableGroup) {
|
||||||
return new SelfRenderingExpression() {
|
return new CaseStatementDiscriminatorExpression( entityTableGroup );
|
||||||
CaseSearchedExpression caseSearchedExpression;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderToSql(
|
|
||||||
SqlAppender sqlAppender,
|
|
||||||
SqlAstTranslator<?> walker,
|
|
||||||
SessionFactoryImplementor sessionFactory) {
|
|
||||||
if ( caseSearchedExpression == null ) {
|
|
||||||
// todo (6.0): possible optimization is to omit cases for table reference joins, that touch a super class, where a subclass is inner joined due to pruning
|
|
||||||
caseSearchedExpression = new CaseSearchedExpression( CaseStatementDiscriminatorMappingImpl.this );
|
|
||||||
tableDiscriminatorDetailsMap.forEach(
|
|
||||||
(tableName, tableDiscriminatorDetails) -> {
|
|
||||||
final TableReference tableReference = entityTableGroup.getTableReference(
|
|
||||||
entityTableGroup.getNavigablePath(),
|
|
||||||
tableName,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( tableReference == null ) {
|
|
||||||
// assume this is because it is a table that is not part of the processing entity's sub-hierarchy
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Predicate predicate = new NullnessPredicate(
|
|
||||||
new ColumnReference(
|
|
||||||
tableReference,
|
|
||||||
tableDiscriminatorDetails.getCheckColumnName(),
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
getJdbcMapping()
|
|
||||||
),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
caseSearchedExpression.when(
|
|
||||||
predicate,
|
|
||||||
new QueryLiteral<>(
|
|
||||||
tableDiscriminatorDetails.getDiscriminatorValue(),
|
|
||||||
getUnderlyingJdbcMappingType()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
caseSearchedExpression.accept( walker );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JdbcMappingContainer getExpressionType() {
|
|
||||||
return CaseStatementDiscriminatorMappingImpl.this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -281,4 +228,63 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class CaseStatementDiscriminatorExpression implements SelfRenderingExpression {
|
||||||
|
private final TableGroup entityTableGroup;
|
||||||
|
CaseSearchedExpression caseSearchedExpression;
|
||||||
|
|
||||||
|
public CaseStatementDiscriminatorExpression(TableGroup entityTableGroup) {
|
||||||
|
this.entityTableGroup = entityTableGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderToSql(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
SqlAstTranslator<?> walker,
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
if ( caseSearchedExpression == null ) {
|
||||||
|
// todo (6.0): possible optimization is to omit cases for table reference joins, that touch a super class, where a subclass is inner joined due to pruning
|
||||||
|
caseSearchedExpression = new CaseSearchedExpression( CaseStatementDiscriminatorMappingImpl.this );
|
||||||
|
tableDiscriminatorDetailsMap.forEach(
|
||||||
|
(tableName, tableDiscriminatorDetails) -> {
|
||||||
|
final TableReference tableReference = entityTableGroup.getTableReference(
|
||||||
|
entityTableGroup.getNavigablePath(),
|
||||||
|
tableName,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( tableReference == null ) {
|
||||||
|
// assume this is because it is a table that is not part of the processing entity's sub-hierarchy
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Predicate predicate = new NullnessPredicate(
|
||||||
|
new ColumnReference(
|
||||||
|
tableReference,
|
||||||
|
tableDiscriminatorDetails.getCheckColumnName(),
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
getJdbcMapping()
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
caseSearchedExpression.when(
|
||||||
|
predicate,
|
||||||
|
new QueryLiteral<>(
|
||||||
|
tableDiscriminatorDetails.getDiscriminatorValue(),
|
||||||
|
getUnderlyingJdbcMappingType()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
caseSearchedExpression.accept( walker );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcMappingContainer getExpressionType() {
|
||||||
|
return CaseStatementDiscriminatorMappingImpl.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart;
|
import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.CaseStatementDiscriminatorMappingImpl;
|
||||||
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
|
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
|
||||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||||
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||||
|
@ -65,6 +66,7 @@ import static org.hibernate.query.results.ResultsHelper.attributeName;
|
||||||
public class DomainResultCreationStateImpl
|
public class DomainResultCreationStateImpl
|
||||||
implements DomainResultCreationState, SqlAstCreationState, SqlAstProcessingState, SqlExpressionResolver {
|
implements DomainResultCreationState, SqlAstCreationState, SqlAstProcessingState, SqlExpressionResolver {
|
||||||
|
|
||||||
|
private static final String DISCRIMINATOR_ALIAS = "clazz_";
|
||||||
private final String stateIdentifier;
|
private final String stateIdentifier;
|
||||||
private final FromClauseAccessImpl fromClauseAccess;
|
private final FromClauseAccessImpl fromClauseAccess;
|
||||||
|
|
||||||
|
@ -295,6 +297,26 @@ public class DomainResultCreationStateImpl
|
||||||
|
|
||||||
return sqlSelection;
|
return sqlSelection;
|
||||||
}
|
}
|
||||||
|
else if ( created instanceof CaseStatementDiscriminatorMappingImpl.CaseStatementDiscriminatorExpression ) {
|
||||||
|
final int valuesArrayPosition;
|
||||||
|
if ( nestingFetchParent != null ) {
|
||||||
|
valuesArrayPosition = nestingFetchParent.getReferencedMappingType().getSelectableIndex( DISCRIMINATOR_ALIAS );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final int jdbcPosition = jdbcResultsMetadata.resolveColumnPosition( DISCRIMINATOR_ALIAS );
|
||||||
|
valuesArrayPosition = ResultsHelper.jdbcPositionToValuesArrayPosition( jdbcPosition );
|
||||||
|
}
|
||||||
|
|
||||||
|
final ResultSetMappingSqlSelection sqlSelection = new ResultSetMappingSqlSelection(
|
||||||
|
valuesArrayPosition,
|
||||||
|
created.getExpressionType().getSingleJdbcMapping()
|
||||||
|
);
|
||||||
|
|
||||||
|
sqlSelectionMap.put( key, sqlSelection );
|
||||||
|
sqlSelectionConsumer.accept( sqlSelection );
|
||||||
|
|
||||||
|
return sqlSelection;
|
||||||
|
}
|
||||||
|
|
||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* 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.hql.joinedSubclass;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Basic;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Inheritance;
|
||||||
|
import jakarta.persistence.InheritanceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jan Schatteman
|
||||||
|
*/
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = {
|
||||||
|
JoinedSubclassNativeQueryTest.Person.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class JoinedSubclassNativeQueryTest {
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public void setup(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
Person p = new Person();
|
||||||
|
p.setFirstName( "Jan" );
|
||||||
|
session.persist( p );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
public void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.createMutationQuery( "delete from Person" ).executeUpdate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-16180")
|
||||||
|
public void testJoinedInheritanceNativeQuery(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
Person p = session.createNativeQuery( "select p.*, 0 as clazz_ from Person p", Person.class ).getSingleResult();
|
||||||
|
Assertions.assertNotNull( p );
|
||||||
|
Assertions.assertEquals( p.getFirstName(), "Jan" );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Person")
|
||||||
|
@Inheritance(strategy = InheritanceType.JOINED)
|
||||||
|
public static class Person {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Basic(optional = false)
|
||||||
|
private String firstName;
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue