diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/HANAXmlTableFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/HANAXmlTableFunction.java index fe5810fd2a62..b8ffd8555863 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/HANAXmlTableFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/xml/HANAXmlTableFunction.java @@ -30,7 +30,6 @@ import org.hibernate.query.sqm.tree.expression.SqmXmlTableFunction; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.Template; -import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.internal.ColumnQualifierCollectorSqlAstWalker; import org.hibernate.sql.ast.spi.FromClauseAccess; diff --git a/hibernate-core/src/main/java/org/hibernate/query/Order.java b/hibernate-core/src/main/java/org/hibernate/query/Order.java index da5804fceeb6..be7c6a1b5679 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/Order.java +++ b/hibernate-core/src/main/java/org/hibernate/query/Order.java @@ -4,6 +4,7 @@ */ package org.hibernate.query; +import jakarta.persistence.criteria.Nulls; import jakarta.persistence.metamodel.SingularAttribute; import org.hibernate.Incubating; @@ -42,11 +43,11 @@ public class Order { private final SingularAttribute attribute; private final Class entityClass; private final String attributeName; - private final NullPrecedence nullPrecedence; + private final Nulls nullPrecedence; private final int element; private final boolean ignoreCase; - private Order(SortDirection order, NullPrecedence nullPrecedence, SingularAttribute attribute) { + private Order(SortDirection order, Nulls nullPrecedence, SingularAttribute attribute) { this.order = order; this.attribute = attribute; this.attributeName = attribute.getName(); @@ -56,7 +57,7 @@ private Order(SortDirection order, NullPrecedence nullPrecedence, SingularAttrib this.ignoreCase = false; } - private Order(SortDirection order, NullPrecedence nullPrecedence, SingularAttribute attribute, boolean ignoreCase) { + private Order(SortDirection order, Nulls nullPrecedence, SingularAttribute attribute, boolean ignoreCase) { this.order = order; this.attribute = attribute; this.attributeName = attribute.getName(); @@ -66,7 +67,7 @@ private Order(SortDirection order, NullPrecedence nullPrecedence, SingularAttrib this.ignoreCase = ignoreCase; } - private Order(SortDirection order, NullPrecedence nullPrecedence, Class entityClass, String attributeName) { + private Order(SortDirection order, Nulls nullPrecedence, Class entityClass, String attributeName) { this.order = order; this.entityClass = entityClass; this.attributeName = attributeName; @@ -76,7 +77,7 @@ private Order(SortDirection order, NullPrecedence nullPrecedence, Class entit this.ignoreCase = false; } - private Order(SortDirection order, NullPrecedence nullPrecedence, int element) { + private Order(SortDirection order, Nulls nullPrecedence, int element) { this.order = order; this.entityClass = null; this.attributeName = null; @@ -86,7 +87,7 @@ private Order(SortDirection order, NullPrecedence nullPrecedence, int element) { this.ignoreCase = false; } - private Order(SortDirection order, NullPrecedence nullPrecedence, Class entityClass, String attributeName, boolean ignoreCase) { + private Order(SortDirection order, Nulls nullPrecedence, Class entityClass, String attributeName, boolean ignoreCase) { this.order = order; this.entityClass = entityClass; this.attributeName = attributeName; @@ -96,7 +97,7 @@ private Order(SortDirection order, NullPrecedence nullPrecedence, Class entit this.ignoreCase = ignoreCase; } - private Order(SortDirection order, NullPrecedence nullPrecedence, int element, boolean ignoreCase) { + private Order(SortDirection order, Nulls nullPrecedence, int element, boolean ignoreCase) { this.order = order; this.entityClass = null; this.attributeName = null; @@ -126,7 +127,7 @@ private Order(Order other, boolean ignoreCase) { this.ignoreCase = ignoreCase; } - private Order(Order other, NullPrecedence nullPrecedence) { + private Order(Order other, Nulls nullPrecedence) { this.order = other.order; this.attribute = other.attribute; this.entityClass = other.entityClass; @@ -142,7 +143,7 @@ private Order(Order other, NullPrecedence nullPrecedence) { * type, the ordering is case-sensitive. */ public static Order asc(SingularAttribute attribute) { - return new Order<>(ASCENDING, NullPrecedence.NONE, attribute); + return new Order<>(ASCENDING, Nulls.NONE, attribute); } /** @@ -151,7 +152,7 @@ public static Order asc(SingularAttribute attribute) { * type, the ordering is case-sensitive. */ public static Order desc(SingularAttribute attribute) { - return new Order<>(DESCENDING, NullPrecedence.NONE, attribute); + return new Order<>(DESCENDING, Nulls.NONE, attribute); } /** @@ -160,7 +161,7 @@ public static Order desc(SingularAttribute attribute) { * type, the ordering is case-sensitive. */ public static Order by(SingularAttribute attribute, SortDirection direction) { - return new Order<>(direction, NullPrecedence.NONE, attribute); + return new Order<>(direction, Nulls.NONE, attribute); } /** @@ -168,7 +169,7 @@ public static Order by(SingularAttribute attribute, SortDirection di * in the given direction, with the specified case-sensitivity. */ public static Order by(SingularAttribute attribute, SortDirection direction, boolean ignoreCase) { - return new Order<>(direction, NullPrecedence.NONE, attribute, ignoreCase); + return new Order<>(direction, Nulls.NONE, attribute, ignoreCase); } /** @@ -177,7 +178,7 @@ public static Order by(SingularAttribute attribute, SortDirection di * null values. If the give attribute is of textual type, the * ordering is case-sensitive. */ - public static Order by(SingularAttribute attribute, SortDirection direction, NullPrecedence nullPrecedence) { + public static Order by(SingularAttribute attribute, SortDirection direction, Nulls nullPrecedence) { return new Order<>(direction, nullPrecedence, attribute); } @@ -188,7 +189,7 @@ public static Order by(SingularAttribute attribute, SortDirection di * case-sensitive. */ public static Order asc(Class entityClass, String attributeName) { - return new Order<>( ASCENDING, NullPrecedence.NONE, entityClass, attributeName ); + return new Order<>( ASCENDING, Nulls.NONE, entityClass, attributeName ); } /** @@ -198,7 +199,7 @@ public static Order asc(Class entityClass, String attributeName) { * case-sensitive. */ public static Order desc(Class entityClass, String attributeName) { - return new Order<>( DESCENDING, NullPrecedence.NONE, entityClass, attributeName ); + return new Order<>( DESCENDING, Nulls.NONE, entityClass, attributeName ); } /** @@ -208,7 +209,7 @@ public static Order desc(Class entityClass, String attributeName) { * case-sensitive. */ public static Order by(Class entityClass, String attributeName, SortDirection direction) { - return new Order<>( direction, NullPrecedence.NONE, entityClass, attributeName ); + return new Order<>( direction, Nulls.NONE, entityClass, attributeName ); } /** @@ -217,7 +218,7 @@ public static Order by(Class entityClass, String attributeName, SortDi * the specified case-sensitivity. */ public static Order by(Class entityClass, String attributeName, SortDirection direction, boolean ignoreCase) { - return new Order<>( direction, NullPrecedence.NONE, entityClass, attributeName, ignoreCase ); + return new Order<>( direction, Nulls.NONE, entityClass, attributeName, ignoreCase ); } /** @@ -227,7 +228,7 @@ public static Order by(Class entityClass, String attributeName, SortDi * precedence for null values. If the named attribute is of * textual type, the ordering is case-sensitive. */ - public static Order by(Class entityClass, String attributeName, SortDirection direction, NullPrecedence nullPrecedence) { + public static Order by(Class entityClass, String attributeName, SortDirection direction, Nulls nullPrecedence) { return new Order<>( direction, nullPrecedence, entityClass, attributeName ); } @@ -237,7 +238,7 @@ public static Order by(Class entityClass, String attributeName, SortDi * item is of textual type, the ordering is case-sensitive. */ public static Order asc(int element) { - return new Order<>( ASCENDING, NullPrecedence.NONE, element ); + return new Order<>( ASCENDING, Nulls.NONE, element ); } /** @@ -246,7 +247,7 @@ public static Order asc(int element) { * item is of textual type, the ordering is case-sensitive. */ public static Order desc(int element) { - return new Order<>( DESCENDING, NullPrecedence.NONE, element ); + return new Order<>( DESCENDING, Nulls.NONE, element ); } /** @@ -255,7 +256,7 @@ public static Order desc(int element) { * is of textual type, the ordering is case-sensitive. */ public static Order by(int element, SortDirection direction) { - return new Order<>( direction, NullPrecedence.NONE, element ); + return new Order<>( direction, Nulls.NONE, element ); } /** @@ -264,7 +265,7 @@ public static Order by(int element, SortDirection direction) { * case-sensitivity. */ public static Order by(int element, SortDirection direction, boolean ignoreCase) { - return new Order<>( direction, NullPrecedence.NONE, element, ignoreCase ); + return new Order<>( direction, Nulls.NONE, element, ignoreCase ); } /** @@ -273,7 +274,7 @@ public static Order by(int element, SortDirection direction, boolean i * precedence for null values. If the named attribute is of * textual type, the ordering is case-sensitive. */ - public static Order by(int element, SortDirection direction, NullPrecedence nullPrecedence) { + public static Order by(int element, SortDirection direction, Nulls nullPrecedence) { return new Order<>( direction, nullPrecedence, element ); } @@ -281,7 +282,7 @@ public SortDirection getDirection() { return order; } - public NullPrecedence getNullPrecedence() { + public Nulls getNullPrecedence() { return nullPrecedence; } @@ -326,7 +327,7 @@ public Order ignoringCase() { * @since 6.5 */ public Order withNullsFirst() { - return new Order<>( this, NullPrecedence.FIRST ); + return new Order<>( this, Nulls.FIRST ); } /** @@ -334,7 +335,37 @@ public Order withNullsFirst() { * @since 6.5 */ public Order withNullsLast() { - return new Order<>( this, NullPrecedence.LAST ); + return new Order<>( this, Nulls.LAST ); + } + + /** + * An order based on this order, possibly reversed. + * + * @param reverse {@code true} if the returned order should be + * {@linkplain #reverse reversed} + * @return this order, but reversed if the argument is {@code true} + * + * @apiNote This is a convenience for use with Jakarta Data + * + * @since 7.0 + */ + public Order reversedIf(boolean reverse) { + return reverse ? reverse() : this; + } + + /** + * An order based on this order, possibly without case-sensitivity. + * + * @param ignoreCase {@code true} if this order should be + * {@linkplain #ignoringCase ignore case} + * @return this order, but ignoring case if the argument is {@code true} + * + * @apiNote This is a convenience for use with Jakarta Data + * + * @since 7.0 + */ + public Order ignoringCaseIf(boolean ignoreCase) { + return ignoreCase ? ignoringCase() : this; } @Override @@ -343,9 +374,8 @@ public String toString() { } @Override - public boolean equals(Object o) { - if ( o instanceof Order) { - Order that = (Order) o; + public boolean equals(Object object) { + if ( object instanceof Order that) { return that.order == this.order && that.nullPrecedence == this.nullPrecedence && that.ignoreCase == this.ignoreCase 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 f3bc7d88b13f..caa5dfe6a8f0 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 @@ -7,7 +7,6 @@ import java.sql.Types; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.IdentityHashMap; import java.util.Iterator; @@ -18,9 +17,9 @@ import java.util.Set; import java.util.StringTokenizer; +import org.hibernate.AssertionFailure; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.Stack; import org.hibernate.jpa.spi.JpaCompliance; import org.hibernate.metamodel.mapping.BasicValuedMapping; @@ -36,13 +35,12 @@ import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.PluralAttributeMapping; 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.IdentifiableDomainType; 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.internal.EntitySqmPathSource; +import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.IllegalSelectQueryException; @@ -99,15 +97,18 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.internal.ConvertedBasicTypeImpl; -import org.hibernate.type.spi.TypeConfiguration; import jakarta.persistence.Tuple; import jakarta.persistence.metamodel.Type; 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 org.hibernate.internal.util.NullnessUtil.castNonNull; 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; /** @@ -171,17 +172,13 @@ public static ModelPartContainer getTargetMappingIfNeeded( SqmToSqlAstConverter sqlAstCreationState) { // We only need to do this for queries if ( sqlAstCreationState.getCurrentClauseStack().getCurrent() != Clause.FROM - && modelPartContainer.getPartMappingType() != modelPartContainer && sqmPath.getLhs() instanceof SqmFrom ) { - final ModelPart modelPart; - if ( modelPartContainer instanceof PluralAttributeMapping pluralAttributeMapping ) { - modelPart = getCollectionPart( - pluralAttributeMapping, - castNonNull( sqmPath.getNavigablePath().getParent() ) - ); - } - else { - modelPart = modelPartContainer; - } + && modelPartContainer.getPartMappingType() != modelPartContainer + && sqmPath.getLhs() instanceof SqmFrom ) { + final ModelPart modelPart = + modelPartContainer instanceof PluralAttributeMapping pluralAttributeMapping + ? getCollectionPart( pluralAttributeMapping, + castNonNull( sqmPath.getNavigablePath().getParent() ) ) + : modelPartContainer; if ( modelPart instanceof EntityAssociationMapping association ) { if ( shouldRenderTargetSide( sqmPath, association, sqlAstCreationState ) ) { return association.getAssociatedEntityMappingType(); @@ -198,13 +195,15 @@ private static boolean shouldRenderTargetSide( if ( !association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() ) ) { return false; } - // If the path is one of the association's target key properties, - // we need to render the target side if in group/order by - final Clause clause = sqlAstCreationState.getCurrentClauseStack().getCurrent(); - return clause == Clause.GROUP || clause == Clause.ORDER + else { + // If the path is one of the association's target key properties, + // we need to render the target side if in group/order by + final Clause clause = sqlAstCreationState.getCurrentClauseStack().getCurrent(); + return clause == Clause.GROUP || clause == Clause.ORDER || !isFkOptimizationAllowed( sqmPath.getLhs(), association ) || clauseContainsPath( Clause.GROUP, sqmPath, sqlAstCreationState ) || clauseContainsPath( Clause.ORDER, sqmPath, sqlAstCreationState ); + } } private static boolean clauseContainsPath( @@ -215,10 +214,8 @@ private static boolean clauseContainsPath( final NavigablePath navigablePath = sqmPath.getNavigablePath(); final Boolean found = queryPartStack.findCurrentFirst( queryPart -> { final SqmQuerySpec spec = queryPart.getFirstQuerySpec(); - if ( clauseToCheck == Clause.GROUP && spec.groupByClauseContains( navigablePath, sqlAstCreationState ) ) { - return true; - } - else if ( clauseToCheck == Clause.ORDER && spec.orderByClauseContains( navigablePath, sqlAstCreationState ) ) { + if ( clauseToCheck == Clause.GROUP && spec.groupByClauseContains( navigablePath, sqlAstCreationState ) + || clauseToCheck == Clause.ORDER && spec.orderByClauseContains( navigablePath, sqlAstCreationState ) ) { return true; } else { @@ -290,9 +287,9 @@ public static boolean isFkOptimizationAllowed(SqmPath sqmPath, EntityAssociat private static boolean isFiltered(EntityAssociationMapping associationMapping) { final EntityMappingType entityMappingType = associationMapping.getAssociatedEntityMappingType(); return !associationMapping.isFkOptimizationAllowed() - // When the identifier mappings are different we have a joined subclass entity - // which will filter rows based on a discriminator predicate - || entityMappingType.getIdentifierMappingForJoin() != entityMappingType.getIdentifierMapping(); + // When the identifier mappings are different we have a joined subclass entity + // which will filter rows based on a discriminator predicate + || entityMappingType.getIdentifierMappingForJoin() != entityMappingType.getIdentifierMapping(); } private static @Nullable EntityAssociationMapping resolveAssociationMapping(SqmJoin sqmJoin) { @@ -305,6 +302,7 @@ private static boolean isFiltered(EntityAssociationMapping associationMapping) { } private static @Nullable EntityAssociationMapping resolveAssociationMapping(SqmSingularJoin sqmJoin) { + final MappingMetamodelImplementor metamodel = sqmJoin.nodeBuilder().getMappingMetamodel(); SingularPersistentAttribute attribute = sqmJoin.getAttribute(); ManagedDomainType declaringType = attribute.getDeclaringType(); if ( declaringType.getPersistenceType() != Type.PersistenceType.ENTITY ) { @@ -314,66 +312,62 @@ private static boolean isFiltered(EntityAssociationMapping associationMapping) { pathBuilder.insert(0, '.'); } pathBuilder.insert( 0, attribute.getName() ); - final SqmFrom lhs = sqmJoin.getLhs(); - if ( !(lhs instanceof SqmSingularJoin ) ) { + if ( sqmJoin.getLhs() instanceof SqmSingularJoin sqmSingularJoin ) { + sqmJoin = sqmSingularJoin; + attribute = sqmJoin.getAttribute(); + declaringType = attribute.getDeclaringType(); + } + else { return null; } - sqmJoin = (SqmSingularJoin) lhs; - attribute = sqmJoin.getAttribute(); - declaringType = attribute.getDeclaringType(); - } while (declaringType.getPersistenceType() != Type.PersistenceType.ENTITY ); + } while ( declaringType.getPersistenceType() != Type.PersistenceType.ENTITY ); pathBuilder.insert(0, '.'); pathBuilder.insert( 0, attribute.getName() ); - final EntityPersister entityDescriptor = - sqmJoin.nodeBuilder().getMappingMetamodel() - .getEntityDescriptor( ( (EntityDomainType) declaringType ).getHibernateEntityName() ); - return (EntityAssociationMapping) entityDescriptor.findByPath( pathBuilder.toString() ); + return (EntityAssociationMapping) + metamodel.getEntityDescriptor( ( (EntityDomainType) declaringType ).getHibernateEntityName() ) + .findByPath( pathBuilder.toString() ); } else { - final EntityPersister entityDescriptor = - sqmJoin.nodeBuilder().getMappingMetamodel() - .getEntityDescriptor( ( (EntityDomainType) declaringType ).getHibernateEntityName() ); - return (EntityAssociationMapping) entityDescriptor.findAttributeMapping( attribute.getName() ); + return (EntityAssociationMapping) + metamodel.getEntityDescriptor( ( (EntityDomainType) declaringType ).getHibernateEntityName() ) + .findAttributeMapping( attribute.getName() ); } } public static List getWhereClauseNavigablePaths(SqmQuerySpec querySpec) { final SqmWhereClause where = querySpec.getWhereClause(); - if ( where == null || where.getPredicate() == null ) { - return Collections.emptyList(); - } + return where == null || where.getPredicate() == null + ? emptyList() + : collectNavigablePaths( List.of( where.getPredicate() ) ); - return collectNavigablePaths( List.of( where.getPredicate() ) ); } public static List getGroupByNavigablePaths(SqmQuerySpec querySpec) { final List> expressions = querySpec.getGroupByClauseExpressions(); - if ( expressions.isEmpty() ) { - return Collections.emptyList(); - } + return expressions.isEmpty() ? emptyList() : collectNavigablePaths( expressions ); - return collectNavigablePaths( expressions ); } public static List getOrderByNavigablePaths(SqmQuerySpec querySpec) { final SqmOrderByClause order = querySpec.getOrderByClause(); if ( order == null || order.getSortSpecifications().isEmpty() ) { - return Collections.emptyList(); + return emptyList(); + } + else { + final List> expressions = + order.getSortSpecifications().stream() + .map( SqmSortSpecification::getSortExpression ) + .collect( toList() ); + return collectNavigablePaths( expressions ); } - - final List> expressions = order.getSortSpecifications() - .stream() - .map( SqmSortSpecification::getSortExpression ) - .collect( toList() ); - return collectNavigablePaths( expressions ); } private static List collectNavigablePaths(final List> expressions) { final List navigablePaths = arrayList( expressions.size() ); final SqmPathVisitor pathVisitor = new SqmPathVisitor( path -> navigablePaths.add( path.getNavigablePath() ) ); for ( final SqmExpression expression : expressions ) { - if ( expression instanceof SqmAliasedNodeRef ) { - final NavigablePath navigablePath = ( (SqmAliasedNodeRef) expression ).getNavigablePath(); + if ( expression instanceof SqmAliasedNodeRef sqmAliasedNodeRef ) { + final NavigablePath navigablePath = sqmAliasedNodeRef.getNavigablePath(); if ( navigablePath != null ) { navigablePaths.add( navigablePath ); } @@ -415,50 +409,39 @@ public static Map, Map, List, Map, List>> result = new IdentityHashMap<>( queryParameterCount ); - - for ( Map.Entry, List>> entry : domainParameterXref.getQueryParameters().entrySet() ) { - final QueryParameterImplementor queryParam = entry.getKey(); - final List> sqmParams = entry.getValue(); - - final Map, List> sqmParamMap = result.computeIfAbsent( - queryParam, - qp -> new IdentityHashMap<>( sqmParams.size() ) - ); - - for ( SqmParameter sqmParam : sqmParams ) { - List> lists = jdbcParameterBySqmParameterAccess.getJdbcParamsBySqmParam().get( - sqmParam ); - sqmParamMap.put( sqmParam, convert( lists ) ); - - final List> expansions = domainParameterXref.getExpansions( sqmParam ); - if ( ! expansions.isEmpty() ) { - for ( SqmParameter expansion : expansions ) { - List> innerList = jdbcParameterBySqmParameterAccess.getJdbcParamsBySqmParam() - .get( expansion ); - sqmParamMap.put( expansion, convert( innerList) ); + else { + final Map, Map, List>> result = + new IdentityHashMap<>( domainParameterXref.getQueryParameterCount() ); + domainParameterXref.getQueryParameters().forEach( (queryParam, sqmParams) -> { + final Map, List> sqmParamMap = + result.computeIfAbsent( queryParam, qp -> new IdentityHashMap<>( sqmParams.size() ) ); + for ( SqmParameter sqmParam : sqmParams ) { + final Map, List>> jdbcParamsBySqmParam = + jdbcParameterBySqmParameterAccess.getJdbcParamsBySqmParam(); + sqmParamMap.put( sqmParam, convert( jdbcParamsBySqmParam.get( sqmParam ) ) ); + for ( SqmParameter expansion : domainParameterXref.getExpansions( sqmParam ) ) { + sqmParamMap.put( expansion, convert( jdbcParamsBySqmParam.get( expansion ) ) ); result.put( queryParam, sqmParamMap ); } } - } + } ); + return result; } - - return result; } private static List convert(final List> lists) { if ( lists == null ) { return null; } - List output = new ArrayList<>( lists.size() ); - for ( List element : lists ) { - output.add( JdbcParametersList.fromList( element ) ); + else { + final List output = new ArrayList<>( lists.size() ); + for ( List element : lists ) { + output.add( JdbcParametersList.fromList( element ) ); + } + return output; } - return output; } // public static JdbcParameterBindings buildJdbcParameterBindings( @@ -507,17 +490,10 @@ public static JdbcParameterBindings createJdbcParameterBindings( Map, Map, List>> jdbcParamXref, SqmParameterMappingModelResolutionAccess mappingModelResolutionAccess, SharedSessionContractImplementor session) { - final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( - domainParameterXref.getSqmParameterCount() - ); - - for ( Map.Entry, List>> entry : - domainParameterXref.getQueryParameters().entrySet() ) { - final QueryParameterImplementor queryParam = entry.getKey(); - final List> sqmParameters = entry.getValue(); - + final JdbcParameterBindings jdbcParameterBindings = + new JdbcParameterBindingsImpl( domainParameterXref.getSqmParameterCount() ); + domainParameterXref.getQueryParameters().forEach( (queryParam, sqmParameters) -> { final QueryParameterBinding domainParamBinding = domainParamBindings.getBinding( queryParam ); - final Map, List> jdbcParamMap = jdbcParamXref.get( queryParam ); for ( SqmParameter sqmParameter : sqmParameters ) { final MappingModelExpressible resolvedMappingModelType = @@ -552,7 +528,6 @@ public static JdbcParameterBindings createJdbcParameterBindings( else if ( domainParamBinding.isMultiValued() ) { final Collection bindValues = domainParamBinding.getBindValues(); 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++ ) { @@ -568,7 +543,7 @@ else if ( domainParamBinding.isMultiValued() ) { ); } - // an then one for each of the expansions + // and then one for each of the expansions final List> expansions = domainParameterXref.getExpansions( sqmParameter ); final int expansionCount = bindValues.size() - 1; final int parameterUseCount = jdbcParamsBinds.size(); @@ -580,7 +555,7 @@ else if ( domainParamBinding.isMultiValued() ) { 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 ); + final JdbcParametersList expansionJdbcParams = jdbcParamBinds.get( i ); createValueBindings( jdbcParameterBindings, queryParam, @@ -597,9 +572,10 @@ else if ( domainParamBinding.isMultiValued() ) { } else { final JdbcMapping jdbcMapping; - if ( domainParamBinding.getType() instanceof JdbcMapping ) { - jdbcMapping = (JdbcMapping) domainParamBinding.getType(); + if ( domainParamBinding.getType() instanceof JdbcMapping mapping ) { + jdbcMapping = mapping; } + // TODO: why do the test and the cast disagree here? getBindType() vs getType() else if ( domainParamBinding.getBindType() instanceof BasicValuedMapping ) { jdbcMapping = ( (BasicValuedMapping) domainParamBinding.getType() ).getJdbcMapping(); } @@ -607,17 +583,17 @@ else if ( domainParamBinding.getBindType() instanceof BasicValuedMapping ) { jdbcMapping = null; } - final BasicValueConverter valueConverter = jdbcMapping == null ? null : jdbcMapping.getValueConverter(); + final BasicValueConverter valueConverter = + jdbcMapping == null ? null : jdbcMapping.getValueConverter(); 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++ ) { final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i ); assert jdbcParams.size() == 1; final JdbcParameter jdbcParameter = jdbcParams.get( 0 ); - jdbcParameterBindings.addBinding( - jdbcParameter, - new JdbcParameterBindingImpl( jdbcMapping, convertedValue ) - ); + jdbcParameterBindings.addBinding( jdbcParameter, + new JdbcParameterBindingImpl( jdbcMapping, convertedValue ) ); } } else { @@ -627,10 +603,8 @@ else if ( domainParamBinding.getBindType() instanceof BasicValuedMapping ) { final JdbcParametersList jdbcParams = jdbcParamsBinds.get( i ); for ( int j = 0; j < jdbcParams.size(); j++ ) { final JdbcParameter jdbcParameter = jdbcParams.get( j ); - jdbcParameterBindings.addBinding( - jdbcParameter, - new JdbcParameterBindingImpl( jdbcMapping, bindValue ) - ); + jdbcParameterBindings.addBinding( jdbcParameter, + new JdbcParameterBindingImpl( jdbcMapping, bindValue ) ); } } } @@ -651,7 +625,7 @@ else if ( domainParamBinding.getBindType() instanceof BasicValuedMapping ) { } } } - } + } ); return jdbcParameterBindings; } @@ -667,22 +641,24 @@ private static void createValueBindings( if ( parameterType == null ) { 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 - parameterType = ( (PluralAttributeMapping) parameterType ).getElementDescriptor(); + parameterType = pluralAttributeMapping.getElementDescriptor(); } if ( parameterType instanceof EntityIdentifierMapping identifierMapping ) { 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 ); } } - else if ( parameterType instanceof EntityMappingType ) { - final EntityIdentifierMapping identifierMapping = ( (EntityMappingType) parameterType ).getIdentifierMapping(); + else if ( parameterType instanceof EntityMappingType entityMappingType ) { + final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping(); final EntityMappingType entityMapping = identifierMapping.findContainingEntityMapping(); parameterType = identifierMapping; - if ( entityMapping.getRepresentationStrategy().getInstantiator().isInstance( bindValue, session.getFactory() ) ) { + if ( entityMapping.getRepresentationStrategy().getInstantiator() + .isInstance( bindValue, session.getFactory() ) ) { bindValue = identifierMapping.getIdentifierIfNotUnsaved( bindValue, session ); } } @@ -694,11 +670,8 @@ else if ( parameterType instanceof EntityAssociationMapping association ) { parameterType = association.getAssociatedEntityMappingType().getIdentifierMapping(); } else { - bindValue = association.getForeignKeyDescriptor().getAssociationKeyFromSide( - bindValue, - association.getSideNature().inverse(), - session - ); + bindValue = association.getForeignKeyDescriptor() + .getAssociationKeyFromSide( bindValue, association.getSideNature().inverse(), session ); parameterType = association.getForeignKeyDescriptor(); } } @@ -706,12 +679,8 @@ else if ( parameterType instanceof JavaObjectType ) { parameterType = domainParamBinding.getType(); } - int offset = jdbcParameterBindings.registerParametersForEachJdbcValue( - bindValue, - parameterType, - jdbcParams, - session - ); + final int offset = + jdbcParameterBindings.registerParametersForEachJdbcValue( bindValue, parameterType, jdbcParams, session ); assert offset == jdbcParams.size(); } @@ -748,10 +717,9 @@ public static Bindable determineParameterType( } } - final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration(); - // 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 static Bindable determineParameterType( * Returns the passes object after casting it to Bindable, * if the type is compatible. * 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. */ - private static Bindable asBindable(final Object o) { - if ( o == null ) { + private static Bindable asBindable(final Object object) { + if ( object == null ) { return null; } //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; } //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; } + //Eventually fallback to the standard check for completeness: + else if ( object instanceof Bindable bindable ) { + return bindable; + } else { - //Eventually fallback to the standard check for completeness: - if ( o instanceof Bindable ) { - return (Bindable) o; - } return null; } } @@ -798,7 +766,7 @@ public Set> getSqmParameters() { @Override public Map, SqmJpaCriteriaParameterWrapper> getJpaCriteriaParamResolutions() { - return Collections.emptyMap(); + return emptyMap(); } }; } @@ -823,7 +791,7 @@ static JpaOrder sortSpecification(SqmSelectStatement sqm, Order order) { return new SqmSortSpecification( new SqmAliasedNodeRef( element, builder.getIntegerType(), builder ), order.getDirection(), - order.getNullPrecedence().getJpaValue(), + order.getNullPrecedence(), order.isCaseInsensitive() ); } @@ -856,10 +824,12 @@ public static boolean isSelectionAssignableToResultType(SqmSelection selectio return true; } else if ( selection != null && selection.getSelectableNode() instanceof SqmParameter sqmParameter ) { - final Class anticipatedClass = sqmParameter.getAnticipatedType() != null ? - sqmParameter.getAnticipatedType().getBindableJavaType() : - null; - return anticipatedClass != null && expectedResultType.isAssignableFrom( anticipatedClass ); + final Class anticipatedClass = + sqmParameter.getAnticipatedType() != null + ? sqmParameter.getAnticipatedType().getBindableJavaType() + : null; + return anticipatedClass != null + && expectedResultType.isAssignableFrom( anticipatedClass ); } else if ( selection == null || !isHqlTuple( selection ) && selection.getSelectableNode().isCompoundSelection() ) { @@ -897,14 +867,8 @@ public void process(SqmParameter parameter) { jpaCriteriaParamResolutions = new IdentityHashMap<>(); } - final JpaCriteriaParameter criteriaParameter = wrapper.getJpaCriteriaParameter(); - - final List> sqmParametersForCriteriaParameter = jpaCriteriaParamResolutions.computeIfAbsent( - criteriaParameter, - jcp -> new ArrayList<>() - ); - - sqmParametersForCriteriaParameter.add( wrapper ); + jpaCriteriaParamResolutions.computeIfAbsent( wrapper.getJpaCriteriaParameter(), r -> new ArrayList<>() ) + .add( wrapper ); sqmParameters.add( wrapper ); } else if ( parameter instanceof JpaCriteriaParameter ) { @@ -936,8 +900,8 @@ else if ( parameter instanceof JpaCriteriaParameter ) { private SqmStatement.ParameterResolutions makeResolution() { return new ParameterResolutionsImpl( - sqmParameters == null ? Collections.emptySet() : sqmParameters, - jpaCriteriaParamResolutions == null ? Collections.emptyMap() : jpaCriteriaParamResolutions + sqmParameters == null ? emptySet() : sqmParameters, + jpaCriteriaParamResolutions == null ? emptyMap() : jpaCriteriaParamResolutions ); } } @@ -952,15 +916,18 @@ public ParameterResolutionsImpl( this.sqmParameters = sqmParameters; if ( jpaCriteriaParamResolutions == null || jpaCriteriaParamResolutions.isEmpty() ) { - this.jpaCriteriaParamResolutions = Collections.emptyMap(); + this.jpaCriteriaParamResolutions = emptyMap(); } else { - this.jpaCriteriaParamResolutions = new IdentityHashMap<>( CollectionHelper.determineProperSizing( jpaCriteriaParamResolutions ) ); - for ( Map.Entry, List>> entry : jpaCriteriaParamResolutions.entrySet() ) { + this.jpaCriteriaParamResolutions = + new IdentityHashMap<>( determineProperSizing( jpaCriteriaParamResolutions ) ); + for ( Map.Entry, List>> entry + : jpaCriteriaParamResolutions.entrySet() ) { final Iterator> itr = entry.getValue().iterator(); if ( !itr.hasNext() ) { 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() ); } @@ -993,15 +960,17 @@ public static void validateQueryReturnType(SqmQueryPart queryPart, @Nullable * Similar to {@link #validateQueryReturnType(SqmQueryPart, Class)} but does not check if {@link #isResultTypeAlwaysAllowed(Class)}. */ public static void checkQueryReturnType(SqmQueryPart queryPart, Class expectedResultType) { - if ( queryPart instanceof SqmQuerySpec ) { - checkQueryReturnType( (SqmQuerySpec) queryPart, expectedResultType ); + if ( queryPart instanceof SqmQuerySpec querySpec ) { + checkQueryReturnType( querySpec, expectedResultType ); } - else { - final SqmQueryGroup queryGroup = (SqmQueryGroup) queryPart; + else if ( queryPart instanceof SqmQueryGroup queryGroup ) { for ( SqmQueryPart sqmQueryPart : queryGroup.getQueryParts() ) { checkQueryReturnType( sqmQueryPart, expectedResultType ); } } + else { + throw new AssertionFailure( "Unexpected query part" ); + } } private static void checkQueryReturnType(SqmQuerySpec querySpec, Class expectedResultClass) { @@ -1116,54 +1085,41 @@ public static boolean isResultTypeAlwaysAllowed(Class expectedResultClass) { } protected static void verifyResultType(Class resultClass, @Nullable SqmExpressible selectionExpressible) { - if ( selectionExpressible == null ) { - // nothing we can validate - return; - } - - final JavaType selectionExpressibleJavaType = selectionExpressible.getExpressibleJavaType(); - if ( selectionExpressibleJavaType == null ) { - // 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 ( selectionExpressible != null ) { + final JavaType javaType = selectionExpressible.getExpressibleJavaType(); + if ( javaType != null ) { + final Class javaTypeClass = javaType.getJavaTypeClass(); + if ( javaTypeClass != Object.class ) { + if ( !isValid( resultClass, selectionExpressible, javaTypeClass, javaType ) ) { + throwQueryTypeMismatchException( resultClass, selectionExpressible ); + } } } - - 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) { if ( selectionExpressible instanceof IdentifiableDomainType identifiableDomainType ) { - final SimpleDomainType idType = identifiableDomainType.getIdType(); - return resultClass.isAssignableFrom( idType.getBindableJavaType() ); + return resultClass.isAssignableFrom( identifiableDomainType.getIdType().getBindableJavaType() ); } else if ( selectionExpressible instanceof EntitySqmPathSource entityPath ) { - final EntityDomainType entityType = entityPath.getSqmPathType(); - final SimpleDomainType idType = entityType.getIdType(); - return resultClass.isAssignableFrom( idType.getBindableJavaType() ); + return resultClass.isAssignableFrom( entityPath.getSqmPathType().getIdType().getBindableJavaType() ); + } + else { + return false; } - - return false; } // Special case for date because we always report java.util.Date as expression type @@ -1173,17 +1129,16 @@ private static boolean isMatchingDateType( Class resultClass, SqmExpressible sqmExpressible) { return javaTypeClass == Date.class - && isMatchingDateJdbcType( resultClass, getJdbcType( sqmExpressible ) ); + && isMatchingDateJdbcType( resultClass, getJdbcType( sqmExpressible ) ); } private static JdbcType getJdbcType(SqmExpressible sqmExpressible) { - if ( sqmExpressible instanceof BasicDomainType ) { - return ( (BasicDomainType) sqmExpressible).getJdbcType(); + if ( sqmExpressible instanceof BasicDomainType basicDomainType ) { + return basicDomainType.getJdbcType(); } else if ( sqmExpressible instanceof SqmPathSource pathSource ) { - final DomainType domainType = pathSource.getSqmPathType(); - if ( domainType instanceof BasicDomainType ) { - return ( (BasicDomainType) domainType ).getJdbcType(); + if ( pathSource.getSqmPathType() instanceof BasicDomainType basicDomainType ) { + return basicDomainType.getJdbcType(); } } return null; diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractQueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractQueryMethod.java index b45cd1caf2ea..2b15c136acc4 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractQueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractQueryMethod.java @@ -500,16 +500,18 @@ void collectOrdering(StringBuilder declaration, List paramTypes) { annotationMetaEntity.staticImport(HIB_SORT_DIRECTION, "*"); declaration .append("\t_orders.add(") - .append(annotationMetaEntity.staticImport(HIB_ORDER, "by")) + .append(annotationMetaEntity.staticImport(HIB_ORDER, orderBy.descending ? "desc" : "asc")) .append('(') .append(annotationMetaEntity.importType(returnTypeName)) .append(".class, \"") .append(orderBy.fieldName) - .append("\", ") - .append(orderBy.descending ? "DESCENDING" : "ASCENDING") - .append(", ") - .append(orderBy.ignoreCase) - .append("));\n"); + .append("\")"); + if ( orderBy.ignoreCase ) { + declaration + .append("\n\t.ignoringCase()"); + } + declaration + .append(");\n"); } for (int i = 0; i < paramTypes.size(); i++) { @@ -542,14 +544,14 @@ else if ( type.startsWith(JD_ORDER) ) { .append(name) .append(".sorts()) {\n") .append("\t\t_orders.add(") - .append(annotationMetaEntity.staticImport(HIB_ORDER, "by")) + .append(annotationMetaEntity.staticImport(HIB_ORDER, "asc")) .append('(') .append(annotationMetaEntity.importType(returnTypeName)) - .append(".class, _sort.property(),") - .append("\n\t\t\t\t\t\t") - .append("_sort.isAscending() ? ASCENDING : DESCENDING,") - .append("\n\t\t\t\t\t\t") - .append("_sort.ignoreCase()));\n") + .append(".class, _sort.property())") + .append("\n\t\t\t\t\t") + .append(".reversedIf(_sort.isDescending())") + .append("\n\t\t\t\t\t") + .append(".ignoringCaseIf(_sort.ignoreCase()));\n") .append("\t}\n"); } else if ( type.startsWith(JD_SORT) && type.endsWith("...") ) { @@ -560,30 +562,32 @@ else if ( type.startsWith(JD_SORT) && type.endsWith("...") ) { .append(name) .append(") {\n") .append("\t\t_orders.add(") - .append(annotationMetaEntity.staticImport(HIB_ORDER, "by")) + .append(annotationMetaEntity.staticImport(HIB_ORDER, "asc")) .append('(') .append(annotationMetaEntity.importType(returnTypeName)) - .append(".class, _sort.property(),") - .append("\n\t\t\t\t\t\t") - .append("_sort.isAscending() ? ASCENDING : DESCENDING,") - .append("\n\t\t\t\t\t\t") - .append("_sort.ignoreCase()));\n") + .append(".class, _sort.property())") + .append("\n\t\t\t\t\t") + .append(".reversedIf(_sort.isDescending())") + .append("\n\t\t\t\t\t") + .append(".ignoringCaseIf(_sort.ignoreCase()));\n") .append("\t}\n"); } else if ( type.startsWith(JD_SORT) ) { annotationMetaEntity.staticImport(HIB_SORT_DIRECTION, "*"); declaration .append("\t_orders.add(") - .append(annotationMetaEntity.staticImport(HIB_ORDER, "by")) + .append(annotationMetaEntity.staticImport(HIB_ORDER, "asc")) .append('(') .append(annotationMetaEntity.importType(returnTypeName)) .append(".class, ") .append(name) - .append(".property(),") - .append("\n\t\t\t\t\t\t") + .append(".property())") + .append("\n\t\t\t\t\t") + .append(".reversedIf(") .append(name) - .append(".isAscending() ? ASCENDING : DESCENDING,") - .append("\n\t\t\t\t\t\t") + .append(".isDescending())") + .append("\n\t\t\t\t\t") + .append(".ignoringCaseIf(") .append(name) .append(".ignoreCase()));\n"); }