cleanups to SqmUtil

This commit is contained in:
Gavin King 2024-11-12 23:12:14 +01:00
parent 8a0dab9071
commit 83f7af31f3
2 changed files with 168 additions and 214 deletions

View File

@ -30,7 +30,6 @@ import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmXmlTableFunction; import org.hibernate.query.sqm.tree.expression.SqmXmlTableFunction;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.Template; import org.hibernate.sql.Template;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.internal.ColumnQualifierCollectorSqlAstWalker; import org.hibernate.sql.ast.internal.ColumnQualifierCollectorSqlAstWalker;
import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.FromClauseAccess;

View File

@ -7,7 +7,6 @@ package org.hibernate.query.sqm.internal;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Iterator; import java.util.Iterator;
@ -18,9 +17,9 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.hibernate.AssertionFailure;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.Stack;
import org.hibernate.jpa.spi.JpaCompliance; import org.hibernate.jpa.spi.JpaCompliance;
import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.BasicValuedMapping;
@ -36,13 +35,12 @@ import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.IdentifiableDomainType; import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource; import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.IllegalSelectQueryException; import org.hibernate.query.IllegalSelectQueryException;
@ -99,15 +97,18 @@ import org.hibernate.type.descriptor.java.spi.PrimitiveJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.internal.BasicTypeImpl;
import org.hibernate.type.internal.ConvertedBasicTypeImpl; import org.hibernate.type.internal.ConvertedBasicTypeImpl;
import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.Tuple; import jakarta.persistence.Tuple;
import jakarta.persistence.metamodel.Type; import jakarta.persistence.metamodel.Type;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
import static org.hibernate.internal.util.NullnessUtil.castNonNull; import static org.hibernate.internal.util.NullnessUtil.castNonNull;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
import static org.hibernate.internal.util.collections.CollectionHelper.determineProperSizing;
import static org.hibernate.query.sqm.tree.jpa.ParameterCollector.collectParameters; import static org.hibernate.query.sqm.tree.jpa.ParameterCollector.collectParameters;
/** /**
@ -171,17 +172,13 @@ public class SqmUtil {
SqmToSqlAstConverter sqlAstCreationState) { SqmToSqlAstConverter sqlAstCreationState) {
// We only need to do this for queries // We only need to do this for queries
if ( sqlAstCreationState.getCurrentClauseStack().getCurrent() != Clause.FROM if ( sqlAstCreationState.getCurrentClauseStack().getCurrent() != Clause.FROM
&& modelPartContainer.getPartMappingType() != modelPartContainer && sqmPath.getLhs() instanceof SqmFrom<?, ?> ) { && modelPartContainer.getPartMappingType() != modelPartContainer
final ModelPart modelPart; && sqmPath.getLhs() instanceof SqmFrom<?, ?> ) {
if ( modelPartContainer instanceof PluralAttributeMapping pluralAttributeMapping ) { final ModelPart modelPart =
modelPart = getCollectionPart( modelPartContainer instanceof PluralAttributeMapping pluralAttributeMapping
pluralAttributeMapping, ? getCollectionPart( pluralAttributeMapping,
castNonNull( sqmPath.getNavigablePath().getParent() ) castNonNull( sqmPath.getNavigablePath().getParent() ) )
); : modelPartContainer;
}
else {
modelPart = modelPartContainer;
}
if ( modelPart instanceof EntityAssociationMapping association ) { if ( modelPart instanceof EntityAssociationMapping association ) {
if ( shouldRenderTargetSide( sqmPath, association, sqlAstCreationState ) ) { if ( shouldRenderTargetSide( sqmPath, association, sqlAstCreationState ) ) {
return association.getAssociatedEntityMappingType(); return association.getAssociatedEntityMappingType();
@ -198,13 +195,15 @@ public class SqmUtil {
if ( !association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() ) ) { if ( !association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() ) ) {
return false; return false;
} }
// If the path is one of the association's target key properties, else {
// we need to render the target side if in group/order by // If the path is one of the association's target key properties,
final Clause clause = sqlAstCreationState.getCurrentClauseStack().getCurrent(); // we need to render the target side if in group/order by
return clause == Clause.GROUP || clause == Clause.ORDER final Clause clause = sqlAstCreationState.getCurrentClauseStack().getCurrent();
return clause == Clause.GROUP || clause == Clause.ORDER
|| !isFkOptimizationAllowed( sqmPath.getLhs(), association ) || !isFkOptimizationAllowed( sqmPath.getLhs(), association )
|| clauseContainsPath( Clause.GROUP, sqmPath, sqlAstCreationState ) || clauseContainsPath( Clause.GROUP, sqmPath, sqlAstCreationState )
|| clauseContainsPath( Clause.ORDER, sqmPath, sqlAstCreationState ); || clauseContainsPath( Clause.ORDER, sqmPath, sqlAstCreationState );
}
} }
private static boolean clauseContainsPath( private static boolean clauseContainsPath(
@ -215,10 +214,8 @@ public class SqmUtil {
final NavigablePath navigablePath = sqmPath.getNavigablePath(); final NavigablePath navigablePath = sqmPath.getNavigablePath();
final Boolean found = queryPartStack.findCurrentFirst( queryPart -> { final Boolean found = queryPartStack.findCurrentFirst( queryPart -> {
final SqmQuerySpec<?> spec = queryPart.getFirstQuerySpec(); final SqmQuerySpec<?> spec = queryPart.getFirstQuerySpec();
if ( clauseToCheck == Clause.GROUP && spec.groupByClauseContains( navigablePath, sqlAstCreationState ) ) { if ( clauseToCheck == Clause.GROUP && spec.groupByClauseContains( navigablePath, sqlAstCreationState )
return true; || clauseToCheck == Clause.ORDER && spec.orderByClauseContains( navigablePath, sqlAstCreationState ) ) {
}
else if ( clauseToCheck == Clause.ORDER && spec.orderByClauseContains( navigablePath, sqlAstCreationState ) ) {
return true; return true;
} }
else { else {
@ -290,9 +287,9 @@ public class SqmUtil {
private static boolean isFiltered(EntityAssociationMapping associationMapping) { private static boolean isFiltered(EntityAssociationMapping associationMapping) {
final EntityMappingType entityMappingType = associationMapping.getAssociatedEntityMappingType(); final EntityMappingType entityMappingType = associationMapping.getAssociatedEntityMappingType();
return !associationMapping.isFkOptimizationAllowed() return !associationMapping.isFkOptimizationAllowed()
// When the identifier mappings are different we have a joined subclass entity // When the identifier mappings are different we have a joined subclass entity
// which will filter rows based on a discriminator predicate // which will filter rows based on a discriminator predicate
|| entityMappingType.getIdentifierMappingForJoin() != entityMappingType.getIdentifierMapping(); || entityMappingType.getIdentifierMappingForJoin() != entityMappingType.getIdentifierMapping();
} }
private static @Nullable EntityAssociationMapping resolveAssociationMapping(SqmJoin<?, ?> sqmJoin) { private static @Nullable EntityAssociationMapping resolveAssociationMapping(SqmJoin<?, ?> sqmJoin) {
@ -305,6 +302,7 @@ public class SqmUtil {
} }
private static @Nullable EntityAssociationMapping resolveAssociationMapping(SqmSingularJoin<?, ?> sqmJoin) { private static @Nullable EntityAssociationMapping resolveAssociationMapping(SqmSingularJoin<?, ?> sqmJoin) {
final MappingMetamodelImplementor metamodel = sqmJoin.nodeBuilder().getMappingMetamodel();
SingularPersistentAttribute<?, ?> attribute = sqmJoin.getAttribute(); SingularPersistentAttribute<?, ?> attribute = sqmJoin.getAttribute();
ManagedDomainType<?> declaringType = attribute.getDeclaringType(); ManagedDomainType<?> declaringType = attribute.getDeclaringType();
if ( declaringType.getPersistenceType() != Type.PersistenceType.ENTITY ) { if ( declaringType.getPersistenceType() != Type.PersistenceType.ENTITY ) {
@ -314,66 +312,62 @@ public class SqmUtil {
pathBuilder.insert(0, '.'); pathBuilder.insert(0, '.');
} }
pathBuilder.insert( 0, attribute.getName() ); pathBuilder.insert( 0, attribute.getName() );
final SqmFrom<?, ?> lhs = sqmJoin.getLhs(); if ( sqmJoin.getLhs() instanceof SqmSingularJoin<?, ?> sqmSingularJoin ) {
if ( !(lhs instanceof SqmSingularJoin<?, ?> ) ) { sqmJoin = sqmSingularJoin;
attribute = sqmJoin.getAttribute();
declaringType = attribute.getDeclaringType();
}
else {
return null; return null;
} }
sqmJoin = (SqmSingularJoin<?, ?>) lhs; } while ( declaringType.getPersistenceType() != Type.PersistenceType.ENTITY );
attribute = sqmJoin.getAttribute();
declaringType = attribute.getDeclaringType();
} while (declaringType.getPersistenceType() != Type.PersistenceType.ENTITY );
pathBuilder.insert(0, '.'); pathBuilder.insert(0, '.');
pathBuilder.insert( 0, attribute.getName() ); pathBuilder.insert( 0, attribute.getName() );
final EntityPersister entityDescriptor = return (EntityAssociationMapping)
sqmJoin.nodeBuilder().getMappingMetamodel() metamodel.getEntityDescriptor( ( (EntityDomainType<?>) declaringType ).getHibernateEntityName() )
.getEntityDescriptor( ( (EntityDomainType<?>) declaringType ).getHibernateEntityName() ); .findByPath( pathBuilder.toString() );
return (EntityAssociationMapping) entityDescriptor.findByPath( pathBuilder.toString() );
} }
else { else {
final EntityPersister entityDescriptor = return (EntityAssociationMapping)
sqmJoin.nodeBuilder().getMappingMetamodel() metamodel.getEntityDescriptor( ( (EntityDomainType<?>) declaringType ).getHibernateEntityName() )
.getEntityDescriptor( ( (EntityDomainType<?>) declaringType ).getHibernateEntityName() ); .findAttributeMapping( attribute.getName() );
return (EntityAssociationMapping) entityDescriptor.findAttributeMapping( attribute.getName() );
} }
} }
public static List<NavigablePath> getWhereClauseNavigablePaths(SqmQuerySpec<?> querySpec) { public static List<NavigablePath> getWhereClauseNavigablePaths(SqmQuerySpec<?> querySpec) {
final SqmWhereClause where = querySpec.getWhereClause(); final SqmWhereClause where = querySpec.getWhereClause();
if ( where == null || where.getPredicate() == null ) { return where == null || where.getPredicate() == null
return Collections.emptyList(); ? emptyList()
} : collectNavigablePaths( List.of( where.getPredicate() ) );
return collectNavigablePaths( List.of( where.getPredicate() ) );
} }
public static List<NavigablePath> getGroupByNavigablePaths(SqmQuerySpec<?> querySpec) { public static List<NavigablePath> getGroupByNavigablePaths(SqmQuerySpec<?> querySpec) {
final List<SqmExpression<?>> expressions = querySpec.getGroupByClauseExpressions(); final List<SqmExpression<?>> expressions = querySpec.getGroupByClauseExpressions();
if ( expressions.isEmpty() ) { return expressions.isEmpty() ? emptyList() : collectNavigablePaths( expressions );
return Collections.emptyList();
}
return collectNavigablePaths( expressions );
} }
public static List<NavigablePath> getOrderByNavigablePaths(SqmQuerySpec<?> querySpec) { public static List<NavigablePath> getOrderByNavigablePaths(SqmQuerySpec<?> querySpec) {
final SqmOrderByClause order = querySpec.getOrderByClause(); final SqmOrderByClause order = querySpec.getOrderByClause();
if ( order == null || order.getSortSpecifications().isEmpty() ) { if ( order == null || order.getSortSpecifications().isEmpty() ) {
return Collections.emptyList(); return emptyList();
}
else {
final List<SqmExpression<?>> expressions =
order.getSortSpecifications().stream()
.map( SqmSortSpecification::getSortExpression )
.collect( toList() );
return collectNavigablePaths( expressions );
} }
final List<SqmExpression<?>> expressions = order.getSortSpecifications()
.stream()
.map( SqmSortSpecification::getSortExpression )
.collect( toList() );
return collectNavigablePaths( expressions );
} }
private static List<NavigablePath> collectNavigablePaths(final List<SqmExpression<?>> expressions) { private static List<NavigablePath> collectNavigablePaths(final List<SqmExpression<?>> expressions) {
final List<NavigablePath> navigablePaths = arrayList( expressions.size() ); final List<NavigablePath> navigablePaths = arrayList( expressions.size() );
final SqmPathVisitor pathVisitor = new SqmPathVisitor( path -> navigablePaths.add( path.getNavigablePath() ) ); final SqmPathVisitor pathVisitor = new SqmPathVisitor( path -> navigablePaths.add( path.getNavigablePath() ) );
for ( final SqmExpression<?> expression : expressions ) { for ( final SqmExpression<?> expression : expressions ) {
if ( expression instanceof SqmAliasedNodeRef ) { if ( expression instanceof SqmAliasedNodeRef sqmAliasedNodeRef ) {
final NavigablePath navigablePath = ( (SqmAliasedNodeRef) expression ).getNavigablePath(); final NavigablePath navigablePath = sqmAliasedNodeRef.getNavigablePath();
if ( navigablePath != null ) { if ( navigablePath != null ) {
navigablePaths.add( navigablePath ); navigablePaths.add( navigablePath );
} }
@ -415,50 +409,39 @@ public class SqmUtil {
DomainParameterXref domainParameterXref, DomainParameterXref domainParameterXref,
JdbcParameterBySqmParameterAccess jdbcParameterBySqmParameterAccess) { JdbcParameterBySqmParameterAccess jdbcParameterBySqmParameterAccess) {
if ( domainParameterXref == null || !domainParameterXref.hasParameters() ) { if ( domainParameterXref == null || !domainParameterXref.hasParameters() ) {
return Collections.emptyMap(); return emptyMap();
} }
else {
final int queryParameterCount = domainParameterXref.getQueryParameterCount(); final Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> result =
final Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> result = new IdentityHashMap<>( queryParameterCount ); new IdentityHashMap<>( domainParameterXref.getQueryParameterCount() );
domainParameterXref.getQueryParameters().forEach( (queryParam, sqmParams) -> {
for ( Map.Entry<QueryParameterImplementor<?>, List<SqmParameter<?>>> entry : domainParameterXref.getQueryParameters().entrySet() ) { final Map<SqmParameter<?>, List<JdbcParametersList>> sqmParamMap =
final QueryParameterImplementor<?> queryParam = entry.getKey(); result.computeIfAbsent( queryParam, qp -> new IdentityHashMap<>( sqmParams.size() ) );
final List<SqmParameter<?>> sqmParams = entry.getValue(); for ( SqmParameter<?> sqmParam : sqmParams ) {
final Map<SqmParameter<?>, List<List<JdbcParameter>>> jdbcParamsBySqmParam =
final Map<SqmParameter<?>, List<JdbcParametersList>> sqmParamMap = result.computeIfAbsent( jdbcParameterBySqmParameterAccess.getJdbcParamsBySqmParam();
queryParam, sqmParamMap.put( sqmParam, convert( jdbcParamsBySqmParam.get( sqmParam ) ) );
qp -> new IdentityHashMap<>( sqmParams.size() ) for ( SqmParameter<?> expansion : domainParameterXref.getExpansions( sqmParam ) ) {
); sqmParamMap.put( expansion, convert( jdbcParamsBySqmParam.get( expansion ) ) );
for ( SqmParameter<?> sqmParam : sqmParams ) {
List<List<JdbcParameter>> lists = jdbcParameterBySqmParameterAccess.getJdbcParamsBySqmParam().get(
sqmParam );
sqmParamMap.put( sqmParam, convert( lists ) );
final List<SqmParameter<?>> expansions = domainParameterXref.getExpansions( sqmParam );
if ( ! expansions.isEmpty() ) {
for ( SqmParameter<?> expansion : expansions ) {
List<List<JdbcParameter>> innerList = jdbcParameterBySqmParameterAccess.getJdbcParamsBySqmParam()
.get( expansion );
sqmParamMap.put( expansion, convert( innerList) );
result.put( queryParam, sqmParamMap ); result.put( queryParam, sqmParamMap );
} }
} }
} } );
return result;
} }
return result;
} }
private static List<JdbcParametersList> convert(final List<List<JdbcParameter>> lists) { private static List<JdbcParametersList> convert(final List<List<JdbcParameter>> lists) {
if ( lists == null ) { if ( lists == null ) {
return null; return null;
} }
List<JdbcParametersList> output = new ArrayList<>( lists.size() ); else {
for ( List<JdbcParameter> element : lists ) { final List<JdbcParametersList> output = new ArrayList<>( lists.size() );
output.add( JdbcParametersList.fromList( element ) ); for ( List<JdbcParameter> element : lists ) {
output.add( JdbcParametersList.fromList( element ) );
}
return output;
} }
return output;
} }
// public static JdbcParameterBindings buildJdbcParameterBindings( // public static JdbcParameterBindings buildJdbcParameterBindings(
@ -507,17 +490,10 @@ public class SqmUtil {
Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> jdbcParamXref, Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> jdbcParamXref,
SqmParameterMappingModelResolutionAccess mappingModelResolutionAccess, SqmParameterMappingModelResolutionAccess mappingModelResolutionAccess,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( final JdbcParameterBindings jdbcParameterBindings =
domainParameterXref.getSqmParameterCount() new JdbcParameterBindingsImpl( domainParameterXref.getSqmParameterCount() );
); domainParameterXref.getQueryParameters().forEach( (queryParam, sqmParameters) -> {
for ( Map.Entry<QueryParameterImplementor<?>, List<SqmParameter<?>>> entry :
domainParameterXref.getQueryParameters().entrySet() ) {
final QueryParameterImplementor<?> queryParam = entry.getKey();
final List<SqmParameter<?>> sqmParameters = entry.getValue();
final QueryParameterBinding<?> domainParamBinding = domainParamBindings.getBinding( queryParam ); final QueryParameterBinding<?> domainParamBinding = domainParamBindings.getBinding( queryParam );
final Map<SqmParameter<?>, List<JdbcParametersList>> jdbcParamMap = jdbcParamXref.get( queryParam ); final Map<SqmParameter<?>, List<JdbcParametersList>> jdbcParamMap = jdbcParamXref.get( queryParam );
for ( SqmParameter<?> sqmParameter : sqmParameters ) { for ( SqmParameter<?> sqmParameter : sqmParameters ) {
final MappingModelExpressible resolvedMappingModelType = final MappingModelExpressible resolvedMappingModelType =
@ -552,7 +528,6 @@ public class SqmUtil {
else if ( domainParamBinding.isMultiValued() ) { else if ( domainParamBinding.isMultiValued() ) {
final Collection<?> bindValues = domainParamBinding.getBindValues(); final Collection<?> bindValues = domainParamBinding.getBindValues();
final Iterator<?> valueItr = bindValues.iterator(); final Iterator<?> valueItr = bindValues.iterator();
// the original SqmParameter is the one we are processing.. create a binding for it.. // the original SqmParameter is the one we are processing.. create a binding for it..
final Object firstValue = valueItr.next(); final Object firstValue = valueItr.next();
for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) { for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) {
@ -568,7 +543,7 @@ public class SqmUtil {
); );
} }
// an then one for each of the expansions // and then one for each of the expansions
final List<SqmParameter<?>> expansions = domainParameterXref.getExpansions( sqmParameter ); final List<SqmParameter<?>> expansions = domainParameterXref.getExpansions( sqmParameter );
final int expansionCount = bindValues.size() - 1; final int expansionCount = bindValues.size() - 1;
final int parameterUseCount = jdbcParamsBinds.size(); final int parameterUseCount = jdbcParamsBinds.size();
@ -580,7 +555,7 @@ public class SqmUtil {
final SqmParameter<?> expansionSqmParam = expansions.get( expansionPosition + j * expansionCount ); final SqmParameter<?> expansionSqmParam = expansions.get( expansionPosition + j * expansionCount );
final List<JdbcParametersList> jdbcParamBinds = jdbcParamMap.get( expansionSqmParam ); final List<JdbcParametersList> jdbcParamBinds = jdbcParamMap.get( expansionSqmParam );
for ( int i = 0; i < jdbcParamBinds.size(); i++ ) { for ( int i = 0; i < jdbcParamBinds.size(); i++ ) {
JdbcParametersList expansionJdbcParams = jdbcParamBinds.get( i ); final JdbcParametersList expansionJdbcParams = jdbcParamBinds.get( i );
createValueBindings( createValueBindings(
jdbcParameterBindings, jdbcParameterBindings,
queryParam, queryParam,
@ -597,9 +572,10 @@ public class SqmUtil {
} }
else { else {
final JdbcMapping jdbcMapping; final JdbcMapping jdbcMapping;
if ( domainParamBinding.getType() instanceof JdbcMapping ) { if ( domainParamBinding.getType() instanceof JdbcMapping mapping ) {
jdbcMapping = (JdbcMapping) domainParamBinding.getType(); jdbcMapping = mapping;
} }
// TODO: why do the test and the cast disagree here? getBindType() vs getType()
else if ( domainParamBinding.getBindType() instanceof BasicValuedMapping ) { else if ( domainParamBinding.getBindType() instanceof BasicValuedMapping ) {
jdbcMapping = ( (BasicValuedMapping) domainParamBinding.getType() ).getJdbcMapping(); jdbcMapping = ( (BasicValuedMapping) domainParamBinding.getType() ).getJdbcMapping();
} }
@ -607,17 +583,17 @@ public class SqmUtil {
jdbcMapping = null; jdbcMapping = null;
} }
final BasicValueConverter valueConverter = jdbcMapping == null ? null : jdbcMapping.getValueConverter(); final BasicValueConverter valueConverter =
jdbcMapping == null ? null : jdbcMapping.getValueConverter();
if ( valueConverter != null ) { if ( valueConverter != null ) {
final Object convertedValue = valueConverter.toRelationalValue( domainParamBinding.getBindValue() ); final Object convertedValue =
valueConverter.toRelationalValue( domainParamBinding.getBindValue() );
for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) { for ( int i = 0; i < jdbcParamsBinds.size(); i++ ) {
final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i ); final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i );
assert jdbcParams.size() == 1; assert jdbcParams.size() == 1;
final JdbcParameter jdbcParameter = jdbcParams.get( 0 ); final JdbcParameter jdbcParameter = jdbcParams.get( 0 );
jdbcParameterBindings.addBinding( jdbcParameterBindings.addBinding( jdbcParameter,
jdbcParameter, new JdbcParameterBindingImpl( jdbcMapping, convertedValue ) );
new JdbcParameterBindingImpl( jdbcMapping, convertedValue )
);
} }
} }
else { else {
@ -627,10 +603,8 @@ public class SqmUtil {
final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i ); final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i );
for ( int j = 0; j < jdbcParams.size(); j++ ) { for ( int j = 0; j < jdbcParams.size(); j++ ) {
final JdbcParameter jdbcParameter = jdbcParams.get( j ); final JdbcParameter jdbcParameter = jdbcParams.get( j );
jdbcParameterBindings.addBinding( jdbcParameterBindings.addBinding( jdbcParameter,
jdbcParameter, new JdbcParameterBindingImpl( jdbcMapping, bindValue ) );
new JdbcParameterBindingImpl( jdbcMapping, bindValue )
);
} }
} }
} }
@ -651,7 +625,7 @@ public class SqmUtil {
} }
} }
} }
} } );
return jdbcParameterBindings; return jdbcParameterBindings;
} }
@ -667,22 +641,24 @@ public class SqmUtil {
if ( parameterType == null ) { if ( parameterType == null ) {
throw new SqlTreeCreationException( "Unable to interpret mapping-model type for Query parameter : " + domainParam ); throw new SqlTreeCreationException( "Unable to interpret mapping-model type for Query parameter : " + domainParam );
} }
else if ( parameterType instanceof PluralAttributeMapping ) { else if ( parameterType instanceof PluralAttributeMapping pluralAttributeMapping ) {
// Default to the collection element // Default to the collection element
parameterType = ( (PluralAttributeMapping) parameterType ).getElementDescriptor(); parameterType = pluralAttributeMapping.getElementDescriptor();
} }
if ( parameterType instanceof EntityIdentifierMapping identifierMapping ) { if ( parameterType instanceof EntityIdentifierMapping identifierMapping ) {
final EntityMappingType entityMapping = identifierMapping.findContainingEntityMapping(); final EntityMappingType entityMapping = identifierMapping.findContainingEntityMapping();
if ( entityMapping.getRepresentationStrategy().getInstantiator().isInstance( bindValue, session.getFactory() ) ) { if ( entityMapping.getRepresentationStrategy().getInstantiator()
.isInstance( bindValue, session.getFactory() ) ) {
bindValue = identifierMapping.getIdentifierIfNotUnsaved( bindValue, session ); bindValue = identifierMapping.getIdentifierIfNotUnsaved( bindValue, session );
} }
} }
else if ( parameterType instanceof EntityMappingType ) { else if ( parameterType instanceof EntityMappingType entityMappingType ) {
final EntityIdentifierMapping identifierMapping = ( (EntityMappingType) parameterType ).getIdentifierMapping(); final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
final EntityMappingType entityMapping = identifierMapping.findContainingEntityMapping(); final EntityMappingType entityMapping = identifierMapping.findContainingEntityMapping();
parameterType = identifierMapping; parameterType = identifierMapping;
if ( entityMapping.getRepresentationStrategy().getInstantiator().isInstance( bindValue, session.getFactory() ) ) { if ( entityMapping.getRepresentationStrategy().getInstantiator()
.isInstance( bindValue, session.getFactory() ) ) {
bindValue = identifierMapping.getIdentifierIfNotUnsaved( bindValue, session ); bindValue = identifierMapping.getIdentifierIfNotUnsaved( bindValue, session );
} }
} }
@ -694,11 +670,8 @@ public class SqmUtil {
parameterType = association.getAssociatedEntityMappingType().getIdentifierMapping(); parameterType = association.getAssociatedEntityMappingType().getIdentifierMapping();
} }
else { else {
bindValue = association.getForeignKeyDescriptor().getAssociationKeyFromSide( bindValue = association.getForeignKeyDescriptor()
bindValue, .getAssociationKeyFromSide( bindValue, association.getSideNature().inverse(), session );
association.getSideNature().inverse(),
session
);
parameterType = association.getForeignKeyDescriptor(); parameterType = association.getForeignKeyDescriptor();
} }
} }
@ -706,12 +679,8 @@ public class SqmUtil {
parameterType = domainParamBinding.getType(); parameterType = domainParamBinding.getType();
} }
int offset = jdbcParameterBindings.registerParametersForEachJdbcValue( final int offset =
bindValue, jdbcParameterBindings.registerParametersForEachJdbcValue( bindValue, parameterType, jdbcParams, session );
parameterType,
jdbcParams,
session
);
assert offset == jdbcParams.size(); assert offset == jdbcParams.size();
} }
@ -748,10 +717,9 @@ public class SqmUtil {
} }
} }
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
// assume we have (or can create) a mapping for the parameter's Java type // assume we have (or can create) a mapping for the parameter's Java type
return typeConfiguration.standardBasicTypeForJavaType( parameter.getParameterType() ); return sessionFactory.getTypeConfiguration()
.standardBasicTypeForJavaType( parameter.getParameterType() );
} }
/** /**
@ -759,26 +727,26 @@ public class SqmUtil {
* Returns the passes object after casting it to Bindable, * Returns the passes object after casting it to Bindable,
* if the type is compatible. * if the type is compatible.
* If it's not, null will be returned. * If it's not, null will be returned.
* @param o any object instance * @param object any object instance
* @return a reference to the same object o, but of type Bindable if possible, or null. * @return a reference to the same object o, but of type Bindable if possible, or null.
*/ */
private static Bindable asBindable(final Object o) { private static Bindable asBindable(final Object object) {
if ( o == null ) { if ( object == null ) {
return null; return null;
} }
//There's a high chance that we're dealing with a BasicTypeImpl, or a subclass of it. //There's a high chance that we're dealing with a BasicTypeImpl, or a subclass of it.
else if ( o instanceof BasicTypeImpl<?> basicType ) { else if ( object instanceof BasicTypeImpl<?> basicType ) {
return basicType; return basicType;
} }
//Alternatively, chances are good that we're dealing with an ConvertedBasicTypeImpl. //Alternatively, chances are good that we're dealing with an ConvertedBasicTypeImpl.
else if ( o instanceof ConvertedBasicTypeImpl<?> convertedBasicType ) { else if ( object instanceof ConvertedBasicTypeImpl<?> convertedBasicType ) {
return convertedBasicType; return convertedBasicType;
} }
//Eventually fallback to the standard check for completeness:
else if ( object instanceof Bindable bindable ) {
return bindable;
}
else { else {
//Eventually fallback to the standard check for completeness:
if ( o instanceof Bindable ) {
return (Bindable) o;
}
return null; return null;
} }
} }
@ -798,7 +766,7 @@ public class SqmUtil {
@Override @Override
public Map<JpaCriteriaParameter<?>, SqmJpaCriteriaParameterWrapper<?>> getJpaCriteriaParamResolutions() { public Map<JpaCriteriaParameter<?>, SqmJpaCriteriaParameterWrapper<?>> getJpaCriteriaParamResolutions() {
return Collections.emptyMap(); return emptyMap();
} }
}; };
} }
@ -823,7 +791,7 @@ public class SqmUtil {
return new SqmSortSpecification( return new SqmSortSpecification(
new SqmAliasedNodeRef( element, builder.getIntegerType(), builder ), new SqmAliasedNodeRef( element, builder.getIntegerType(), builder ),
order.getDirection(), order.getDirection(),
order.getNullPrecedence().getJpaValue(), order.getNullPrecedence(),
order.isCaseInsensitive() order.isCaseInsensitive()
); );
} }
@ -856,10 +824,12 @@ public class SqmUtil {
return true; return true;
} }
else if ( selection != null && selection.getSelectableNode() instanceof SqmParameter<?> sqmParameter ) { else if ( selection != null && selection.getSelectableNode() instanceof SqmParameter<?> sqmParameter ) {
final Class<?> anticipatedClass = sqmParameter.getAnticipatedType() != null ? final Class<?> anticipatedClass =
sqmParameter.getAnticipatedType().getBindableJavaType() : sqmParameter.getAnticipatedType() != null
null; ? sqmParameter.getAnticipatedType().getBindableJavaType()
return anticipatedClass != null && expectedResultType.isAssignableFrom( anticipatedClass ); : null;
return anticipatedClass != null
&& expectedResultType.isAssignableFrom( anticipatedClass );
} }
else if ( selection == null else if ( selection == null
|| !isHqlTuple( selection ) && selection.getSelectableNode().isCompoundSelection() ) { || !isHqlTuple( selection ) && selection.getSelectableNode().isCompoundSelection() ) {
@ -897,14 +867,8 @@ public class SqmUtil {
jpaCriteriaParamResolutions = new IdentityHashMap<>(); jpaCriteriaParamResolutions = new IdentityHashMap<>();
} }
final JpaCriteriaParameter<?> criteriaParameter = wrapper.getJpaCriteriaParameter(); jpaCriteriaParamResolutions.computeIfAbsent( wrapper.getJpaCriteriaParameter(), r -> new ArrayList<>() )
.add( wrapper );
final List<SqmJpaCriteriaParameterWrapper<?>> sqmParametersForCriteriaParameter = jpaCriteriaParamResolutions.computeIfAbsent(
criteriaParameter,
jcp -> new ArrayList<>()
);
sqmParametersForCriteriaParameter.add( wrapper );
sqmParameters.add( wrapper ); sqmParameters.add( wrapper );
} }
else if ( parameter instanceof JpaCriteriaParameter ) { else if ( parameter instanceof JpaCriteriaParameter ) {
@ -936,8 +900,8 @@ public class SqmUtil {
private SqmStatement.ParameterResolutions makeResolution() { private SqmStatement.ParameterResolutions makeResolution() {
return new ParameterResolutionsImpl( return new ParameterResolutionsImpl(
sqmParameters == null ? Collections.emptySet() : sqmParameters, sqmParameters == null ? emptySet() : sqmParameters,
jpaCriteriaParamResolutions == null ? Collections.emptyMap() : jpaCriteriaParamResolutions jpaCriteriaParamResolutions == null ? emptyMap() : jpaCriteriaParamResolutions
); );
} }
} }
@ -952,15 +916,18 @@ public class SqmUtil {
this.sqmParameters = sqmParameters; this.sqmParameters = sqmParameters;
if ( jpaCriteriaParamResolutions == null || jpaCriteriaParamResolutions.isEmpty() ) { if ( jpaCriteriaParamResolutions == null || jpaCriteriaParamResolutions.isEmpty() ) {
this.jpaCriteriaParamResolutions = Collections.emptyMap(); this.jpaCriteriaParamResolutions = emptyMap();
} }
else { else {
this.jpaCriteriaParamResolutions = new IdentityHashMap<>( CollectionHelper.determineProperSizing( jpaCriteriaParamResolutions ) ); this.jpaCriteriaParamResolutions =
for ( Map.Entry<JpaCriteriaParameter<?>, List<SqmJpaCriteriaParameterWrapper<?>>> entry : jpaCriteriaParamResolutions.entrySet() ) { new IdentityHashMap<>( determineProperSizing( jpaCriteriaParamResolutions ) );
for ( Map.Entry<JpaCriteriaParameter<?>, List<SqmJpaCriteriaParameterWrapper<?>>> entry
: jpaCriteriaParamResolutions.entrySet() ) {
final Iterator<SqmJpaCriteriaParameterWrapper<?>> itr = entry.getValue().iterator(); final Iterator<SqmJpaCriteriaParameterWrapper<?>> itr = entry.getValue().iterator();
if ( !itr.hasNext() ) { if ( !itr.hasNext() ) {
throw new IllegalStateException( throw new IllegalStateException(
"SqmJpaCriteriaParameterWrapper references for JpaCriteriaParameter [" + entry.getKey() + "] already exhausted" ); "SqmJpaCriteriaParameterWrapper references for JpaCriteriaParameter ["
+ entry.getKey() + "] already exhausted" );
} }
this.jpaCriteriaParamResolutions.put( entry.getKey(), itr.next() ); this.jpaCriteriaParamResolutions.put( entry.getKey(), itr.next() );
} }
@ -993,15 +960,17 @@ public class SqmUtil {
* Similar to {@link #validateQueryReturnType(SqmQueryPart, Class)} but does not check if {@link #isResultTypeAlwaysAllowed(Class)}. * Similar to {@link #validateQueryReturnType(SqmQueryPart, Class)} but does not check if {@link #isResultTypeAlwaysAllowed(Class)}.
*/ */
public static void checkQueryReturnType(SqmQueryPart<?> queryPart, Class<?> expectedResultType) { public static void checkQueryReturnType(SqmQueryPart<?> queryPart, Class<?> expectedResultType) {
if ( queryPart instanceof SqmQuerySpec<?> ) { if ( queryPart instanceof SqmQuerySpec<?> querySpec ) {
checkQueryReturnType( (SqmQuerySpec<?>) queryPart, expectedResultType ); checkQueryReturnType( querySpec, expectedResultType );
} }
else { else if ( queryPart instanceof SqmQueryGroup<?> queryGroup ) {
final SqmQueryGroup<?> queryGroup = (SqmQueryGroup<?>) queryPart;
for ( SqmQueryPart<?> sqmQueryPart : queryGroup.getQueryParts() ) { for ( SqmQueryPart<?> sqmQueryPart : queryGroup.getQueryParts() ) {
checkQueryReturnType( sqmQueryPart, expectedResultType ); checkQueryReturnType( sqmQueryPart, expectedResultType );
} }
} }
else {
throw new AssertionFailure( "Unexpected query part" );
}
} }
private static void checkQueryReturnType(SqmQuerySpec<?> querySpec, Class<?> expectedResultClass) { private static void checkQueryReturnType(SqmQuerySpec<?> querySpec, Class<?> expectedResultClass) {
@ -1116,54 +1085,41 @@ public class SqmUtil {
} }
protected static void verifyResultType(Class<?> resultClass, @Nullable SqmExpressible<?> selectionExpressible) { protected static void verifyResultType(Class<?> resultClass, @Nullable SqmExpressible<?> selectionExpressible) {
if ( selectionExpressible == null ) { if ( selectionExpressible != null ) {
// nothing we can validate final JavaType<?> javaType = selectionExpressible.getExpressibleJavaType();
return; if ( javaType != null ) {
} final Class<?> javaTypeClass = javaType.getJavaTypeClass();
if ( javaTypeClass != Object.class ) {
final JavaType<?> selectionExpressibleJavaType = selectionExpressible.getExpressibleJavaType(); if ( !isValid( resultClass, selectionExpressible, javaTypeClass, javaType ) ) {
if ( selectionExpressibleJavaType == null ) { throwQueryTypeMismatchException( resultClass, selectionExpressible );
// nothing we can validate }
return;
}
final Class<?> selectionExpressibleJavaTypeClass = selectionExpressibleJavaType.getJavaTypeClass();
if ( selectionExpressibleJavaTypeClass != Object.class ) {
// performs a series of opt-out checks for validity... each if branch and return indicates a valid case
if ( resultClass.isAssignableFrom( selectionExpressibleJavaTypeClass ) ) {
return;
}
if ( selectionExpressibleJavaType instanceof final PrimitiveJavaType<?> primitiveJavaType ) {
if ( primitiveJavaType.getPrimitiveClass() == resultClass ) {
return;
} }
} }
if ( isMatchingDateType( selectionExpressibleJavaTypeClass, resultClass, selectionExpressible ) ) {
return;
}
if ( isEntityIdType( selectionExpressible, resultClass ) ) {
return;
}
throwQueryTypeMismatchException( resultClass, selectionExpressible );
} }
} }
private static boolean isValid(
Class<?> resultClass,
SqmExpressible<?> selectionExpressible,
Class<?> selectionExpressibleJavaTypeClass,
JavaType<?> selectionExpressibleJavaType) {
return resultClass.isAssignableFrom( selectionExpressibleJavaTypeClass )
|| selectionExpressibleJavaType instanceof final PrimitiveJavaType<?> primitiveJavaType
&& primitiveJavaType.getPrimitiveClass() == resultClass
|| isMatchingDateType( selectionExpressibleJavaTypeClass, resultClass, selectionExpressible )
|| isEntityIdType( selectionExpressible, resultClass );
}
private static boolean isEntityIdType(SqmExpressible<?> selectionExpressible, Class<?> resultClass) { private static boolean isEntityIdType(SqmExpressible<?> selectionExpressible, Class<?> resultClass) {
if ( selectionExpressible instanceof IdentifiableDomainType<?> identifiableDomainType ) { if ( selectionExpressible instanceof IdentifiableDomainType<?> identifiableDomainType ) {
final SimpleDomainType<?> idType = identifiableDomainType.getIdType(); return resultClass.isAssignableFrom( identifiableDomainType.getIdType().getBindableJavaType() );
return resultClass.isAssignableFrom( idType.getBindableJavaType() );
} }
else if ( selectionExpressible instanceof EntitySqmPathSource<?> entityPath ) { else if ( selectionExpressible instanceof EntitySqmPathSource<?> entityPath ) {
final EntityDomainType<?> entityType = entityPath.getSqmPathType(); return resultClass.isAssignableFrom( entityPath.getSqmPathType().getIdType().getBindableJavaType() );
final SimpleDomainType<?> idType = entityType.getIdType(); }
return resultClass.isAssignableFrom( idType.getBindableJavaType() ); else {
return false;
} }
return false;
} }
// Special case for date because we always report java.util.Date as expression type // Special case for date because we always report java.util.Date as expression type
@ -1173,17 +1129,16 @@ public class SqmUtil {
Class<?> resultClass, Class<?> resultClass,
SqmExpressible<?> sqmExpressible) { SqmExpressible<?> sqmExpressible) {
return javaTypeClass == Date.class return javaTypeClass == Date.class
&& isMatchingDateJdbcType( resultClass, getJdbcType( sqmExpressible ) ); && isMatchingDateJdbcType( resultClass, getJdbcType( sqmExpressible ) );
} }
private static JdbcType getJdbcType(SqmExpressible<?> sqmExpressible) { private static JdbcType getJdbcType(SqmExpressible<?> sqmExpressible) {
if ( sqmExpressible instanceof BasicDomainType<?> ) { if ( sqmExpressible instanceof BasicDomainType<?> basicDomainType ) {
return ( (BasicDomainType<?>) sqmExpressible).getJdbcType(); return basicDomainType.getJdbcType();
} }
else if ( sqmExpressible instanceof SqmPathSource<?> pathSource ) { else if ( sqmExpressible instanceof SqmPathSource<?> pathSource ) {
final DomainType<?> domainType = pathSource.getSqmPathType(); if ( pathSource.getSqmPathType() instanceof BasicDomainType<?> basicDomainType ) {
if ( domainType instanceof BasicDomainType<?> ) { return basicDomainType.getJdbcType();
return ( (BasicDomainType<?>) domainType ).getJdbcType();
} }
} }
return null; return null;