HHH-16739 Fix several failures when comparing enum parameters with constant values

This commit is contained in:
Christian Beikov 2023-06-09 14:51:55 +02:00
parent f24660e1fd
commit e8acf51608
37 changed files with 469 additions and 76 deletions

View File

@ -158,7 +158,8 @@ public class InverseDistributionFunction extends AbstractSqmSelfRenderingFunctio
return (ReturnableType<?>)
getWithinGroup().getSortSpecifications().get( 0 )
.getSortExpression()
.getExpressible();
.getExpressible()
.getSqmType();
}
@Override

View File

@ -26,4 +26,8 @@ import org.hibernate.type.descriptor.java.JavaType;
* @author Steve Ebersole
*/
public interface DomainType<J> extends SqmExpressible<J> {
@Override
default DomainType<J> getSqmType() {
return this;
}
}

View File

@ -22,4 +22,9 @@ public interface EntityDomainType<J> extends IdentifiableDomainType<J>, EntityTy
@Override
Collection<? extends EntityDomainType<? extends J>> getSubTypes();
@Override
default DomainType<J> getSqmType() {
return this;
}
}

View File

@ -16,4 +16,8 @@ import jakarta.persistence.metamodel.MappedSuperclassType;
* @author Steve Ebersole
*/
public interface MappedSuperclassDomainType<J> extends IdentifiableDomainType<J>, MappedSuperclassType<J>, SqmPathSource<J> {
@Override
default DomainType<J> getSqmType() {
return IdentifiableDomainType.super.getSqmType();
}
}

View File

@ -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 class AnyDiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
return (BasicType<D>) super.getSqmPathType();
}
@Override
public DomainType<D> getSqmType() {
return getSqmPathType();
}
@Override
public JavaType<D> getExpressibleJavaType() {
return getSqmPathType().getExpressibleJavaType();

View File

@ -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.TerminalPathException;
import org.hibernate.query.sqm.SqmPathSource;
@ -42,6 +43,11 @@ public class BasicSqmPathSource<J>
return (BasicDomainType<J>) super.getSqmPathType();
}
@Override
public DomainType<J> getSqmType() {
return getSqmPathType();
}
@Override
public SqmPathSource<?> findSubPathSource(String name) {
String path = pathModel.getPathName();

View File

@ -60,4 +60,9 @@ public class DiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
public Class<D> getJavaType() {
return getExpressibleJavaType().getJavaTypeClass();
}
@Override
public DomainType<D> getSqmType() {
return this;
}
}

View File

@ -754,8 +754,7 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
@Override
public MappingModelExpressible<?> resolveMappingExpressible(
SqmExpressible<?> sqmExpressible,
Function<NavigablePath,
TableGroup> tableGroupLocator) {
Function<NavigablePath, TableGroup> tableGroupLocator) {
if ( sqmExpressible instanceof SqmPath ) {
final SqmPath<?> sqmPath = (SqmPath<?>) sqmExpressible;
final NavigablePath navigablePath = sqmPath.getNavigablePath();

View File

@ -214,7 +214,7 @@ public class AnonymousTupleType<T> implements TupleType<T>, DomainType<T>, Retur
else {
return new AnonymousTupleSimpleSqmPathSource<>(
name,
(DomainType<? extends Object>) component.getExpressible(),
component.getExpressible().getSqmType(),
BindableType.SINGULAR_ATTRIBUTE
);
}
@ -246,6 +246,11 @@ public class AnonymousTupleType<T> implements TupleType<T>, DomainType<T>, Retur
return this;
}
@Override
public DomainType<T> getSqmType() {
return this;
}
@Override
public SqmPath<T> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
throw new UnsupportedMappingException(

View File

@ -971,6 +971,10 @@ public class QuerySplitter {
@Override
public SqmBasicValuedSimplePath<?> visitBasicValuedPath(SqmBasicValuedSimplePath<?> path) {
final SqmPath<?> existing = sqmPathCopyMap.get( path.getNavigablePath() );
if ( existing != null ) {
return (SqmBasicValuedSimplePath<?>) existing;
}
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
final SqmPath<?> lhs = findLhs( path );
@ -989,6 +993,10 @@ public class QuerySplitter {
@Override
public SqmEmbeddedValuedSimplePath<?> visitEmbeddableValuedPath(SqmEmbeddedValuedSimplePath<?> path) {
final SqmPath<?> existing = sqmPathCopyMap.get( path.getNavigablePath() );
if ( existing != null ) {
return (SqmEmbeddedValuedSimplePath<?>) existing;
}
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
final SqmPath<?> lhs = findLhs( path );
final SqmEmbeddedValuedSimplePath<?> copy = new SqmEmbeddedValuedSimplePath<>(
@ -1004,6 +1012,10 @@ public class QuerySplitter {
@Override
public SqmEntityValuedSimplePath<?> visitEntityValuedPath(SqmEntityValuedSimplePath<?> path) {
final SqmPath<?> existing = sqmPathCopyMap.get( path.getNavigablePath() );
if ( existing != null ) {
return (SqmEntityValuedSimplePath<?>) existing;
}
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
final SqmPath<?> lhs = findLhs( path );
final SqmEntityValuedSimplePath<?> copy = new SqmEntityValuedSimplePath<>(
@ -1019,6 +1031,10 @@ public class QuerySplitter {
@Override
public SqmPluralValuedSimplePath<?> visitPluralValuedPath(SqmPluralValuedSimplePath<?> path) {
final SqmPath<?> existing = sqmPathCopyMap.get( path.getNavigablePath() );
if ( existing != null ) {
return (SqmPluralValuedSimplePath<?>) existing;
}
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
SqmPath<?> lhs = findLhs( path );

View File

@ -25,6 +25,7 @@ import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@ -124,6 +125,7 @@ import org.hibernate.query.sqm.tree.domain.SqmMapJoin;
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;
@ -313,6 +315,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
private ParameterCollector parameterCollector;
private ParameterStyle parameterStyle;
private Map<Object, AbstractSqmParameter<?>> parameters;
private boolean isExtractingJdbcTemporalType;
// Provides access to the current CTE that is being processed, which is potentially recursive
@ -3839,14 +3842,14 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
HqlParser.NamedParameterContext ctx,
SqmExpressible<T> expressibleType) {
parameterStyle = parameterStyle.withNamed();
final SqmNamedParameter<T> 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
@ -3861,14 +3864,28 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
throw new ParameterLabelException( "Unlabeled ordinal parameter ('?' rather than ?1)" );
}
parameterStyle = parameterStyle.withPositional();
final SqmPositionalParameter<T> 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 extends AbstractSqmParameter<?>> 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;
}

View File

@ -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;
@ -51,4 +52,6 @@ public interface SqmExpressible<J> extends BindableType<J> {
JavaType<J> expressibleJavaType = getExpressibleJavaType();
return expressibleJavaType == null ? "unknown" : expressibleJavaType.getJavaType().getTypeName();
}
DomainType<J> getSqmType();
}

View File

@ -84,6 +84,11 @@ public interface SqmPathSource<J> extends SqmExpressible<J>, Bindable<J>, SqmExp
return (SqmExpressible<J>) getSqmPathType();
}
@Override
default DomainType<J> getSqmType() {
return (DomainType<J>) getSqmPathType();
}
/**
* Indicates if this path source is generically typed
*/

View File

@ -1289,6 +1289,11 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public Class<T> getBindableJavaType() {
return javaType.getJavaTypeClass();
}
@Override
public DomainType<T> getSqmType() {
return null;
}
}
@Override
@ -1829,8 +1834,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public <Y> JpaCoalesce<Y> coalesce(Expression<? extends Y> x, Expression<? extends Y> y) {
@SuppressWarnings("unchecked")
final SqmExpressible<Y> sqmExpressible = (SqmExpressible<Y>) highestPrecedenceType(
( (SqmExpression<? extends Y>) x ).getNodeType(),
( (SqmExpression<? extends Y>) y ).getNodeType()
( (SqmExpression<? extends Y>) x ).getExpressible(),
( (SqmExpression<? extends Y>) y ).getExpressible()
);
return new SqmCoalesce<>(
sqmExpressible,
@ -1860,9 +1865,9 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
private <Y> SqmExpression<Y> createNullifFunctionNode(SqmExpression<Y> first, SqmExpression<Y> second) {
//noinspection unchecked
final ReturnableType<Y> type = (ReturnableType<Y>) highestPrecedenceType(
first.getNodeType(),
second.getNodeType()
);
first.getExpressible(),
second.getExpressible()
).getSqmType();
return getFunctionDescriptor("nullif").generateSqmExpression(
asList( first, second ),

View File

@ -32,8 +32,10 @@ import org.hibernate.query.sqm.SqmExpressible;
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;
@ -211,13 +213,34 @@ public class SqmMappingModelHelper {
}
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();

View File

@ -257,6 +257,7 @@ public class SqmUtil {
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(
@ -265,7 +266,7 @@ public class SqmUtil {
domainParamBinding,
parameterType,
jdbcParams,
valueItr.next(),
firstValue,
tableGroupLocator,
session
);
@ -273,23 +274,30 @@ public class SqmUtil {
// an then one for each of the expansions
final List<SqmParameter<?>> 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<JdbcParametersList> 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<JdbcParametersList> 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 ) {

View File

@ -5594,11 +5594,14 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
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 ) {
@ -5759,19 +5762,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
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() );
}

View File

@ -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.query.sqm.UnknownPathException;
import org.hibernate.spi.NavigablePath;
@ -63,6 +64,11 @@ public class SqmBasicValuedSimplePath<T>
return path;
}
@Override
public SqmExpressible<T> getExpressible() {
return this;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SemanticPathPart
@ -123,6 +129,11 @@ public class SqmBasicValuedSimplePath<T>
return getJavaType();
}
@Override
public DomainType<T> getSqmType() {
return getNodeType().getSqmType();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Visitation

View File

@ -90,6 +90,11 @@ public class SqmCteRoot<T> extends SqmRoot<T> implements JpaRoot<T> {
return null;
}
@Override
public String getEntityName() {
return null;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return getReferencedPathSource();

View File

@ -92,6 +92,11 @@ public class SqmDerivedRoot<T> extends SqmRoot<T> implements JpaDerivedRoot<T> {
return null;
}
@Override
public String getEntityName() {
return null;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return getReferencedPathSource();

View File

@ -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 class SqmEmbeddedValuedSimplePath<T>
return path;
}
@Override
public SqmExpressible<T> getExpressible() {
return this;
}
@Override
public DomainType<T> getSqmType() {
return getReferencedPathSource().getSqmType();
}
@Override
public SqmPath<?> resolvePathPart(
String name,

View File

@ -12,6 +12,7 @@ import java.util.List;
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;
@ -120,6 +121,11 @@ public class SqmMapEntryReference<K,V>
return this;
}
@Override
public DomainType<Map.Entry<K, V>> getSqmType() {
return null;
}
@Override
public Class<Map.Entry<K, V>> getBindableJavaType() {
return getNodeType().getBindableJavaType();

View File

@ -24,6 +24,7 @@ import org.hibernate.type.descriptor.java.JavaType;
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 @@ public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> i
SqmTreeCreationLogger.LOGGER.debugf(
"Applying inferable type to SqmExpression [%s] : %s -> %s",
this,
getNodeType(),
getExpressible(),
newType
);
setExpressibleType( highestPrecedenceType( newType, getNodeType() ) );
setExpressibleType( highestPrecedenceType2( newType, getExpressible() ) );
}
@Override

View File

@ -36,12 +36,7 @@ public abstract class AbstractSqmParameter<T> extends AbstractSqmExpression<T> i
else if ( type instanceof PluralPersistentAttribute<?, ?, ?> ) {
type = ( (PluralPersistentAttribute<?, ?, ?>) type ).getElementType();
}
final SqmExpressible<T> oldType = getNodeType();
final SqmExpressible<?> newType = QueryHelper.highestPrecedenceType( oldType, type );
if ( newType != null && newType != oldType ) {
internalApplyInferableType( newType );
}
internalApplyInferableType( type );
}
@Override

View File

@ -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 NullSqmExpressible implements SqmExpressible<Object> {
public JavaType<Object> getExpressibleJavaType() {
return null;
}
@Override
public DomainType<Object> getSqmType() {
return null;
}
}

View File

@ -96,7 +96,7 @@ public class SqmCaseSearched<R>
return;
}
final SqmExpressible<?> oldType = getNodeType();
final SqmExpressible<?> oldType = getExpressible();
final SqmExpressible<?> newType = QueryHelper.highestPrecedenceType2( oldType, type );
if ( newType != null && newType != oldType ) {

View File

@ -104,9 +104,9 @@ public class SqmCaseSimple<T, R>
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 );
}

View File

@ -10,6 +10,7 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.query.hql.spi.SemanticPathPart;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.NodeBuilder;
@ -26,7 +27,7 @@ import org.hibernate.type.descriptor.java.EnumJavaType;
*
* @author Steve Ebersole
*/
public class SqmEnumLiteral<E extends Enum<E>> extends AbstractSqmExpression<E> implements SqmExpressible<E>, SemanticPathPart {
public class SqmEnumLiteral<E extends Enum<E>> extends SqmLiteral<E> implements SqmExpressible<E>, SemanticPathPart {
private final E enumValue;
private final EnumJavaType<E> referencedEnumTypeDescriptor;
private final String enumValueName;
@ -62,6 +63,16 @@ public class SqmEnumLiteral<E extends Enum<E>> extends AbstractSqmExpression<E>
return expression;
}
@Override
public SqmExpressible<E> getExpressible() {
return this;
}
@Override
public DomainType<E> getSqmType() {
return null;
}
public E getEnumValue() {
return enumValue;
}

View File

@ -14,6 +14,7 @@ import java.util.List;
import java.util.Locale;
import org.hibernate.QueryException;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.query.criteria.JpaSelection;
import org.hibernate.query.hql.spi.SemanticPathPart;
import org.hibernate.query.hql.spi.SqmCreationState;
@ -275,4 +276,9 @@ public class SqmFieldLiteral<T> implements SqmExpression<T>, SqmExpressible<T>,
return null;
}
@Override
public DomainType<T> getSqmType() {
return null;
}
}

View File

@ -33,9 +33,9 @@ public class SqmBetweenPredicate extends AbstractNegatableSqmPredicate {
this.upperBound = upperBound;
final SqmExpressible<?> expressibleType = QueryHelper.highestPrecedenceType(
expression.getNodeType(),
lowerBound.getNodeType(),
upperBound.getNodeType()
expression.getExpressible(),
lowerBound.getExpressible(),
upperBound.getExpressible()
);
expression.applyInferableType( expressibleType );

View File

@ -42,8 +42,8 @@ public class SqmComparisonPredicate extends AbstractNegatableSqmPredicate {
this.operator = operator;
final SqmExpressible<?> expressibleType = QueryHelper.highestPrecedenceType(
leftHandExpression.getNodeType(),
rightHandExpression.getNodeType()
leftHandExpression.getExpressible(),
rightHandExpression.getExpressible()
);
leftHandExpression.applyInferableType( expressibleType );

View File

@ -137,7 +137,7 @@ public class SqmInListPredicate<T> extends AbstractNegatableSqmPredicate impleme
private void implyListElementType(SqmExpression<?> expression) {
nodeBuilder().assertComparable( getTestExpression(), expression );
expression.applyInferableType(
QueryHelper.highestPrecedenceType2( getTestExpression().getNodeType(), expression.getNodeType() )
QueryHelper.highestPrecedenceType2( getTestExpression().getExpressible(), expression.getExpressible() )
);
}

View File

@ -41,8 +41,8 @@ public class SqmInSubQueryPredicate<T> extends AbstractNegatableSqmPredicate imp
this.subQueryExpression = subQueryExpression;
final SqmExpressible<?> expressibleType = QueryHelper.highestPrecedenceType2(
testExpression.getNodeType(),
subQueryExpression.getNodeType()
testExpression.getExpressible(),
subQueryExpression.getExpressible()
);
testExpression.applyInferableType( expressibleType );

View File

@ -52,8 +52,8 @@ public class SqmLikePredicate extends AbstractNegatableSqmPredicate {
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 );

View File

@ -11,6 +11,7 @@ import java.util.List;
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 class SqmDynamicInstantiation<T>
public Class<T> getBindableJavaType() {
return getTargetTypeDescriptor().getJavaTypeClass();
}
@Override
public DomainType<T> getSqmType() {
return null;
}
}

View File

@ -10,6 +10,7 @@ import java.util.ArrayList;
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 class SqmJpaCompoundSelection<T>
public void visitSubSelectableNodes(Consumer<SqmSelectableNode<?>> jpaSelectionConsumer) {
selectableNodes.forEach( jpaSelectionConsumer );
}
@Override
public DomainType<T> getSqmType() {
return null;
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}
}