From e1ed7e96d576081010aed407acedc9a7317809bd Mon Sep 17 00:00:00 2001 From: lvydra Date: Fri, 12 Sep 2025 11:54:50 +0200 Subject: [PATCH] [HHH-16739][JBEAP-30930] Several failures when comparing enum parameters with constant values --- .../function/InverseDistributionFunction.java | 7 +- .../metamodel/model/domain/DomainType.java | 6 + .../model/domain/EntityDomainType.java | 5 + .../domain/MappedSuperclassDomainType.java | 4 + .../AnyDiscriminatorSqmPathSource.java | 6 + .../domain/internal/BasicSqmPathSource.java | 6 + .../internal/DiscriminatorSqmPathSource.java | 5 + .../domain/internal/MappingMetamodelImpl.java | 3 +- .../query/derived/AnonymousTupleType.java | 7 +- .../hql/internal/SemanticQueryBuilder.java | 45 ++-- .../hibernate/query/sqm/SqmExpressible.java | 3 + .../hibernate/query/sqm/SqmPathSource.java | 5 + .../sqm/internal/SqmCriteriaNodeBuilder.java | 15 +- .../sqm/internal/SqmMappingModelHelper.java | 27 ++- .../hibernate/query/sqm/internal/SqmUtil.java | 38 +-- .../sqm/sql/BaseSqmToSqlAstConverter.java | 19 +- .../tree/domain/SqmBasicValuedSimplePath.java | 11 + .../query/sqm/tree/domain/SqmCteRoot.java | 5 + .../query/sqm/tree/domain/SqmDerivedRoot.java | 5 + .../domain/SqmEmbeddedValuedSimplePath.java | 11 + .../sqm/tree/domain/SqmMapEntryReference.java | 6 + .../expression/AbstractSqmExpression.java | 5 +- .../tree/expression/AbstractSqmParameter.java | 7 +- .../tree/expression/NullSqmExpressible.java | 6 + .../sqm/tree/expression/SqmCaseSearched.java | 2 +- .../sqm/tree/expression/SqmCaseSimple.java | 4 +- .../sqm/tree/expression/SqmEnumLiteral.java | 13 +- .../sqm/tree/expression/SqmFieldLiteral.java | 6 + .../tree/predicate/SqmBetweenPredicate.java | 6 +- .../predicate/SqmComparisonPredicate.java | 4 +- .../tree/predicate/SqmInListPredicate.java | 2 +- .../predicate/SqmInSubQueryPredicate.java | 4 +- .../sqm/tree/predicate/SqmLikePredicate.java | 4 +- .../tree/select/SqmDynamicInstantiation.java | 6 + .../tree/select/SqmJpaCompoundSelection.java | 6 + .../converted/enums/VarcharEnumTypeTest.java | 221 ++++++++++++++++++ 36 files changed, 458 insertions(+), 77 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/enums/VarcharEnumTypeTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/InverseDistributionFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/InverseDistributionFunction.java index a96212edf6d3..9fc69d4dc8ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/InverseDistributionFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/InverseDistributionFunction.java @@ -153,8 +153,11 @@ public SelfRenderingInverseDistributionFunction( @Override protected ReturnableType resolveResultType(TypeConfiguration typeConfiguration) { - return (ReturnableType) withinGroupClause.getSortSpecifications().get( 0 ).getSortExpression() - .getExpressible(); + return (ReturnableType) + getWithinGroup().getSortSpecifications().get( 0 ) + .getSortExpression() + .getExpressible() + .getSqmType(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/DomainType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/DomainType.java index 71066935b16e..68d0a7f16620 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/DomainType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/DomainType.java @@ -26,6 +26,12 @@ * @author Steve Ebersole */ public interface DomainType extends SqmExpressible { + + @Override + default DomainType getSqmType() { + return this; + } + /** * The name of the type. * diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/EntityDomainType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/EntityDomainType.java index f68925ba9a0e..3a7e22b76bad 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/EntityDomainType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/EntityDomainType.java @@ -22,4 +22,9 @@ public interface EntityDomainType extends IdentifiableDomainType, EntityTy @Override Collection> getSubTypes(); + + @Override + default DomainType getSqmType() { + return this; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/MappedSuperclassDomainType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/MappedSuperclassDomainType.java index 567db2503746..fbe879089f5b 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/MappedSuperclassDomainType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/MappedSuperclassDomainType.java @@ -16,4 +16,8 @@ * @author Steve Ebersole */ public interface MappedSuperclassDomainType extends IdentifiableDomainType, MappedSuperclassType, SqmPathSource { + @Override + default DomainType getSqmType() { + return IdentifiableDomainType.super.getSqmType(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPathSource.java index 0e9ca3244605..a7e46deb0be0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPathSource.java @@ -6,6 +6,7 @@ */ package org.hibernate.metamodel.model.domain.internal; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.SimpleDomainType; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.SqmPathSource; @@ -61,6 +62,11 @@ public BasicType getSqmPathType() { return (BasicType) super.getSqmPathType(); } + @Override + public DomainType getSqmType() { + return getSqmPathType(); + } + @Override public JavaType getExpressibleJavaType() { return getSqmPathType().getExpressibleJavaType(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java index b920b2cf2da3..d08ecb44c0fe 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java @@ -7,6 +7,7 @@ package org.hibernate.metamodel.model.domain.internal; import org.hibernate.metamodel.model.domain.BasicDomainType; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.ReturnableType; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath; @@ -41,6 +42,11 @@ public BasicDomainType getSqmPathType() { return (BasicDomainType) super.getSqmPathType(); } + @Override + public DomainType getSqmType() { + return getSqmPathType(); + } + @Override public SqmPathSource findSubPathSource(String name) { throw new IllegalStateException( "Basic paths cannot be dereferenced" ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DiscriminatorSqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DiscriminatorSqmPathSource.java index 3b6e7c46e679..2aaafbae38ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DiscriminatorSqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DiscriminatorSqmPathSource.java @@ -60,4 +60,9 @@ public PersistenceType getPersistenceType() { public Class getJavaType() { return getExpressibleJavaType().getJavaTypeClass(); } + + @Override + public DomainType getSqmType() { + return this; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java index 779fba05c015..b1aea66004f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java @@ -756,8 +756,7 @@ private String[] doGetImplementors(Class clazz) throws MappingException { @Override public MappingModelExpressible resolveMappingExpressible( SqmExpressible sqmExpressible, - Function tableGroupLocator) { + Function tableGroupLocator) { if ( sqmExpressible instanceof SqmPath ) { final SqmPath sqmPath = (SqmPath) sqmExpressible; final NavigablePath navigablePath = sqmPath.getNavigablePath(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleType.java b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleType.java index 7e45f620a8cb..e0bae0d71df0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleType.java @@ -212,7 +212,7 @@ else if ( sqmPath.getNodeType() instanceof EntityDomainType ) { else { return new AnonymousTupleSimpleSqmPathSource<>( name, - (DomainType) component.getExpressible(), + component.getExpressible().getSqmType(), BindableType.SINGULAR_ATTRIBUTE ); } @@ -244,6 +244,11 @@ public DomainType getSqmPathType() { return this; } + @Override + public DomainType getSqmType() { + return this; + } + @Override public SqmPath createSqmPath(SqmPath lhs, SqmPathSource intermediatePathSource) { throw new UnsupportedMappingException( diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 13917efce855..bcd72a287d27 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -26,6 +26,7 @@ import java.util.Calendar; import java.util.Collections; import java.util.GregorianCalendar; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -121,6 +122,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor; +import org.hibernate.query.sqm.tree.expression.AbstractSqmParameter; import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef; import org.hibernate.query.sqm.tree.expression.SqmAny; import org.hibernate.query.sqm.tree.expression.SqmAnyDiscriminatorValue; @@ -310,6 +312,7 @@ public static SqmStatement buildSemanticModel( private ParameterCollector parameterCollector; private ParameterStyle parameterStyle; + private Map> parameters; private boolean isExtractingJdbcTemporalType; // Provides access to the current CTE that is being processed, which is potentially recursive @@ -3832,14 +3835,14 @@ private SqmNamedParameter visitNamedParameter( HqlParser.NamedParameterContext ctx, SqmExpressible expressibleType) { parameterStyle = parameterStyle.withNamed(); - final SqmNamedParameter param = new SqmNamedParameter<>( - ctx.getChild( 1 ).getText(), - parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(), - expressibleType, - creationContext.getNodeBuilder() + return resolveParameter( + new SqmNamedParameter<>( + ctx.getChild( 1 ).getText(), + parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(), + expressibleType, + creationContext.getNodeBuilder() + ) ); - parameterCollector.addParameter( param ); - return param; } @Override @@ -3854,14 +3857,28 @@ private SqmPositionalParameter visitPositionalParameter( throw new SemanticException( "Unlabeled ordinal parameter ('?' rather than ?1)" ); } parameterStyle = parameterStyle.withPositional(); - final SqmPositionalParameter param = new SqmPositionalParameter<>( - Integer.parseInt( ctx.getChild( 1 ).getText() ), - parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(), - expressibleType, - creationContext.getNodeBuilder() + return resolveParameter( + new SqmPositionalParameter<>( + Integer.parseInt( ctx.getChild( 1 ).getText() ), + parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(), + expressibleType, + creationContext.getNodeBuilder() + ) ); - parameterCollector.addParameter( param ); - return param; + } + + private > T resolveParameter(T parameter) { + if ( parameters == null ) { + parameters = new HashMap<>(); + } + final Object key = parameter.getName() == null ? parameter.getPosition() : parameter.getName(); + final AbstractSqmParameter existingParameter = parameters.putIfAbsent( key, parameter ); + if ( existingParameter == null ) { + parameterCollector.addParameter( parameter ); + return parameter; + } + //noinspection unchecked + return (T) existingParameter; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmExpressible.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmExpressible.java index a50c5cfd8e33..fafdb2b07b2d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmExpressible.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmExpressible.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.BindableType; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.type.descriptor.java.JavaType; @@ -38,4 +39,6 @@ default boolean isInstance(J value) { default SqmExpressible resolveExpressible(SessionFactoryImplementor sessionFactory) { return this; } + + DomainType getSqmType(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java index ec405f2e1d03..1bf15b5776ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java @@ -87,6 +87,11 @@ default SqmExpressible getExpressible() { return (SqmExpressible) getSqmPathType(); } + @Override + default DomainType getSqmType() { + return (DomainType) getSqmPathType(); + } + /** * Indicates if this path source is generically typed */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java index a82bd4002579..b3717dac1ebc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java @@ -1323,6 +1323,11 @@ public JavaType getExpressibleJavaType() { public Class getBindableJavaType() { return javaType.getJavaTypeClass(); } + + @Override + public DomainType getSqmType() { + return null; + } } @Override @@ -1902,8 +1907,8 @@ public SqmCoalesce coalesce() { public JpaCoalesce coalesce(Expression x, Expression y) { @SuppressWarnings("unchecked") final SqmExpressible sqmExpressible = (SqmExpressible) highestPrecedenceType( - ( (SqmExpression) x ).getNodeType(), - ( (SqmExpression) y ).getNodeType() + ( (SqmExpression) x ).getExpressible(), + ( (SqmExpression) y ).getExpressible() ); return new SqmCoalesce<>( sqmExpressible, @@ -1934,9 +1939,9 @@ public SqmExpression nullif(Expression x, Y y) { private SqmExpression createNullifFunctionNode(SqmExpression first, SqmExpression second) { //noinspection unchecked final ReturnableType type = (ReturnableType) highestPrecedenceType( - first.getNodeType(), - second.getNodeType() - ); + first.getExpressible(), + second.getExpressible() + ).getSqmType(); return getFunctionDescriptor("nullif").generateSqmExpression( asList( first, second ), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java index e28102196ab5..151576794237 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmMappingModelHelper.java @@ -32,8 +32,10 @@ import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; +import org.hibernate.query.sqm.tree.cte.SqmCteTable; import org.hibernate.query.sqm.tree.domain.AbstractSqmSpecificPluralPartPath; import org.hibernate.query.sqm.tree.domain.SqmPath; +import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -212,13 +214,34 @@ private static ModelPart resolveSqmPath( } if ( sqmPath.getLhs() == null ) { - final EntityDomainType entityDomainType = (EntityDomainType) sqmPath.getReferencedPathSource(); - return domainModel.findEntityDescriptor( entityDomainType.getHibernateEntityName() ); + final SqmPathSource referencedPathSource = sqmPath.getReferencedPathSource(); + if ( referencedPathSource instanceof EntityDomainType ) { + final EntityDomainType entityDomainType = (EntityDomainType) referencedPathSource; + return domainModel.findEntityDescriptor( entityDomainType.getHibernateEntityName() ); + } + assert referencedPathSource instanceof SqmCteTable; + return null; } final TableGroup lhsTableGroup = tableGroupLocator.apply( sqmPath.getLhs().getNavigablePath() ); final ModelPartContainer modelPart; if ( lhsTableGroup == null ) { modelPart = (ModelPartContainer) resolveSqmPath( sqmPath.getLhs(), domainModel, tableGroupLocator ); + if ( modelPart == null ) { + // There are many reasons for why this situation can happen, + // but they all boil down to a parameter being compared against a SqmPath. + + // * If the parameter is used in multiple queries (CTE or subquery), + // resolving the parameter type based on a SqmPath from a query context other than the current one will fail. + + // * If the parameter is compared to paths with a polymorphic root, + // the parameter has a SqmPath set as SqmExpressible + // which is still referring to the polymorphic navigable path, + // but during query splitting, the SqmRoot in the query is replaced with a root for a subtype. + // Unfortunately, we can't copy the parameter to reset the SqmExpressible, + // because we currently build only a single DomainParameterXref, instead of one per query split, + // so we have to handle this here instead + return null; + } } else { modelPart = lhsTableGroup.getModelPart(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java index 1fc0b70b5a8a..e6f353c8b819 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java @@ -379,6 +379,7 @@ else if ( domainParamBinding.isMultiValued() ) { final Iterator valueItr = bindValues.iterator(); // the original SqmParameter is the one we are processing.. create a binding for it.. + final Object firstValue = valueItr.next(); for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) { final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i ); createValueBindings( @@ -387,7 +388,7 @@ else if ( domainParamBinding.isMultiValued() ) { domainParamBinding, parameterType, jdbcParams, - valueItr.next(), + firstValue, tableGroupLocator, session ); @@ -395,23 +396,30 @@ else if ( domainParamBinding.isMultiValued() ) { // an then one for each of the expansions final List> expansions = domainParameterXref.getExpansions( sqmParameter ); - assert expansions.size() == bindValues.size() - 1; + final int expansionCount = bindValues.size() - 1; + final int parameterUseCount = jdbcParamsBinds.size(); + assert expansions.size() == expansionCount * parameterUseCount; int expansionPosition = 0; while ( valueItr.hasNext() ) { - final SqmParameter expansionSqmParam = expansions.get( expansionPosition++ ); - final List jdbcParamBinds = jdbcParamMap.get( expansionSqmParam ); - for ( int i = 0; i < jdbcParamBinds.size(); i++ ) { - JdbcParametersList expansionJdbcParams = jdbcParamBinds.get( i ); - createValueBindings( - jdbcParameterBindings, - queryParam, domainParamBinding, - parameterType, - expansionJdbcParams, - valueItr.next(), - tableGroupLocator, - session - ); + final Object expandedValue = valueItr.next(); + for ( int j = 0; j < parameterUseCount; j++ ) { + final SqmParameter expansionSqmParam = expansions.get( expansionPosition + j * expansionCount ); + final List jdbcParamBinds = jdbcParamMap.get( expansionSqmParam ); + for ( int i = 0; i < jdbcParamBinds.size(); i++ ) { + JdbcParametersList expansionJdbcParams = jdbcParamBinds.get( i ); + createValueBindings( + jdbcParameterBindings, + queryParam, + domainParamBinding, + parameterType, + expansionJdbcParams, + expandedValue, + tableGroupLocator, + session + ); + } } + expansionPosition++; } } else if ( domainParamBinding.getBindValue() == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 25eccf163b01..c0e75d11fe9c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -5626,11 +5626,14 @@ private MappingModelExpressible determineValueMapping(SqmExpression sqmExp if ( sqmExpression instanceof SqmPath ) { log.debugf( "Determining mapping-model type for SqmPath : %s ", sqmExpression ); - return SqmMappingModelHelper.resolveMappingModelExpressible( + final MappingModelExpressible mappingModelExpressible = SqmMappingModelHelper.resolveMappingModelExpressible( sqmExpression, domainModel, fromClauseIndex::findTableGroup ); + if ( mappingModelExpressible != null ) { + return mappingModelExpressible; + } } if ( sqmExpression instanceof SqmBooleanExpressionPredicate ) { @@ -5791,19 +5794,7 @@ else if ( paramType instanceof EntityDomainType ) { final SqmExpressible paramSqmType = paramType.resolveExpressible( creationContext.getSessionFactory() ); if ( paramSqmType instanceof SqmPath ) { - final SqmPath sqmPath = (SqmPath) paramSqmType; - final NavigablePath navigablePath = sqmPath.getNavigablePath(); - final ModelPart modelPart; - if ( navigablePath.getParent() != null ) { - final TableGroup tableGroup = getFromClauseAccess().getTableGroup( navigablePath.getParent() ); - modelPart = tableGroup.getModelPart().findSubPart( - navigablePath.getLocalName(), - null - ); - } - else { - modelPart = getFromClauseAccess().getTableGroup( navigablePath ).getModelPart(); - } + final MappingModelExpressible modelPart = determineValueMapping( (SqmPath) paramSqmType ); if ( modelPart instanceof PluralAttributeMapping ) { return resolveInferredValueMappingForParameter( ( (PluralAttributeMapping) modelPart ).getElementDescriptor() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java index 2be7a0170c66..c707111bded4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java @@ -6,6 +6,7 @@ */ package org.hibernate.query.sqm.tree.domain; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.spi.NavigablePath; import org.hibernate.query.PathException; @@ -64,6 +65,11 @@ public SqmBasicValuedSimplePath copy(SqmCopyContext context) { return path; } + @Override + public SqmExpressible getExpressible() { + return this; + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SemanticPathPart @@ -124,6 +130,11 @@ public Class getBindableJavaType() { return getJavaType(); } + @Override + public DomainType getSqmType() { + return getNodeType().getSqmType(); + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Visitation diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java index f16ed137cc7e..db11cdaf805b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java @@ -90,6 +90,11 @@ public EntityDomainType getModel() { return null; } + @Override + public String getEntityName() { + return null; + } + @Override public SqmPathSource getResolvedModel() { return getReferencedPathSource(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java index 9d31e5d4818a..180d6e75ba02 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java @@ -92,6 +92,11 @@ public EntityDomainType getModel() { return null; } + @Override + public String getEntityName() { + return null; + } + @Override public SqmPathSource getResolvedModel() { return getReferencedPathSource(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEmbeddedValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEmbeddedValuedSimplePath.java index dff793ca3009..d63522739328 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEmbeddedValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEmbeddedValuedSimplePath.java @@ -6,6 +6,7 @@ */ package org.hibernate.query.sqm.tree.domain; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.EmbeddableDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.PathException; @@ -68,6 +69,16 @@ public SqmEmbeddedValuedSimplePath copy(SqmCopyContext context) { return path; } + @Override + public SqmExpressible getExpressible() { + return this; + } + + @Override + public DomainType getSqmType() { + return getReferencedPathSource().getSqmType(); + } + @Override public SqmPath resolvePathPart( String name, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java index ec6330504d1c..eb348ac602ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java @@ -12,6 +12,7 @@ import java.util.Map; import java.util.function.Consumer; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; @@ -121,6 +122,11 @@ public SqmExpressible> getNodeType() { return this; } + @Override + public DomainType> getSqmType() { + return null; + } + @Override public Class> getBindableJavaType() { return getNodeType().getBindableJavaType(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmExpression.java index 3cb86cdf8fbd..8e3afe48ecdf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmExpression.java @@ -24,6 +24,7 @@ import jakarta.persistence.criteria.Expression; import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType; +import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType2; /** * @author Steve Ebersole @@ -57,11 +58,11 @@ protected void internalApplyInferableType(SqmExpressible newType) { SqmTreeCreationLogger.LOGGER.debugf( "Applying inferable type to SqmExpression [%s] : %s -> %s", this, - getNodeType(), + getExpressible(), newType ); - setExpressibleType( highestPrecedenceType( newType, getNodeType() ) ); + setExpressibleType( highestPrecedenceType2( newType, getExpressible() ) ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmParameter.java index 571f9ba0d71a..43224b7cbd57 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmParameter.java @@ -36,12 +36,7 @@ public void applyInferableType(SqmExpressible type) { else if ( type instanceof PluralPersistentAttribute ) { type = ( (PluralPersistentAttribute) type ).getElementType(); } - final SqmExpressible oldType = getNodeType(); - - final SqmExpressible newType = QueryHelper.highestPrecedenceType( oldType, type ); - if ( newType != null && newType != oldType ) { - internalApplyInferableType( newType ); - } + internalApplyInferableType( type ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/NullSqmExpressible.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/NullSqmExpressible.java index c5853f3bd929..b4a51568794b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/NullSqmExpressible.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/NullSqmExpressible.java @@ -6,6 +6,7 @@ */ package org.hibernate.query.sqm.tree.expression; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.type.descriptor.java.JavaType; @@ -27,4 +28,9 @@ public Class getBindableJavaType() { public JavaType getExpressibleJavaType() { return null; } + + @Override + public DomainType getSqmType() { + return null; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java index e63cbe9da460..4c251f735155 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java @@ -96,7 +96,7 @@ private void applyInferableResultType(SqmExpressible type) { return; } - final SqmExpressible oldType = getNodeType(); + final SqmExpressible oldType = getExpressible(); final SqmExpressible newType = QueryHelper.highestPrecedenceType2( oldType, type ); if ( newType != null && newType != oldType ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java index 41bdf80c7595..ec32fc8be6f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java @@ -104,9 +104,9 @@ private void applyInferableResultType(SqmExpressible type) { return; } - final SqmExpressible oldType = getNodeType(); + final SqmExpressible oldType = getExpressible(); - final SqmExpressible newType = QueryHelper.highestPrecedenceType2(oldType, type ); + final SqmExpressible newType = QueryHelper.highestPrecedenceType2( oldType, type ); if ( newType != null && newType != oldType ) { internalApplyInferableType( newType ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java index 8870283b8365..405c19dd0201 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java @@ -10,6 +10,7 @@ import java.math.BigInteger; import java.util.Locale; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.SemanticException; import org.hibernate.query.hql.spi.SemanticPathPart; import org.hibernate.query.hql.spi.SqmCreationState; @@ -26,7 +27,7 @@ * * @author Steve Ebersole */ -public class SqmEnumLiteral> extends AbstractSqmExpression implements SqmExpressible, SemanticPathPart { +public class SqmEnumLiteral> extends SqmLiteral implements SqmExpressible, SemanticPathPart { private final E enumValue; private final EnumJavaType referencedEnumTypeDescriptor; private final String enumValueName; @@ -62,6 +63,16 @@ public SqmEnumLiteral copy(SqmCopyContext context) { return expression; } + @Override + public SqmExpressible getExpressible() { + return this; + } + + @Override + public DomainType getSqmType() { + return null; + } + public E getEnumValue() { return enumValue; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java index c5e1e755178b..39f1836becf0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java @@ -14,6 +14,7 @@ import java.util.Locale; import org.hibernate.QueryException; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.SemanticException; import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.hql.spi.SemanticPathPart; @@ -275,4 +276,9 @@ public String getAlias() { return null; } + @Override + public DomainType getSqmType() { + return null; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBetweenPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBetweenPredicate.java index ce32c56f2aab..5d1143878d44 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBetweenPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBetweenPredicate.java @@ -33,9 +33,9 @@ public SqmBetweenPredicate( this.upperBound = upperBound; final SqmExpressible expressibleType = QueryHelper.highestPrecedenceType( - expression.getNodeType(), - lowerBound.getNodeType(), - upperBound.getNodeType() + expression.getExpressible(), + lowerBound.getExpressible(), + upperBound.getExpressible() ); expression.applyInferableType( expressibleType ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmComparisonPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmComparisonPredicate.java index 7cd0eecf70eb..7496f4ad9fa7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmComparisonPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmComparisonPredicate.java @@ -42,8 +42,8 @@ private SqmComparisonPredicate( this.operator = operator; final SqmExpressible expressibleType = QueryHelper.highestPrecedenceType( - leftHandExpression.getNodeType(), - rightHandExpression.getNodeType() + leftHandExpression.getExpressible(), + rightHandExpression.getExpressible() ); leftHandExpression.applyInferableType( expressibleType ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java index 424fffef8751..7db208d14b6c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java @@ -137,7 +137,7 @@ public void addExpression(SqmExpression expression) { private void implyListElementType(SqmExpression expression) { nodeBuilder().assertComparable( getTestExpression(), expression ); expression.applyInferableType( - QueryHelper.highestPrecedenceType2( getTestExpression().getNodeType(), expression.getNodeType() ) + QueryHelper.highestPrecedenceType2( getTestExpression().getExpressible(), expression.getExpressible() ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java index 178162a7f50e..36adafd2568b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java @@ -41,8 +41,8 @@ public SqmInSubQueryPredicate( this.subQueryExpression = subQueryExpression; final SqmExpressible expressibleType = QueryHelper.highestPrecedenceType2( - testExpression.getNodeType(), - subQueryExpression.getNodeType() + testExpression.getExpressible(), + subQueryExpression.getExpressible() ); testExpression.applyInferableType( expressibleType ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmLikePredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmLikePredicate.java index 778e91545fb1..ab5d8590bb73 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmLikePredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmLikePredicate.java @@ -52,8 +52,8 @@ public SqmLikePredicate( this.escapeCharacter = escapeCharacter; this.isCaseSensitive = isCaseSensitive; final SqmExpressible expressibleType = QueryHelper.highestPrecedenceType( - matchExpression.getNodeType(), - pattern.getNodeType() + matchExpression.getExpressible(), + pattern.getExpressible() ); matchExpression.applyInferableType( expressibleType ); pattern.applyInferableType( expressibleType ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java index e9485c2fc712..42d1e6df9ccf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.function.Consumer; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.sqm.DynamicInstantiationNature; import org.hibernate.query.criteria.JpaCompoundSelection; import org.hibernate.query.sqm.NodeBuilder; @@ -265,6 +266,11 @@ public JavaType getExpressibleJavaType() { public Class getBindableJavaType() { return getTargetTypeDescriptor().getJavaTypeClass(); } + + @Override + public DomainType getSqmType() { + return null; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java index b5568f9924be..c5fbb5ff16e2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.function.Consumer; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.criteria.JpaCompoundSelection; import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.sqm.NodeBuilder; @@ -149,4 +150,9 @@ public void appendHqlString(StringBuilder sb) { public void visitSubSelectableNodes(Consumer> jpaSelectionConsumer) { selectableNodes.forEach( jpaSelectionConsumer ); } + + @Override + public DomainType getSqmType() { + return null; + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/enums/VarcharEnumTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/enums/VarcharEnumTypeTest.java new file mode 100644 index 000000000000..0bf13dd3bc8b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/enums/VarcharEnumTypeTest.java @@ -0,0 +1,221 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.mapping.converted.enums; + +import org.hibernate.type.descriptor.JdbcBindingLogging; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.Logger; +import org.hibernate.testing.orm.junit.MessageKeyInspection; +import org.hibernate.testing.orm.junit.MessageKeyWatcher; +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.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +import static org.junit.Assert.assertTrue; + +@MessageKeyInspection( + logger = @Logger( loggerName = JdbcBindingLogging.NAME ), + messageKey = "binding parameter [" +) +@DomainModel( annotatedClasses = VarcharEnumTypeTest.Person.class ) +@SessionFactory +public class VarcharEnumTypeTest { + @BeforeEach + protected void createTestData(SessionFactoryScope scope) { + scope.inTransaction( + (session) -> { + final Person person = Person.person( Gender.MALE, HairColor.BROWN ); + session.persist( person ); + session.persist( Person.person( Gender.MALE, HairColor.BLACK ) ); + session.persist( Person.person( Gender.FEMALE, HairColor.BROWN ) ); + session.persist( Person.person( Gender.FEMALE, HairColor.BLACK ) ); + } + ); + } + + @AfterEach + public void dropTestData(SessionFactoryScope scope) { + scope.inTransaction( + (session) -> session.createQuery( "delete Person" ).executeUpdate() + ); + } + + @Test + @JiraKey("HHH-12978") + public void testEnumAsBindParameterAndExtract(SessionFactoryScope scope, MessageKeyWatcher loggingWatcher) { + scope.inTransaction( + (session) -> { + session.createQuery( "select p.id from Person p where p.id = :id", Long.class ) + .setParameter( "id", 1L ) + .list(); + + assertTrue( loggingWatcher.wasTriggered() ); + } + ); + + loggingWatcher.reset(); + + scope.inTransaction( + (session) -> { + final String qry = "select p.gender from Person p where p.gender = :gender and p.hairColor = :hairColor"; + session.createQuery( qry, Gender.class ) + .setParameter( "gender", Gender.MALE ) + .setParameter( "hairColor", HairColor.BROWN ) + .getSingleResult(); + + assertTrue( loggingWatcher.wasTriggered() ); + } + ); + } + + @Test + @JiraKey("HHH-10282") + public void hqlTestEnumShortHandSyntax(SessionFactoryScope scope, MessageKeyWatcher loggingWatcher) { + scope.inTransaction( + (session) -> { + session.createQuery( + "select id from Person where originalHairColor = BLONDE") + .getResultList(); + + assertTrue( loggingWatcher.wasTriggered() ); + } + ); + } + + @Test + @JiraKey("HHH-10282") + public void hqlTestEnumQualifiedShortHandSyntax(SessionFactoryScope scope, MessageKeyWatcher loggingWatcher) { + final String qry = "select id from Person where originalHairColor = HairColor.BLONDE"; + scope.inTransaction( + (session) -> { + session.createQuery( qry ).getResultList(); + + assertTrue( loggingWatcher.wasTriggered() ); + } + ); + } + + @Test + @JiraKey("HHH-10282") + public void hqlTestEnumShortHandSyntaxInPredicate(SessionFactoryScope scope, MessageKeyWatcher loggingWatcher) { + scope.inTransaction( + (session) -> { + final String qry = "select id from Person where originalHairColor in (BLONDE, BROWN)"; + session.createQuery( qry ).getResultList(); + + assertTrue( loggingWatcher.wasTriggered() ); + } + ); + } + + @Test + @JiraKey("HHH-10282") + public void hqlTestEnumQualifiedShortHandSyntaxInPredicate(SessionFactoryScope scope, MessageKeyWatcher loggingWatcher) { + scope.inTransaction( + (session) -> { + final String qry = "select id from Person where originalHairColor in (HairColor.BLONDE, HairColor.BROWN)"; + session.createQuery( qry ).getResultList(); + + assertTrue( loggingWatcher.wasTriggered() ); + } + ); + } + + + @Test + @JiraKey("HHH-16739") + public void testCompareEnumParameterWithDifferentTypedExpressions(SessionFactoryScope scope) { + scope.inSession( + s -> { + s.createQuery( "select p.id from Person p where p.gender = :gender and :gender = 'MALE'", Long.class ) + .setParameter( "gender", Gender.MALE ) + .getResultList(); + s.createQuery( "select p.id from Person p where p.gender = :gender and :gender = org.hibernate.orm.test.mapping.converted.enums.Gender.MALE", Long.class ) + .setParameter( "gender", Gender.MALE ) + .getResultList(); + + s.createQuery( "select p.id from Person p where :gender = org.hibernate.orm.test.mapping.converted.enums.Gender.MALE and p.gender = :gender", Long.class ) + .setParameter( "gender", Gender.MALE ) + .getResultList(); + + s.createQuery( "select p.id from Person p where :gender = 'MALE' and p.gender = :gender", Long.class ) + .setParameter( "gender", Gender.MALE ) + .getResultList(); + + s.createQuery( "select p.id from Person p where :gender = 'MALE' or :gender = 'FEMALE' and p.gender = :gender", Long.class ) + .setParameter( "gender", Gender.MALE ) + .getResultList(); + } + ); + } + + @Entity(name = "Person") + public static class Person { + + @Id + @GeneratedValue + private Long id; + + @Enumerated(EnumType.STRING) + private Gender gender; + + @Enumerated(EnumType.STRING) + private HairColor hairColor; + + @Enumerated(EnumType.STRING) + private HairColor originalHairColor; + + public static Person person(Gender gender, HairColor hairColor) { + Person person = new Person(); + person.setGender( gender ); + person.setHairColor( hairColor ); + return person; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Gender getGender() { + return gender; + } + + public void setGender(Gender gender) { + this.gender = gender; + } + + public HairColor getHairColor() { + return hairColor; + } + + public void setHairColor(HairColor hairColor) { + this.hairColor = hairColor; + } + + public HairColor getOriginalHairColor() { + return originalHairColor; + } + + public void setOriginalHairColor(HairColor originalHairColor) { + this.originalHairColor = originalHairColor; + } + } +}