HHH-15323 @AnyDiscriminator Unable to filter a polymorphic relationship in a query

This commit is contained in:
Andrea Boriero 2022-07-04 11:14:00 +02:00 committed by Andrea Boriero
parent c84b36e3e9
commit 83ea2e8f42
16 changed files with 661 additions and 45 deletions

View File

@ -20,6 +20,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.spi.NavigablePath;
@ -98,6 +99,14 @@ public class DiscriminatedCollectionPart implements DiscriminatedAssociationMode
return discriminatorMapping.resolveDiscriminatorValueToEntityMapping( entityMappingType );
}
@Override
public int forEachSelectable(int offset, SelectableConsumer consumer) {
discriminatorMapping.getDiscriminatorPart().forEachSelectable( offset, consumer );
discriminatorMapping.getKeyPart().forEachSelectable( offset + 1, consumer );
return 2;
}
@Override
public String getFetchableName() {
return nature.getName();

View File

@ -0,0 +1,70 @@
/*
* 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.metamodel.model.domain.internal;
import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.PathException;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.domain.AbstractSqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.spi.NavigablePath;
public class AnyDiscriminatorSqmPath extends AbstractSqmPath {
protected AnyDiscriminatorSqmPath(
NavigablePath navigablePath,
SqmPathSource referencedPathSource,
SqmPath lhs,
NodeBuilder nodeBuilder) {
super( navigablePath, referencedPathSource, lhs, nodeBuilder );
}
@Override
public AnyDiscriminatorSqmPath copy(SqmCopyContext context) {
final AnyDiscriminatorSqmPath existing = context.getCopy( this );
if ( existing != null ) {
return existing;
}
return context.registerCopy(
this,
(AnyDiscriminatorSqmPath) getLhs().copy( context ).type()
);
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitAnyDiscriminatorTypeExpression( this ) ;
}
@Override
public void appendHqlString(StringBuilder sb) {
sb.append( "type(" );
getLhs().appendHqlString( sb );
sb.append( ')' );
}
@Override
public SqmPath<?> resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) {
throw new IllegalStateException( "Discriminator cannot be de-referenced" );
}
@Override
public SqmTreatedPath treatAs(Class treatJavaType) throws PathException {
throw new UnsupportedMappingException( "Cannot apply TREAT operator to discriminator path" );
}
@Override
public SqmTreatedPath treatAs(EntityDomainType treatTarget) throws PathException {
throw new UnsupportedMappingException( "Cannot apply TREAT operator to discriminator path" );
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.metamodel.model.domain.internal;
import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.query.BindableType;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.spi.NavigablePath;
/**
* SqmPathSource implementation for {@link org.hibernate.annotations.AnyDiscriminator}
*
*/
public class AnyDiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
implements BindableType<D>, ReturnableType<D> {
public AnyDiscriminatorSqmPathSource(
String localPathName,
SimpleDomainType domainType,
BindableType jpaBindableType) {
super( localPathName, domainType, jpaBindableType );
}
@Override
public SqmPath<D> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
final NavigablePath navigablePath;
if ( intermediatePathSource == null ) {
navigablePath = lhs.getNavigablePath();
}
else {
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() );
}
return new AnyDiscriminatorSqmPath( navigablePath, this, lhs, lhs.nodeBuilder() );
}
@Override
public SqmPathSource<?> findSubPathSource(String name) {
throw new IllegalStateException( "Entity discriminator cannot be de-referenced" );
}
@Override
public PersistenceType getPersistenceType() {
return PersistenceType.BASIC;
}
@Override
public Class<D> getJavaType() {
return getExpressibleJavaType().getJavaTypeClass();
}
}

View File

@ -10,6 +10,7 @@ import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.type.AnyType;
import org.hibernate.type.BasicType;
import org.hibernate.type.MetaType;
import org.hibernate.type.descriptor.java.JavaType;
/**
@ -41,7 +42,7 @@ public class AnyMappingDomainTypeImpl<T> implements AnyMappingDomainType<T> {
@Override
public SimpleDomainType<?> getDiscriminatorType() {
return (BasicType<?>) anyType.getDiscriminatorType();
return (SimpleDomainType<?>) ((MetaType) anyType.getDiscriminatorType()).getBaseType();
}
@Override

View File

@ -22,6 +22,7 @@ import static jakarta.persistence.metamodel.Bindable.BindableType.SINGULAR_ATTRI
*/
public class AnyMappingSqmPathSource<J> extends AbstractSqmPathSource<J> implements BindableType<J> {
private final SqmPathSource<?> keyPathSource;
private final AnyDiscriminatorSqmPathSource discriminatorPathSource;
public AnyMappingSqmPathSource(
String localPathName,
@ -29,6 +30,7 @@ public class AnyMappingSqmPathSource<J> extends AbstractSqmPathSource<J> impleme
BindableType jpaBindableType) {
super( localPathName, domainType, jpaBindableType );
keyPathSource = new BasicSqmPathSource<>( "id", (BasicDomainType<?>) domainType.getKeyType(), SINGULAR_ATTRIBUTE );
discriminatorPathSource = new AnyDiscriminatorSqmPathSource<>( localPathName, domainType.getDiscriminatorType(), jpaBindableType );
}
@Override @SuppressWarnings("unchecked")
@ -41,6 +43,9 @@ public class AnyMappingSqmPathSource<J> extends AbstractSqmPathSource<J> impleme
if ( "id".equals( name ) ) {
return keyPathSource;
}
else if("{discriminator}".equals( name )) {
return discriminatorPathSource;
}
throw new UnsupportedMappingException( "De-referencing parts of an ANY mapping, other than the key, is not supported" );
}

View File

@ -56,6 +56,7 @@ import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
import org.hibernate.query.PathException;
import org.hibernate.query.ReturnableType;
@ -118,6 +119,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
import org.hibernate.query.sqm.tree.expression.SqmAny;
import org.hibernate.query.sqm.tree.expression.SqmAnyDiscriminatorValue;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmByUnit;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
@ -133,6 +135,7 @@ import org.hibernate.query.sqm.tree.expression.SqmExtractUnit;
import org.hibernate.query.sqm.tree.expression.SqmFormat;
import org.hibernate.query.sqm.tree.expression.SqmFunction;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
import org.hibernate.query.sqm.tree.expression.SqmNamedParameter;
import org.hibernate.query.sqm.tree.expression.SqmOver;
@ -2101,8 +2104,21 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
);
break;
}
left = (SqmExpression<?>) leftExpressionContext.accept( this );
right = (SqmExpression<?>) rightExpressionContext.accept( this );
final SqmExpression<?> l = (SqmExpression<?>) leftExpressionContext.accept( this );
final SqmExpression<?> r = (SqmExpression<?>) rightExpressionContext.accept( this );
if ( l instanceof AnyDiscriminatorSqmPath && r instanceof SqmLiteralEntityType ) {
left = l;
right = createDiscriminatorValue( (AnyDiscriminatorSqmPath) left, rightExpressionContext );
}
else if ( r instanceof AnyDiscriminatorSqmPath && l instanceof SqmLiteralEntityType ) {
left = createDiscriminatorValue( (AnyDiscriminatorSqmPath) r, leftExpressionContext );
right = r;
}
else {
left = l;
right = r;
}
// This is something that we used to support before 6 which is also used in our testsuite
if ( left instanceof SqmLiteralNull<?> ) {
return new SqmNullnessPredicate(
@ -2136,6 +2152,20 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
);
}
private <T> SqmExpression<T> createDiscriminatorValue(AnyDiscriminatorSqmPath anyDiscriminatorTypeSqmPath, HqlParser.ExpressionContext valueExpressionContext) {
final SqmPath<T> discriminatorSqmPath = anyDiscriminatorTypeSqmPath.getLhs();
final EntityDomainType<T> entityWithDiscriminator = creationContext.getJpaMetamodel()
.entity( discriminatorSqmPath.findRoot().getNavigablePath().getLocalName() );
final EntityDomainType<Object> entityDiscriminatorValue = creationContext.getJpaMetamodel()
.resolveHqlEntityReference( valueExpressionContext.getText() );
return new SqmAnyDiscriminatorValue<>(
entityWithDiscriminator,
discriminatorSqmPath.getNodeType().getPathName(),
entityDiscriminatorValue,
creationContext.getNodeBuilder()
);
}
private SqmExpression<?> resolveEnumShorthandLiteral(HqlParser.ExpressionContext expressionContext, Map<Class<?>, Enum<?>> possibleEnumValues, Class<?> enumType) {
final Enum<?> enumValue;
if ( possibleEnumValues != null && ( enumValue = possibleEnumValues.get( enumType ) ) != null ) {
@ -3335,7 +3365,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
//noinspection unchecked
return new SqmLiteral<>(
value,
(SqmExpressible<Number>) type,
type,
creationContext.getNodeBuilder()
);
}

View File

@ -8,6 +8,7 @@ package org.hibernate.query.sqm;
import java.util.List;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
import org.hibernate.query.sqm.sql.internal.SelfInterpretingSqmPath;
import org.hibernate.query.sqm.tree.cte.SqmCteContainer;
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
@ -29,6 +30,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmAny;
import org.hibernate.query.sqm.tree.expression.SqmAnyDiscriminatorValue;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmByUnit;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
@ -227,6 +229,10 @@ public interface SemanticQueryWalker<T> {
T visitEntityTypeLiteralExpression(SqmLiteralEntityType<?> expression);
T visitAnyDiscriminatorTypeExpression(AnyDiscriminatorSqmPath expression);
T visitAnyDiscriminatorTypeValueExpression(SqmAnyDiscriminatorValue expression);
T visitParameterizedEntityTypeExpression(SqmParameterizedEntityType<?> expression);
T visitUnaryOperationExpression(SqmUnaryOperation<?> expression);

View File

@ -9,6 +9,7 @@ package org.hibernate.query.sqm.internal;
import java.util.List;
import java.util.Locale;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
import org.hibernate.query.QueryLogging;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.sql.internal.SelfInterpretingSqmPath;
@ -33,6 +34,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmAny;
import org.hibernate.query.sqm.tree.expression.SqmAnyDiscriminatorValue;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmByUnit;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
@ -1044,6 +1046,16 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
return null;
}
@Override
public Object visitAnyDiscriminatorTypeExpression(AnyDiscriminatorSqmPath expression) {
return null;
}
@Override
public Object visitAnyDiscriminatorTypeValueExpression(SqmAnyDiscriminatorValue expression) {
return null;
}
@Override
public Object visitDynamicInstantiation(SqmDynamicInstantiation sqmDynamicInstantiation) {
processStanza(

View File

@ -8,6 +8,7 @@ package org.hibernate.query.sqm.spi;
import java.util.List;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.sql.internal.SelfInterpretingSqmPath;
import org.hibernate.query.sqm.tree.SqmStatement;
@ -34,6 +35,7 @@ import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmAggregateFunction;
import org.hibernate.query.sqm.tree.expression.SqmAny;
import org.hibernate.query.sqm.tree.expression.SqmAnyDiscriminatorValue;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmByUnit;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
@ -578,6 +580,16 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
return expression;
}
@Override
public Object visitAnyDiscriminatorTypeExpression(AnyDiscriminatorSqmPath expression) {
return expression;
}
@Override
public Object visitAnyDiscriminatorTypeValueExpression(SqmAnyDiscriminatorValue expression) {
return expression;
}
@Override
public Object visitParameterizedEntityTypeExpression(SqmParameterizedEntityType<?> expression) {
expression.getDiscriminatorSource().accept( this );

View File

@ -63,6 +63,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.Bindable;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.ConvertibleModelPart;
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
@ -83,6 +84,7 @@ import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.SqlExpressible;
import org.hibernate.metamodel.mapping.SqlTypedMapping;
import org.hibernate.metamodel.mapping.ValueMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
import org.hibernate.metamodel.mapping.internal.SqlTypedMappingImpl;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
@ -96,6 +98,7 @@ import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
import org.hibernate.query.derived.AnonymousTupleType;
import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource;
@ -138,6 +141,7 @@ import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
import org.hibernate.query.sqm.spi.BaseSemanticQueryWalker;
import org.hibernate.query.sqm.sql.internal.BasicValuedPathInterpretation;
import org.hibernate.query.sqm.sql.internal.DiscriminatedAssociationPathInterpretation;
import org.hibernate.query.sqm.sql.internal.DiscriminatedAssociationTypePathInterpretation;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.query.sqm.sql.internal.EmbeddableValuedPathInterpretation;
import org.hibernate.query.sqm.sql.internal.EntityValuedPathInterpretation;
@ -182,6 +186,7 @@ import org.hibernate.query.sqm.tree.expression.Conversion;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
import org.hibernate.query.sqm.tree.expression.SqmAny;
import org.hibernate.query.sqm.tree.expression.SqmAnyDiscriminatorValue;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmByUnit;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
@ -6227,6 +6232,60 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return new EntityTypeLiteral( mappingDescriptor );
}
@Override
public Expression visitAnyDiscriminatorTypeExpression(AnyDiscriminatorSqmPath expression) {
return DiscriminatedAssociationTypePathInterpretation.from( expression, this );
}
@Override
public Expression visitAnyDiscriminatorTypeValueExpression(SqmAnyDiscriminatorValue expression) {
final EntityPersister mappingDescriptor = creationContext.getSessionFactory()
.getRuntimeMetamodels()
.getMappingMetamodel()
.getEntityDescriptor( ((EntityDomainType)expression.getNodeType()).getHibernateEntityName() );
for ( AttributeMapping attributeMapping : mappingDescriptor.getAttributeMappings() ) {
if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
final ModelPart subPart = ( (EmbeddedAttributeMapping) attributeMapping ).findSubPart(
expression.getPathName(),
null
);
if ( subPart != null ) {
return buildQueryLiteral( expression, subPart );
}
}
else if ( attributeMapping.getAttributeName().equals( expression.getPathName() ) ) {
return buildQueryLiteral( expression, attributeMapping );
}
}
throw new HibernateException(String.format(
Locale.ROOT,
"Could not locate attribute mapping : %s -> %s",
mappingDescriptor.getEntityName(),
expression.getPathName()
));
}
private QueryLiteral<Object> buildQueryLiteral(
SqmAnyDiscriminatorValue expression,
ModelPart attributeMapping) {
final DiscriminatedAssociationModelPart discriminatedAssociationModelPart;
if ( attributeMapping instanceof PluralAttributeMapping ) {
discriminatedAssociationModelPart = (DiscriminatedAssociationModelPart) ( (PluralAttributeMapping) attributeMapping ).getElementDescriptor();
}
else {
assert attributeMapping instanceof DiscriminatedAssociationModelPart;
discriminatedAssociationModelPart = (DiscriminatedAssociationModelPart) attributeMapping;
}
final Object value = discriminatedAssociationModelPart.resolveDiscriminatorForEntityType(
creationContext.getMappingMetamodel()
.findEntityDescriptor( expression.getEntityValue().getHibernateEntityName() ) );
return new QueryLiteral<>(
value,
discriminatedAssociationModelPart.getDiscriminatorPart()
);
}
@Override
public Expression visitParameterizedEntityTypeExpression(SqmParameterizedEntityType<?> sqmExpression) {
assert resolveInferredType() instanceof EntityDiscriminatorMapping;

View File

@ -0,0 +1,95 @@
/*
* 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.query.sqm.sql.internal;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.AnyDiscriminatorPart;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.SqlAstWalker;
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;
public class DiscriminatedAssociationTypePathInterpretation<T> extends AbstractSqmPathInterpretation<T> {
private final Expression expression;
public static <T> DiscriminatedAssociationTypePathInterpretation<T> from(
AnyDiscriminatorSqmPath sqmPath,
SqmToSqlAstConverter converter) {
final SqmPath lhs = sqmPath.getLhs();
final TableGroup tableGroup = converter.getFromClauseAccess().findTableGroup( lhs.getLhs().getNavigablePath() );
final ModelPart subPart = tableGroup.getModelPart().findSubPart(
lhs.getNavigablePath().getLocalName(),
null
);
final DiscriminatedAssociationModelPart mapping;
if ( subPart instanceof PluralAttributeMapping ) {
PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) subPart;
mapping = (DiscriminatedAssociationModelPart) pluralAttributeMapping.getElementDescriptor();
}
else {
mapping = (DiscriminatedAssociationModelPart) subPart;
}
final List<Expression> tupleExpressions = new ArrayList<>();
mapping.forEachSelectable(
(selectionIndex, selectableMapping) -> {
if ( selectableMapping instanceof AnyDiscriminatorPart ) {
final TableReference tableReference = tableGroup.getPrimaryTableReference();
final Expression expression = converter.getSqlExpressionResolver().resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableReference,
selectableMapping.getSelectionExpression()
),
processingState -> new ColumnReference(
tableReference,
selectableMapping,
converter.getCreationContext().getSessionFactory()
)
);
tupleExpressions.add( expression );
}
}
);
assert tupleExpressions.size() == 1;
return new DiscriminatedAssociationTypePathInterpretation<T>(
sqmPath.getNavigablePath(),
mapping,
tableGroup,
tupleExpressions.get( 0 )
);
}
public DiscriminatedAssociationTypePathInterpretation(
NavigablePath navigablePath,
ModelPart mapping,
TableGroup tableGroup,
Expression expression) {
super( navigablePath, mapping, tableGroup );
this.expression = expression;
}
@Override
public void accept(SqlAstWalker sqlTreeWalker) {
expression.accept( sqlTreeWalker );
}
}

View File

@ -0,0 +1,92 @@
/*
* 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.query.sqm.tree.expression;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.hql.HqlInterpretationException;
import org.hibernate.query.hql.spi.SemanticPathPart;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
public class SqmAnyDiscriminatorValue<T> extends AbstractSqmExpression<T>
implements SqmSelectableNode<T>, SemanticPathPart {
private final EntityDomainType value;
private final String pathName;
public SqmAnyDiscriminatorValue(
EntityDomainType<T> entityWithDiscriminator,
String pathName,
EntityDomainType entityValue,
NodeBuilder nodeBuilder) {
super( entityWithDiscriminator, nodeBuilder );
this.value = entityValue;
this.pathName = pathName;
}
@Override
public SqmAnyDiscriminatorValue<T> copy(SqmCopyContext context) {
final SqmAnyDiscriminatorValue<T> existing = context.getCopy( this );
if ( existing != null ) {
return existing;
}
final SqmAnyDiscriminatorValue<T> expression = context.registerCopy(
this,
new SqmAnyDiscriminatorValue<>(
(EntityDomainType) getNodeType(),
pathName,
value,
nodeBuilder()
)
);
copyTo( expression, context );
return expression;
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitAnyDiscriminatorTypeValueExpression( this );
}
public EntityDomainType getEntityValue() {
return value;
}
public String getPathName() {
return pathName;
}
@Override
public String asLoggableText() {
return getEntityValue().getName();
}
@Override
public SemanticPathPart resolvePathPart(
String name,
boolean isTerminal,
SqmCreationState creationState) {
throw new HqlInterpretationException( "Cannot dereference an entity name" );
}
@Override
public SqmPath<?> resolveIndexedAccess(
SqmExpression<?> selector,
boolean isTerminal,
SqmCreationState creationState) {
throw new HqlInterpretationException( "Cannot dereference an entity name" );
}
@Override
public void appendHqlString(StringBuilder sb) {
sb.append( getEntityValue().getName() );
}
}

View File

@ -6,9 +6,12 @@
*/
package org.hibernate.orm.test.any.annotations;
import java.util.List;
import org.hibernate.LazyInitializationException;
import org.hibernate.query.spi.QueryImplementor;
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;
@ -16,6 +19,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@ -31,6 +35,7 @@ import static org.junit.Assert.fail;
LazyPropertySet.class,
PropertyMap.class,
PropertyList.class,
PropertyHolder.class,
CharProperty.class
}
)
@ -41,48 +46,68 @@ public class AnyTest {
scope.inTransaction(
session -> {
{
final PropertySet set1 = new PropertySet("string");
final Property property = new StringProperty("name", "Alex");
set1.setSomeProperty(property);
set1.addGeneralProperty(property);
session.save(set1);
final PropertySet set1 = new PropertySet( "string" );
final Property property = new StringProperty( "name", "Alex" );
set1.setSomeProperty( property );
set1.addGeneralProperty( property );
session.persist( set1 );
final PropertySet set2 = new PropertySet("integer");
final Property property2 = new IntegerProperty("age", 33);
set2.setSomeProperty(property2);
set2.addGeneralProperty(property2);
session.save(set2);
final PropertySet set2 = new PropertySet( "integer" );
final Property property2 = new IntegerProperty( "age", 33 );
set2.setSomeProperty( property2 );
set2.addGeneralProperty( property2 );
session.persist( set2 );
}
{
final PropertyMap map = new PropertyMap("sample");
map.getProperties().put("name", new StringProperty("name", "Alex"));
map.getProperties().put("age", new IntegerProperty("age", 33));
session.save(map);
final PropertyMap map = new PropertyMap( "sample" );
map.getProperties().put( "name", new StringProperty( "name", "Alex" ) );
map.getProperties().put( "age", new IntegerProperty( "age", 33 ) );
session.persist( map );
}
{
final PropertyList list = new PropertyList("sample");
final StringProperty stringProperty = new StringProperty("name", "Alex");
final IntegerProperty integerProperty = new IntegerProperty("age", 33);
final LongProperty longProperty = new LongProperty("distance", 121L);
final CharProperty charProp = new CharProperty("Est", 'E');
StringProperty nameProperty = new StringProperty( "name", "John Doe" );
session.persist( nameProperty );
list.setSomeProperty(longProperty);
PropertyHolder namePropertyHolder = new PropertyHolder();
namePropertyHolder.setId( 1 );
namePropertyHolder.setProperty( nameProperty );
list.addGeneralProperty(stringProperty);
list.addGeneralProperty(integerProperty);
list.addGeneralProperty(longProperty);
list.addGeneralProperty(charProp);
session.persist( namePropertyHolder );
session.save(list);
final IntegerProperty ageProperty = new IntegerProperty( "age", 23 );
session.persist( ageProperty );
PropertyHolder agePropertyHolder = new PropertyHolder();
agePropertyHolder.setId( 2 );
agePropertyHolder.setProperty( ageProperty );
session.persist( agePropertyHolder );
}
{
final PropertyList list = new PropertyList( "sample" );
final StringProperty stringProperty = new StringProperty( "name", "Alex" );
final IntegerProperty integerProperty = new IntegerProperty( "age", 33 );
final LongProperty longProperty = new LongProperty( "distance", 121L );
final CharProperty charProp = new CharProperty( "Est", 'E' );
list.setSomeProperty( longProperty );
list.addGeneralProperty( stringProperty );
list.addGeneralProperty( integerProperty );
list.addGeneralProperty( longProperty );
list.addGeneralProperty( charProp );
session.persist( list );
}
{
final LazyPropertySet set = new LazyPropertySet( "string" );
final Property property = new StringProperty( "name", "Alex" );
set.setSomeProperty( property );
session.save( set );
session.persist( set );
}
}
);
@ -92,14 +117,66 @@ public class AnyTest {
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete StringProperty" ).executeUpdate();
session.createQuery( "delete IntegerProperty" ).executeUpdate();
session.createQuery( "delete LongProperty" ).executeUpdate();
session.createQuery( "delete CharProperty" ).executeUpdate();
session.createMutationQuery( "delete StringProperty" ).executeUpdate();
session.createMutationQuery( "delete IntegerProperty" ).executeUpdate();
session.createMutationQuery( "delete LongProperty" ).executeUpdate();
session.createMutationQuery( "delete CharProperty" ).executeUpdate();
session.createQuery( "delete PropertyList" ).executeUpdate();
session.createQuery( "delete PropertyMap" ).executeUpdate();
session.createQuery( "delete PropertySet" ).executeUpdate();
session.createMutationQuery( "delete PropertyHolder" ).executeUpdate();
session.createMutationQuery( "delete PropertyList" ).executeUpdate();
session.createMutationQuery( "delete PropertyMap" ).executeUpdate();
session.createMutationQuery( "delete PropertySet" ).executeUpdate();
session.createMutationQuery( "delete LazyPropertySet" ).executeUpdate();
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-15323")
public void testHqlCollectionTypeQuery(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
List<PropertySet> propertySets = session.createQuery(
"select p from PropertySet p where type(p.generalProperties) = IntegerProperty ",
PropertySet.class ).list();
assertEquals( 1, propertySets.size() );
PropertySet propertySet = propertySets.get( 0 );
assertEquals( 1, propertySet.getGeneralProperties().size() );
assertEquals( "age", propertySet.getGeneralProperties().get( 0 ).getName() );
propertySets = session.createQuery(
"select p from PropertySet p where type(p.generalProperties) = StringProperty ",
PropertySet.class ).list();
assertEquals( 1, propertySets.size() );
propertySet = propertySets.get( 0 );
assertEquals( 1, propertySet.getGeneralProperties().size() );
assertEquals( "name", propertySet.getGeneralProperties().get( 0 ).getName() );
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-15323")
public void testHqlTypeQuery(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
List<PropertyHolder> propertyHolders = session.createQuery(
"select p from PropertyHolder p where type(p.property) = IntegerProperty ",
PropertyHolder.class ).list();
assertEquals( 1, propertyHolders.size() );
assertEquals( "age", propertyHolders.get( 0 ).getProperty().getName() );
propertyHolders = session.createQuery(
"select p from PropertyHolder p where type(p.property) = StringProperty ",
PropertyHolder.class ).list();
assertEquals( 1, propertyHolders.size() );
assertEquals( "name", propertyHolders.get( 0 ).getProperty().getName() );
}
);
}
@ -108,7 +185,10 @@ public class AnyTest {
public void testDefaultAnyAssociation(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final QueryImplementor<PropertySet> query = session.createQuery("select s from PropertySet s where name = :name", PropertySet.class);
final QueryImplementor<PropertySet> query = session.createQuery(
"select s from PropertySet s where name = :name",
PropertySet.class
);
{
final PropertySet result = query.setParameter( "name", "string" ).uniqueResult();
@ -192,8 +272,11 @@ public class AnyTest {
public void testFetchEager(SessionFactoryScope scope) {
final PropertySet result = scope.fromTransaction(
session -> {
final PropertySet localResult = session.createQuery("select s from PropertySet s where name = :name", PropertySet.class)
.setParameter("name", "string")
final PropertySet localResult = session.createQuery(
"select s from PropertySet s where name = :name",
PropertySet.class
)
.setParameter( "name", "string" )
.getSingleResult();
assertNotNull( localResult );
assertNotNull( localResult.getSomeProperty() );
@ -210,8 +293,11 @@ public class AnyTest {
public void testFetchLazy(SessionFactoryScope scope) {
final LazyPropertySet result = scope.fromTransaction(
session -> {
final LazyPropertySet localResult = session.createQuery("select s from LazyPropertySet s where name = :name", LazyPropertySet.class)
.setParameter("name", "string")
final LazyPropertySet localResult = session.createQuery(
"select s from LazyPropertySet s where name = :name",
LazyPropertySet.class
)
.setParameter( "name", "string" )
.getSingleResult();
assertNotNull( localResult );
assertNotNull( localResult.getSomeProperty() );

View File

@ -6,11 +6,14 @@
*/
package org.hibernate.orm.test.any.annotations;
import java.util.List;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyDiscriminator;
import org.hibernate.annotations.AnyDiscriminatorValue;
import org.hibernate.annotations.AnyKeyJavaClass;
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;
@ -33,6 +36,7 @@ import static org.junit.Assert.assertTrue;
@DomainModel( annotatedClasses = { EmbeddedAnyTest.Foo.class, EmbeddedAnyTest.Bar1.class, EmbeddedAnyTest.Bar2.class } )
@SessionFactory
public class EmbeddedAnyTest {
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
@ -78,6 +82,28 @@ public class EmbeddedAnyTest {
);
}
@Test
@TestForIssue( jiraKey = "HHH-15323")
public void testEmbeddedTypeHql(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final List<Foo> foos = session.createQuery( "from Foo f where type(f.fooEmbedded.bar) = Bar1", Foo.class )
.list();
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-15323")
public void testEmbeddedTypeHql2(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final List<Foo> foos = session.createQuery( "from Foo f where Bar1 = type(f.fooEmbedded.bar)", Foo.class )
.list();
}
);
}
@Entity(name = "Foo")
public static class Foo {

View File

@ -6,9 +6,9 @@
*/
package org.hibernate.orm.test.any.annotations;
public interface Property {
public String getName();
public String asString();
String getName();
String asString();
}

View File

@ -0,0 +1,52 @@
/*
* 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.any.annotations;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyDiscriminator;
import org.hibernate.annotations.AnyDiscriminatorValue;
import org.hibernate.annotations.AnyKeyJavaClass;
import jakarta.persistence.Column;
import jakarta.persistence.DiscriminatorType;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Table;
@Entity
@Table(name = "property_holder")
public class PropertyHolder {
@Id
private Integer id;
@Any
@AnyDiscriminator(DiscriminatorType.STRING)
@AnyDiscriminatorValue(discriminator = "S", entity = StringProperty.class)
@AnyDiscriminatorValue(discriminator = "I", entity = IntegerProperty.class)
@AnyKeyJavaClass(Integer.class)
@Column(name = "property_type")
@JoinColumn(name = "property_id")
private Property property;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Property getProperty() {
return property;
}
public void setProperty(Property property) {
this.property = property;
}
}