diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPath.java index 447b0e59687c..2c7bb5db2d4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPath.java @@ -13,7 +13,6 @@ import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.spi.NavigablePath; -import java.util.Objects; public class AnyDiscriminatorSqmPath extends AbstractSqmPath implements DiscriminatorSqmPath { @@ -49,15 +48,25 @@ public AnyDiscriminatorSqmPathSource getExpressible() { return (AnyDiscriminatorSqmPathSource) getReferencedPathSource(); } - @Override public boolean equals(Object object) { return object instanceof AnyDiscriminatorSqmPath that - && Objects.equals( this.getLhs(), that.getLhs() ); + && getLhs().equals( that.getLhs() ); } @Override public int hashCode() { return getLhs().hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return object instanceof AnyDiscriminatorSqmPath that + && getLhs().isCompatible( that.getLhs() ); + } + + @Override + public int cacheHashCode() { + return getLhs().cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedDiscriminatorSqmPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedDiscriminatorSqmPath.java index ed51523984fd..78ade731285b 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedDiscriminatorSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedDiscriminatorSqmPath.java @@ -14,8 +14,6 @@ import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.spi.NavigablePath; -import java.util.Objects; - /** * {@link SqmPath} specialization for an embeddable discriminator * @@ -65,11 +63,22 @@ public X accept(SemanticQueryWalker walker) { @Override public boolean equals(Object object) { return object instanceof EmbeddedDiscriminatorSqmPath that - && Objects.equals( this.getLhs(), that.getLhs() ); + && getLhs().equals( that.getLhs() ); } @Override public int hashCode() { return getLhs().hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return object instanceof EmbeddedDiscriminatorSqmPath that + && getLhs().isCompatible( that.getLhs() ); + } + + @Override + public int cacheHashCode() { + return getLhs().cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java index 539a37f88a7f..b2e86951085f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java @@ -17,7 +17,6 @@ import org.hibernate.query.sqm.tree.domain.SqmEntityDomainType; import org.hibernate.spi.NavigablePath; -import java.util.Objects; /** * {@link SqmPath} specialization for an entity discriminator @@ -79,11 +78,22 @@ public X accept(SemanticQueryWalker walker) { @Override public boolean equals(Object object) { return object instanceof EntityDiscriminatorSqmPath that - && Objects.equals( this.getLhs(), that.getLhs() ); + && getLhs().equals( that.getLhs() ); } @Override public int hashCode() { return getLhs().hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return object instanceof EntityDiscriminatorSqmPath that + && getLhs().isCompatible( that.getLhs() ); + } + + @Override + public int cacheHashCode() { + return getLhs().cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePath.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePath.java deleted file mode 100644 index ebf298fb6760..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePath.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.query.hql.internal; - -import org.hibernate.query.hql.spi.SemanticPathPart; -import org.hibernate.query.hql.spi.SqmCreationState; -import org.hibernate.query.sqm.tree.domain.SqmPath; -import org.hibernate.query.sqm.tree.expression.SqmExpression; - -/** - * @author Steve Ebersole - */ -public class FullyQualifiedReflectivePath implements SemanticPathPart, FullyQualifiedReflectivePathSource { - private final FullyQualifiedReflectivePathSource pathSource; - private final String localName; - - public FullyQualifiedReflectivePath( - FullyQualifiedReflectivePathSource pathSource, - String localName) { - this.pathSource = pathSource; - this.localName = localName; - } - - @Override - public FullyQualifiedReflectivePath resolvePathPart( - String name, - boolean isTerminal, - SqmCreationState creationState) { - if ( isTerminal ) { - return new FullyQualifiedReflectivePathTerminal<>( this, name, creationState ); - } - else { - return new FullyQualifiedReflectivePath( this, name ); - } - } - - @Override - public SqmPath resolveIndexedAccess( - SqmExpression selector, - boolean isTerminal, - SqmCreationState creationState) { - throw new UnsupportedOperationException( "Fully qualified reflective paths cannot contain indexed access" ); - } - - @Override - public FullyQualifiedReflectivePathSource getParent() { - return pathSource; - } - - @Override - public String getLocalName() { - return localName; - } - - @Override - public String getFullPath() { - return pathSource.getFullPath() + '.' + localName; - } - - @Override - public FullyQualifiedReflectivePath append(String subPathName) { - return new FullyQualifiedReflectivePath( this, subPathName ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathSource.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathSource.java deleted file mode 100644 index 78a4f529ff45..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathSource.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.query.hql.internal; - -import org.hibernate.spi.DotIdentifierSequence; -import org.hibernate.query.hql.spi.SemanticPathPart; - -/** - * @author Steve Ebersole - */ -public interface FullyQualifiedReflectivePathSource extends DotIdentifierSequence, SemanticPathPart { -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathTerminal.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathTerminal.java deleted file mode 100644 index 5cef5fab437e..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathTerminal.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.query.hql.internal; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Collection; -import java.util.List; -import java.util.function.Function; - -import org.checkerframework.checker.nullness.qual.Nullable; -import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; -import org.hibernate.metamodel.model.domain.EntityDomainType; -import org.hibernate.query.criteria.JpaSelection; -import org.hibernate.query.hql.HqlInterpretationException; -import org.hibernate.query.hql.spi.SqmCreationState; -import org.hibernate.query.sqm.NodeBuilder; -import org.hibernate.query.sqm.SqmBindableType; -import org.hibernate.query.sqm.SemanticQueryWalker; -import org.hibernate.query.sqm.spi.SqmCreationContext; -import org.hibernate.query.sqm.tree.SqmCopyContext; -import org.hibernate.query.sqm.tree.SqmRenderContext; -import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral; -import org.hibernate.query.sqm.tree.expression.SqmExpression; -import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral; -import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType; -import org.hibernate.query.sqm.tree.domain.SqmEntityDomainType; -import org.hibernate.query.sqm.tree.predicate.SqmPredicate; -import org.hibernate.type.descriptor.java.EnumJavaType; -import org.hibernate.type.descriptor.java.JavaType; - -import jakarta.persistence.criteria.Expression; - -import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry; - -/** - * @author Steve Ebersole - */ -public class FullyQualifiedReflectivePathTerminal - extends FullyQualifiedReflectivePath - implements SqmExpression { - private final @Nullable SqmBindableType expressibleType; - private final SqmCreationState creationState; - - private final Function,?> handler; - - public FullyQualifiedReflectivePathTerminal( - FullyQualifiedReflectivePathSource pathSource, - String subPathName, - SqmCreationState creationState) { - super( pathSource, subPathName ); - this.creationState = creationState; - - this.handler = resolveTerminalSemantic(); - - // todo (6.0) : how to calculate this? - this.expressibleType = null; - } - - @Override - public FullyQualifiedReflectivePathTerminal copy(SqmCopyContext context) { - return this; - } - - private Function, ?> resolveTerminalSemantic() { - return semanticQueryWalker -> { - final SqmCreationContext creationContext = creationState.getCreationContext(); - final String fullPath = getFullPath(); - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // See if it is an entity-type literal - - final EntityDomainType entityDescriptor = - creationContext.getJpaMetamodel().findEntityType( fullPath ); - if ( entityDescriptor != null ) { - return new SqmLiteralEntityType<>( - (SqmEntityDomainType) entityDescriptor, - creationContext.getNodeBuilder() - ); - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // See if it is a Class FQN - - try { - final Class namedClass = creationContext.classForName( fullPath ); - if ( namedClass != null ) { - return semanticQueryWalker.visitFullyQualifiedClass( namedClass ); - } - } - catch (ClassLoadingException ignore) { - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Check the parent path as a Class FQN, meaning the terminal is a field or - // enum-value - - final String parentFullPath = getParent().getFullPath(); - try { - final Class namedClass = creationContext.classForName( parentFullPath ); - if ( namedClass != null ) { - return createEnumOrFieldLiteral( namedClass ); - } - } - catch (ClassLoadingException | NoSuchFieldException ignore) { - } - - throw new HqlInterpretationException( "Unsure how to handle semantic path terminal - " + fullPath ); - }; - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private SqmExpression createEnumOrFieldLiteral(Class namedClass) throws NoSuchFieldException { - if ( namedClass.isEnum() ) { - return new SqmEnumLiteral( - Enum.valueOf( namedClass, getLocalName() ), - (EnumJavaType) - javaTypeRegistry() - .resolveDescriptor( namedClass, () -> new EnumJavaType( namedClass ) ), - getLocalName(), - nodeBuilder() - ); - } - else { - return new SqmFieldLiteral( - namedClass.getField( getLocalName() ), - javaTypeRegistry() - .resolveDescriptor( namedClass, () -> new EnumJavaType( namedClass ) ), - nodeBuilder() - ); - } - } - - private JavaTypeRegistry javaTypeRegistry() { - return creationState.getCreationContext().getTypeConfiguration().getJavaTypeRegistry(); - } - - @Override - public @Nullable SqmBindableType getNodeType() { - return expressibleType; - } - - @Override - public X accept(SemanticQueryWalker walker) { - return (X) handler.apply( walker ); - } - - @Override - public JavaType getJavaTypeDescriptor() { - return expressibleType == null ? null : expressibleType.getExpressibleJavaType(); - } - - - @Override - public void applyInferableType(@Nullable SqmBindableType type) { - } - - @Override - public void appendHqlString(StringBuilder hql, SqmRenderContext context) { - hql.append( getParent().getFullPath() ); - hql.append( '.' ); - hql.append( getLocalName() ); - } - - @Override - public SqmExpression asLong() { - return null; - } - - @Override - public SqmExpression asInteger() { - return null; - } - - @Override - public SqmExpression asFloat() { - return null; - } - - @Override - public SqmExpression asDouble() { - return null; - } - - @Override - public SqmExpression asBigDecimal() { - return null; - } - - @Override - public SqmExpression asBigInteger() { - return null; - } - - @Override - public SqmExpression asString() { - return null; - } - - @Override - public SqmExpression as(Class type) { - return null; - } - - @Override - public SqmPredicate isNull() { - return null; - } - - @Override - public SqmPredicate isNotNull() { - return null; - } - - @Override - public SqmPredicate equalTo(Expression that) { - return null; - } - - @Override - public SqmPredicate equalTo(Object that) { - return null; - } - - @Override - public SqmPredicate notEqualTo(Expression value) { - return null; - } - - @Override - public SqmPredicate notEqualTo(Object value) { - return null; - } - - @Override - public SqmExpression cast(Class type) { - return null; - } - - @Override - public SqmPredicate in(Object... values) { - return null; - } - - @Override - public SqmPredicate in(Expression[] values) { - return null; - } - - @Override - public SqmPredicate in(Collection values) { - return null; - } - - @Override - public SqmPredicate in(Expression values) { - return null; - } - - @Override - public List> getSelectionItems() { - return null; - } - - @Override - public JpaSelection alias(String name) { - return null; - } - - @Override - public boolean isCompoundSelection() { - return false; - } - - @Override - public String getAlias() { - return null; - } - - @Override - public NodeBuilder nodeBuilder() { - return null; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterIdentifiedImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterIdentifiedImpl.java new file mode 100644 index 000000000000..e3bf419de9eb --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterIdentifiedImpl.java @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.query.internal; + +import org.hibernate.query.named.NamedQueryMemento; +import org.hibernate.query.spi.AbstractQueryParameter; +import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; +import org.hibernate.type.BindableType; + + +/** + * QueryParameter impl for unnamed JPA Criteria-parameters. + */ +public class QueryParameterIdentifiedImpl extends AbstractQueryParameter { + /** + * Create an identified parameter descriptor from the SQM parameter + * + * @param parameter The source parameter info + * + * @return The parameter descriptor + */ + public static QueryParameterIdentifiedImpl fromSqm(SqmJpaCriteriaParameterWrapper parameter) { + assert parameter.getName() == null; + assert parameter.getPosition() == null; + return new QueryParameterIdentifiedImpl<>( + parameter.getUnnamedParameterId(), + parameter.allowMultiValuedBinding(), + parameter.getAnticipatedType() + ); + } + + private final int unnamedParameterId; + + private QueryParameterIdentifiedImpl(int unnamedParameterId, boolean allowMultiValuedBinding, BindableType anticipatedType) { + super( allowMultiValuedBinding, anticipatedType ); + this.unnamedParameterId = unnamedParameterId; + } + + public int getUnnamedParameterId() { + return unnamedParameterId; + } + + @Override + public NamedQueryMemento.ParameterMemento toMemento() { + return session -> new QueryParameterIdentifiedImpl<>( unnamedParameterId, allowsMultiValuedBinding(), getHibernateType() ); + } + + @Override + public boolean equals(Object o) { + return this == o + || o instanceof QueryParameterIdentifiedImpl that + && unnamedParameterId == that.unnamedParameterId; + } + + @Override + public int hashCode() { + return unnamedParameterId; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java index e58d63e7b6b6..47c2834c8cf7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java @@ -722,7 +722,7 @@ else if ( parameter.getPosition() != null ) { protected

QueryParameterBinding

locateBinding(QueryParameterImplementor

parameter) { getCheckOpen(); - return getQueryParameterBindings().getBinding( parameter ); + return getQueryParameterBindings().getBinding( getQueryParameter( parameter ) ); } protected

QueryParameterBinding

locateBinding(String name) { @@ -735,10 +735,14 @@ protected

QueryParameterBinding

locateBinding(int position) { return getQueryParameterBindings().getBinding( position ); } + protected

QueryParameterImplementor

getQueryParameter(QueryParameterImplementor

parameter) { + return parameter; + } + public boolean isBound(Parameter param) { getCheckOpen(); final var parameter = getParameterMetadata().resolve( param ); - return parameter != null && getQueryParameterBindings().isBound( parameter ); + return parameter != null && getQueryParameterBindings().isBound( getQueryParameter( parameter ) ); } public T getParameterValue(Parameter param) { @@ -749,7 +753,7 @@ public T getParameterValue(Parameter param) { throw new IllegalArgumentException( "The parameter [" + param + "] is not part of this Query" ); } - final var binding = getQueryParameterBindings().getBinding( parameter ); + final var binding = getQueryParameterBindings().getBinding( getQueryParameter( parameter ) ); if ( binding == null || !binding.isBound() ) { throw new IllegalStateException( "Parameter value not yet bound : " + param.toString() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQueryParameter.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQueryParameter.java index d9ed101570bc..b19cead4457f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQueryParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQueryParameter.java @@ -33,7 +33,7 @@ public AbstractQueryParameter(boolean allowMultiValuedBinding, BindableType a @Override public void disallowMultiValuedBinding() { QUERY_MESSAGE_LOGGER.debugf( "QueryParameter#disallowMultiValuedBinding() called: %s", this ); - this.allowMultiValuedBinding = true; + this.allowMultiValuedBinding = false; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java index d6d7479150ce..7bbae586ecdd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java @@ -136,4 +136,5 @@ public QueryKey.ParameterBindingsMemento generateQueryKeyMemento(SharedSessionCo static QueryParameterBindings empty() { return QueryParameterBindingsImpl.EMPTY; } + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java index 366bc0e1c5b7..0465d242a535 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java @@ -13,6 +13,7 @@ import org.hibernate.query.sqm.produce.function.ArgumentsValidator; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; @@ -127,15 +128,9 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object o) { - if ( o == null || getClass() != o.getClass() ) { - return false; - } - if ( !super.equals( o ) ) { - return false; - } - - SelfRenderingSqmAggregateFunction that = (SelfRenderingSqmAggregateFunction) o; - return Objects.equals( filter, that.filter ); + return super.equals( o ) + && o instanceof SelfRenderingSqmAggregateFunction that + && Objects.equals( filter, that.filter ); } @Override @@ -144,4 +139,18 @@ public int hashCode() { result = 31 * result + Objects.hashCode( filter ); return result; } + + @Override + public boolean isCompatible(Object o) { + return super.isCompatible( o ) + && o instanceof SelfRenderingSqmAggregateFunction that + && SqmCacheable.areCompatible( filter, that.filter ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( filter ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmOrderedSetAggregateFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmOrderedSetAggregateFunction.java index a32b1f3c944e..0853d952cc82 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmOrderedSetAggregateFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmOrderedSetAggregateFunction.java @@ -14,6 +14,7 @@ import org.hibernate.query.sqm.produce.function.ArgumentsValidator; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; @@ -177,15 +178,9 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object o) { - if ( o == null || getClass() != o.getClass() ) { - return false; - } - if ( !super.equals( o ) ) { - return false; - } - - SelfRenderingSqmOrderedSetAggregateFunction that = (SelfRenderingSqmOrderedSetAggregateFunction) o; - return Objects.equals( withinGroup, that.withinGroup ); + return super.equals( o ) + && o instanceof SelfRenderingSqmOrderedSetAggregateFunction that + && Objects.equals( withinGroup, that.withinGroup ); } @Override @@ -194,4 +189,18 @@ public int hashCode() { result = 31 * result + Objects.hashCode( withinGroup ); return result; } + + @Override + public boolean isCompatible(Object o) { + return super.isCompatible( o ) + && o instanceof SelfRenderingSqmOrderedSetAggregateFunction that + && SqmCacheable.areCompatible( withinGroup, that.withinGroup ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( withinGroup ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmWindowFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmWindowFunction.java index 04dc8bdc62af..242fa87c2e7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmWindowFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmWindowFunction.java @@ -13,6 +13,7 @@ import org.hibernate.query.sqm.produce.function.ArgumentsValidator; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; @@ -161,15 +162,9 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object o) { - if ( o == null || getClass() != o.getClass() ) { - return false; - } - if ( !super.equals( o ) ) { - return false; - } - - SelfRenderingSqmWindowFunction that = (SelfRenderingSqmWindowFunction) o; - return Objects.equals( filter, that.filter ) + return super.equals( o ) + && o instanceof SelfRenderingSqmWindowFunction that + && Objects.equals( filter, that.filter ) && Objects.equals( respectNulls, that.respectNulls ) && Objects.equals( fromFirst, that.fromFirst ); } @@ -182,4 +177,22 @@ public int hashCode() { result = 31 * result + Objects.hashCode( fromFirst ); return result; } + + @Override + public boolean isCompatible(Object o) { + return super.isCompatible( o ) + && o instanceof SelfRenderingSqmWindowFunction that + && SqmCacheable.areCompatible( filter, that.filter ) + && Objects.equals( respectNulls, that.respectNulls ) + && Objects.equals( fromFirst, that.fromFirst ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + Objects.hashCode( filter ); + result = 31 * result + Objects.hashCode( respectNulls ); + result = 31 * result + Objects.hashCode( fromFirst ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java index c096c00d0f67..9f97c9e15889 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java @@ -4,8 +4,11 @@ */ package org.hibernate.query.sqm.internal; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.query.spi.QueryParameterImplementor; +import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; import org.hibernate.type.BindableType; import org.hibernate.query.KeyedPage; import org.hibernate.query.KeyedResultList; @@ -135,24 +138,61 @@ else if ( bindType != null ) { } ); // Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here - for ( var sqmParameter : getDomainParameterXref().getParameterResolutions().getSqmParameters() ) { + bindValueBindCriteriaParameters( getDomainParameterXref(), parameterBindings ); + } + + protected static void bindValueBindCriteriaParameters( + DomainParameterXref domainParameterXref, + QueryParameterBindings bindings) { + for ( var entry : domainParameterXref.getQueryParameters().entrySet() ) { + final var sqmParameter = entry.getValue().get( 0 ); if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper wrapper ) { - bindCriteriaParameter( wrapper ); + @SuppressWarnings("unchecked") + final var criteriaParameter = (JpaCriteriaParameter) wrapper.getJpaCriteriaParameter(); + final var value = criteriaParameter.getValue(); + // We don't set a null value, unless the type is also null which + // is the case when using HibernateCriteriaBuilder.value + if ( value != null || criteriaParameter.getNodeType() == null ) { + // Use the anticipated type for binding the value if possible + //noinspection unchecked + final var parameter = (QueryParameterImplementor) entry.getKey(); + bindings.getBinding( parameter ) + .setBindValue( value, criteriaParameter.getAnticipatedType() ); + } } } } - protected void bindCriteriaParameter(SqmJpaCriteriaParameterWrapper sqmParameter) { - final var criteriaParameter = sqmParameter.getJpaCriteriaParameter(); - final T value = criteriaParameter.getValue(); - // We don't set a null value, unless the type is also null which - // is the case when using HibernateCriteriaBuilder.value - if ( value != null || criteriaParameter.getNodeType() == null ) { - // Use the anticipated type for binding the value if possible - getQueryParameterBindings() - .getBinding( criteriaParameter ) - .setBindValue( value, criteriaParameter.getAnticipatedType() ); + @Override + protected

QueryParameterImplementor

getQueryParameter(QueryParameterImplementor

parameter) { + if ( parameter instanceof JpaCriteriaParameter criteriaParameter ) { + final var parameterWrapper = getDomainParameterXref().getParameterResolutions() + .getJpaCriteriaParamResolutions() + .get( criteriaParameter ); + //noinspection unchecked + return (QueryParameterImplementor

) getDomainParameterXref().getQueryParameter( parameterWrapper ); + } + else { + return parameter; + } + } + + public int @Nullable [] unnamedParameterIndices() { + final var domainParameterXref = getDomainParameterXref(); + final var jpaCriteriaParamResolutions = + domainParameterXref.getParameterResolutions().getJpaCriteriaParamResolutions(); + if ( jpaCriteriaParamResolutions.isEmpty() ) { + return null; + } + int maxId = 0; + for ( var criteriaWrapper : jpaCriteriaParamResolutions.values() ) { + maxId = Math.max( maxId, criteriaWrapper.getCriteriaParameterId() ); + } + final var unnamedParameterIndices = new int[maxId + 1]; + for ( var entry : jpaCriteriaParamResolutions.entrySet() ) { + unnamedParameterIndices[entry.getValue().getCriteriaParameterId()] = entry.getValue().getUnnamedParameterId(); } + return unnamedParameterIndices; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java index dc5c8a1868d9..39395cd63da8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java @@ -9,12 +9,11 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.TreeMap; +import org.hibernate.query.internal.QueryParameterIdentifiedImpl; import org.hibernate.query.internal.QueryParameterNamedImpl; import org.hibernate.query.internal.QueryParameterPositionalImpl; import org.hibernate.query.spi.QueryParameterImplementor; -import org.hibernate.query.sqm.SqmTreeTransformationLogger; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; @@ -60,48 +59,26 @@ public static DomainParameterXref from(SqmStatement sqmStatement) { ); } - // `xrefMap` is used to help maintain the proper cardinality between an - // SqmParameter and a QueryParameter. Multiple SqmParameter references - // can map to the same QueryParameter. Consider, e.g., - // `.. where a.b = :param or a.c = :param`. Here we have 2 SqmParameter - // references (one for each occurrence of `:param`) both of which map to - // the same QueryParameter. - final Map, QueryParameterImplementor> xrefMap = new TreeMap<>(); - - final QueryParameterImplementor queryParameter = xrefMap.computeIfAbsent( - sqmParameter, - parameter -> { - if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper sqmJpaCriteriaParameterWrapper ) { - return sqmJpaCriteriaParameterWrapper.getJpaCriteriaParameter(); - } - else if ( sqmParameter.getName() != null ) { - return QueryParameterNamedImpl.fromSqm( sqmParameter ); - } - else if ( sqmParameter.getPosition() != null ) { - return QueryParameterPositionalImpl.fromSqm( sqmParameter ); - } - else { - throw new UnsupportedOperationException( - "Unexpected SqmParameter type : " + sqmParameter ); - } - } - ); - - if ( !sqmParameter.allowMultiValuedBinding() ) { - if ( queryParameter.allowsMultiValuedBinding() ) { - SqmTreeTransformationLogger.LOGGER.debugf( - "SqmParameter [%s] does not allow multi-valued binding, " + - "but mapped to existing QueryParameter [%s] that does - " + - "disallowing multi-valued binding", - sqmParameter, - queryParameter - ); - queryParameter.disallowMultiValuedBinding(); - } + final QueryParameterImplementor queryParameter; + if ( sqmParameter.getName() != null ) { + queryParameter = QueryParameterNamedImpl.fromSqm( sqmParameter ); + } + else if ( sqmParameter.getPosition() != null ) { + queryParameter = QueryParameterPositionalImpl.fromSqm( sqmParameter ); } - else if ( sqmParameter.getExpressible() != null + else if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper criteriaParameter ) { + if ( sqmParameter.allowMultiValuedBinding() + && sqmParameter.getExpressible() != null && sqmParameter.getExpressible().getSqmType() instanceof BasicCollectionType ) { - queryParameter.disallowMultiValuedBinding(); + // The wrapper parameter was inferred to be of a basic collection type, + // so we disallow multivalued bindings, because binding a list of collections isn't useful + criteriaParameter.getJpaCriteriaParameter().disallowMultiValuedBinding(); + } + queryParameter = QueryParameterIdentifiedImpl.fromSqm( criteriaParameter ); + } + else { + throw new UnsupportedOperationException( + "Unexpected SqmParameter type : " + sqmParameter ); } sqmParamsByQueryParam.computeIfAbsent( queryParameter, impl -> new ArrayList<>() ).add( sqmParameter ); @@ -178,10 +155,7 @@ public List> getSqmParameters(QueryParameterImplementor query } public QueryParameterImplementor getQueryParameter(SqmParameter sqmParameter) { - if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper parameterWrapper ) { - return parameterWrapper.getJpaCriteriaParameter(); - } - else if ( sqmParameter instanceof QueryParameterImplementor parameterImplementor ) { + if ( sqmParameter instanceof QueryParameterImplementor parameterImplementor ) { return parameterImplementor; } else { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmInterpretationsKey.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmInterpretationsKey.java index f48e2875662d..c392da27216f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmInterpretationsKey.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmInterpretationsKey.java @@ -4,13 +4,16 @@ */ package org.hibernate.query.sqm.internal; +import java.util.Arrays; import java.util.Collection; import java.util.Objects; import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.LockOptions; import org.hibernate.query.spi.QueryInterpretationCache; import org.hibernate.query.sqm.spi.InterpretationsKeySource; +import org.hibernate.query.sqm.tree.SqmStatement; /** @@ -21,9 +24,11 @@ public final class SqmInterpretationsKey implements QueryInterpretationCache.Key public static SqmInterpretationsKey createInterpretationsKey(InterpretationsKeySource keySource) { if ( isCacheable ( keySource ) ) { final Object query = keySource.getQueryStringCacheKey(); + final int hashCode = query instanceof SqmStatement statement ? statement.cacheHashCode() : query.hashCode(); return new SqmInterpretationsKey( query, - query.hashCode(), + keySource.unnamedParameterIndices(), + hashCode, keySource.getResultType(), keySource.getQueryOptions().getLockOptions(), memoryEfficientDefensiveSetCopy( keySource.getLoadQueryInfluencers().getEnabledFetchProfileNames() ) @@ -80,6 +85,7 @@ public static QueryInterpretationCache.Key generateNonSelectKey(InterpretationsK } private final Object query; + private final int @Nullable [] unnamedParameterIndices; private final Class resultType; private final LockOptions lockOptions; private final Collection enabledFetchProfiles; @@ -87,11 +93,14 @@ public static QueryInterpretationCache.Key generateNonSelectKey(InterpretationsK private SqmInterpretationsKey( Object query, + int @Nullable [] unnamedParameterIndices, int hash, Class resultType, LockOptions lockOptions, Collection enabledFetchProfiles) { + assert query.getClass() == String.class || query instanceof SqmStatement; this.query = query; + this.unnamedParameterIndices = unnamedParameterIndices; this.hashCode = hash; this.resultType = resultType; this.lockOptions = lockOptions; @@ -102,6 +111,7 @@ private SqmInterpretationsKey( public QueryInterpretationCache.Key prepareForStore() { return new SqmInterpretationsKey( query, + unnamedParameterIndices, hashCode, resultType, // Since lock options might be mutable, we need a copy for the cache key @@ -124,7 +134,10 @@ public boolean equals(Object other) { return false; } return this.hashCode == that.hashCode //check this first as some other checks are expensive - && this.query.equals( that.query ) + && ( query.getClass() == String.class + ? query.equals( that.query ) + : ((SqmStatement) query).isCompatible( that.query ) ) + && Arrays.equals( this.unnamedParameterIndices, that.unnamedParameterIndices ) && Objects.equals( this.resultType, that.resultType ) && Objects.equals( this.lockOptions, that.lockOptions ) && Objects.equals( this.enabledFetchProfiles, that.enabledFetchProfiles ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmQueryImpl.java index 659625ca36ff..6c93e3fca5e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmQueryImpl.java @@ -60,7 +60,6 @@ import org.hibernate.query.sqm.tree.AbstractSqmDmlStatement; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; -import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; @@ -217,11 +216,7 @@ public SqmQueryImpl( parameterBindings = parameterMetadata.createBindings( session.getFactory() ); // Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here - for ( var sqmParameter : domainParameterXref.getParameterResolutions().getSqmParameters() ) { - if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper wrapper ) { - bindCriteriaParameter( wrapper ); - } - } + bindValueBindCriteriaParameters( domainParameterXref, parameterBindings ); validateQuery( expectedResultType, sqm, hql ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java index e53339b892c4..073421682a0b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java @@ -54,7 +54,6 @@ import org.hibernate.query.sqm.SqmSelectionQuery; import org.hibernate.query.sqm.spi.InterpretationsKeySource; import org.hibernate.query.sqm.spi.SqmSelectionQueryImplementor; -import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.sql.results.internal.TupleMetadata; import org.hibernate.sql.results.spi.ResultsConsumer; @@ -188,11 +187,7 @@ public SqmSelectionQueryImpl( parameterBindings = parameterMetadata.createBindings( session.getFactory() ); // Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here - for ( var sqmParameter : domainParameterXref.getParameterResolutions().getSqmParameters() ) { - if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper wrapper ) { - bindCriteriaParameter( wrapper ); - } - } + bindValueBindCriteriaParameters( domainParameterXref, parameterBindings ); resultType = determineResultType( sqm, expectedResultType ); @@ -249,11 +244,7 @@ SqmSelectionQueryImpl(AbstractSqmSelectionQuery original, KeyedPage ke original.getQueryParameterBindings().visitBindings( this::setBindValues ); // Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here - for ( var sqmParameter : domainParameterXref.getParameterResolutions().getSqmParameters() ) { - if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper parameterWrapper ) { - bindCriteriaParameter( parameterWrapper ); - } - } + bindValueBindCriteriaParameters( domainParameterXref, parameterBindings ); //noinspection unchecked expectedResultType = (Class) KeyedResult.class; 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 77a923be89a3..c34dd2295ab1 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 @@ -518,18 +518,20 @@ public static JdbcParameterBindings createJdbcParameterBindings( SharedSessionContractImplementor session) { final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( domainParameterXref.getSqmParameterCount() ); - domainParameterXref.getQueryParameters() - .forEach( (queryParameter, sqmParameters) -> - createJdbcParameterBinding( - domainParamBindings, - domainParameterXref, - jdbcParamXref, - mappingModelResolutionAccess, - session, - queryParameter, - sqmParameters, - jdbcParameterBindings - ) ); + for ( var entry : domainParameterXref.getQueryParameters().entrySet() ) { + var queryParameter = entry.getKey(); + var sqmParameters = entry.getValue(); + createJdbcParameterBinding( + domainParamBindings, + domainParameterXref, + jdbcParamXref, + mappingModelResolutionAccess, + session, + queryParameter, + sqmParameters, + jdbcParameterBindings + ); + } return jdbcParameterBindings; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/CacheabilityInfluencers.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/CacheabilityInfluencers.java index abf94f12b60f..81d2ac34a634 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/CacheabilityInfluencers.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/CacheabilityInfluencers.java @@ -4,6 +4,7 @@ */ package org.hibernate.query.sqm.spi; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.tree.SqmStatement; @@ -18,6 +19,8 @@ public interface CacheabilityInfluencers { Object getQueryStringCacheKey(); + int @Nullable [] unnamedParameterIndices(); + SqmStatement getSqmStatement(); QueryOptions getQueryOptions(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 9f39827f9782..0b298949f0f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -5914,8 +5914,8 @@ public MappingModelExpressible determineValueMapping(SqmExpression sqmExpr } private MappingModelExpressible determineValueMapping(SqmExpression sqmExpression, FromClauseIndex fromClauseIndex) { - if ( sqmExpression instanceof SqmParameter ) { - return determineValueMapping( (SqmParameter) sqmExpression ); + if ( sqmExpression instanceof SqmParameter ) { + return determineValueMapping( getSqmParameter( sqmExpression ) ); } if ( sqmExpression instanceof SqmPath ) { @@ -8153,10 +8153,11 @@ private InListPredicate processInSingleCriteriaParameter( SqmInListPredicate sqmPredicate, JpaCriteriaParameter jpaCriteriaParameter) { assert jpaCriteriaParameter.allowsMultiValuedBinding(); - final QueryParameterBinding domainParamBinding = domainParameterBindings.getBinding( jpaCriteriaParameter ); + final SqmJpaCriteriaParameterWrapper sqmWrapper = jpaCriteriaParamResolutions.get( jpaCriteriaParameter ); + final QueryParameterImplementor domainParam = domainParameterXref.getQueryParameter( sqmWrapper ); + final QueryParameterBinding domainParamBinding = domainParameterBindings.getBinding( domainParam ); if ( domainParamBinding.isMultiValued() ) { - final SqmJpaCriteriaParameterWrapper sqmWrapper = jpaCriteriaParamResolutions.get( jpaCriteriaParameter ); - return processInSingleParameter( sqmPredicate, sqmWrapper, jpaCriteriaParameter, domainParamBinding ); + return processInSingleParameter( sqmPredicate, sqmWrapper, domainParam, domainParamBinding ); } else { return null; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java index 79e31cf55f13..41571f318d60 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.function.Function; @@ -64,7 +65,7 @@ protected Map> copyCteStatements(SqmCopyContext conte protected void putAllCtes(SqmCteContainer cteContainer) { for ( SqmCteStatement cteStatement : cteContainer.getCteStatements() ) { - if ( cteStatements.putIfAbsent( cteStatement.getName(), cteStatement ) != null ) { + if ( cteStatements.putIfAbsent( cteStatement.getCteTable().getCteName(), cteStatement ) != null ) { throw new IllegalArgumentException( "A CTE with the label " + cteStatement.getCteTable().getCteName() + " already exists" ); } } @@ -205,4 +206,34 @@ protected void appendHqlCteString(StringBuilder sb, SqmRenderContext context) { sb.setLength( sb.length() - 2 ); } } + + @Override + public boolean equals(Object object) { + return object instanceof AbstractSqmDmlStatement that + && getClass() == that.getClass() + && getTarget().equals( that.getTarget() ) + && Objects.equals( cteStatements, that.cteStatements ); + } + + @Override + public int hashCode() { + int result = getTarget().hashCode(); + result = 31 * result + Objects.hashCode( cteStatements ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof AbstractSqmDmlStatement that + && getClass() == that.getClass() + && getTarget().isCompatible( that.getTarget() ) + && SqmCacheable.areCompatible( cteStatements, that.cteStatements ); + } + + @Override + public int cacheHashCode() { + int result = getTarget().cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( cteStatements ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmRestrictedDmlStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmRestrictedDmlStatement.java index 7a8a15f18a6b..de947cc22d4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmRestrictedDmlStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmRestrictedDmlStatement.java @@ -5,6 +5,7 @@ package org.hibernate.query.sqm.tree; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.hibernate.metamodel.model.domain.EntityDomainType; @@ -135,4 +136,32 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { whereClause.getPredicate().appendHqlString( hql, context ); } } + + @Override + public boolean equals(Object object) { + return object instanceof AbstractSqmRestrictedDmlStatement that + && super.equals( object ) + && Objects.equals( getWhereClause(), that.getWhereClause() ); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + Objects.hashCode( getWhereClause() ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof AbstractSqmRestrictedDmlStatement that + && super.isCompatible( object ) + && SqmCacheable.areCompatible( getWhereClause(), that.getWhereClause() ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( getWhereClause() ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java index 0e120199148a..516723e5ca06 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java @@ -4,7 +4,7 @@ */ package org.hibernate.query.sqm.tree; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import org.hibernate.query.sqm.NodeBuilder; @@ -47,7 +47,7 @@ protected Set> copyParameters(SqmCopyContext context) { return null; } else { - final Set> parameters = new HashSet<>( this.parameters.size() ); + final Set> parameters = new LinkedHashSet<>( this.parameters.size() ); for ( SqmParameter parameter : this.parameters ) { parameters.add( parameter.copy( context ) ); } @@ -63,7 +63,7 @@ public SqmQuerySource getQuerySource() { @Override public void addParameter(SqmParameter parameter) { if ( parameters == null ) { - parameters = new HashSet<>(); + parameters = new LinkedHashSet<>(); } parameters.add( parameter ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmCacheable.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmCacheable.java new file mode 100644 index 000000000000..a1ebe8479897 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmCacheable.java @@ -0,0 +1,107 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.query.sqm.tree; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Base contract for any SQM AST node caching. + */ +public interface SqmCacheable { + + boolean isCompatible(Object object); + + int cacheHashCode(); + + static boolean areCompatible(@Nullable SqmCacheable e1, @Nullable SqmCacheable e2) { + return e1 == null ? e2 == null : e1.isCompatible( e2 ); + } + + static boolean areCompatible(@Nullable Collection collection1, @Nullable Collection collection2) { + if ( collection1 == null ) { + return collection2 == null; + } + if ( collection2 != null ) { + if ( collection1.size() == collection2.size() ) { + OUTER: for ( SqmCacheable node1 : collection1 ) { + for ( SqmCacheable node2 : collection2 ) { + if ( node1.isCompatible( node2 ) ) { + continue OUTER; + } + } + return false; + } + return true; + } + } + return false; + } + + static boolean areCompatible(@Nullable List collection1, @Nullable List collection2) { + if ( collection1 == null ) { + return collection2 == null; + } + if ( collection2 != null ) { + final int size = collection1.size(); + if ( size == collection2.size() ) { + for ( int i = 0; i < size; i++ ) { + if ( !collection1.get( i ).isCompatible( collection2.get( i ) ) ) { + return false; + } + } + return true; + } + } + return false; + } + + static boolean areCompatible(@Nullable Map collection1, @Nullable Map collection2) { + if ( collection1 == null ) { + return collection2 == null; + } + if ( collection2 != null ) { + final int size = collection1.size(); + if ( size == collection2.size() ) { + for ( Map.Entry entry : collection1.entrySet() ) { + if ( !entry.getValue().isCompatible( collection2.get( entry.getKey() ) ) ) { + return false; + } + } + return true; + } + } + return false; + } + + static int cacheHashCode(@Nullable SqmCacheable e1) { + return e1 == null ? 0 : e1.cacheHashCode(); + } + + static int cacheHashCode(@Nullable Collection collection) { + if ( collection == null ) { + return 0; + } + int result = 1; + for ( SqmCacheable node : collection ) { + result = 31 * result + node.cacheHashCode(); + } + return result; + } + + static int cacheHashCode(@Nullable Map map) { + if ( map == null ) { + return 0; + } + int result = 0; + for ( Map.Entry entry : map.entrySet() ) { + result = entry.getKey().hashCode() ^ entry.getValue().cacheHashCode(); + } + return result; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmNode.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmNode.java index c5fb9c1ffc7e..698a8297cb77 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmNode.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmNode.java @@ -14,7 +14,7 @@ * * @author Steve Ebersole */ -public interface SqmNode extends JpaCriteriaNode { +public interface SqmNode extends JpaCriteriaNode, SqmCacheable { Logger LOG = Logger.getLogger( SqmNode.class ); default String asLoggableText() { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java index c062545773fd..639eaf88063a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java @@ -15,6 +15,7 @@ import org.hibernate.query.criteria.JpaCteCriteriaType; import org.hibernate.query.criteria.JpaSearchOrder; import org.hibernate.query.SortDirection; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmLiteral; @@ -42,9 +43,9 @@ public class SqmCteStatement extends AbstractSqmNode implements SqmVisitableN private SqmSelectQuery cteDefinition; private CteMaterialization materialization; private CteSearchClauseKind searchClauseKind; - private List searchBySpecifications; + private List searchBySpecifications; private String searchAttributeName; - private List cycleAttributes; + private List cycleAttributes; private String cycleMarkAttributeName; private String cyclePathAttributeName; private SqmLiteral cycleValue; @@ -115,9 +116,9 @@ private SqmCteStatement( SqmSelectQuery cteDefinition, CteMaterialization materialization, CteSearchClauseKind searchClauseKind, - List searchBySpecifications, + List searchBySpecifications, String searchAttributeName, - List cycleAttributes, + List cycleAttributes, String cycleMarkAttributeName, String cyclePathAttributeName, SqmLiteral cycleValue, @@ -203,7 +204,8 @@ public CteSearchClauseKind getSearchClauseKind() { @Override public List getSearchBySpecifications() { - return searchBySpecifications; + //noinspection unchecked + return (List) (List) searchBySpecifications; } @Override @@ -213,7 +215,8 @@ public String getSearchAttributeName() { @Override public List getCycleAttributes() { - return cycleAttributes; + //noinspection unchecked + return (List) (List) cycleAttributes; } @Override @@ -270,7 +273,8 @@ public void search(CteSearchClauseKind kind, String searchAttributeName, List) (List) orders; } } @@ -297,7 +301,7 @@ public void cycleUsing( if ( cycleValueLiteral.getNodeType() != noCycleValueLiteral.getNodeType() ) { throw new IllegalArgumentException( "Inconsistent types for cycle mark values: [" + cycleValueLiteral.getNodeType() + ", " + noCycleValueLiteral.getNodeType() + "]" ); } - final List attributes = new ArrayList<>( cycleAttributes.size() ); + final List attributes = new ArrayList<>( cycleAttributes.size() ); for ( JpaCteCriteriaAttribute cycleAttribute : cycleAttributes ) { if ( !cteTable.getAttributes().contains( cycleAttribute ) ) { throw new IllegalArgumentException( @@ -306,7 +310,7 @@ public void cycleUsing( "' passed, which is not part of the JpaCteCriteria!" ); } - attributes.add( cycleAttribute ); + attributes.add( (SqmCteTableColumn) cycleAttribute ); } this.cycleMarkAttributeName = cycleMarkAttributeName; this.cyclePathAttributeName = cyclePathAttributeName; @@ -409,8 +413,8 @@ else if ( getCteDefinition() instanceof SqmSelectStatement selectStatement ) @Override public boolean equals(Object object) { return object instanceof SqmCteStatement that - && Objects.equals( cteTable, that.cteTable ) - && Objects.equals( cteDefinition, that.cteDefinition ) + && cteTable.equals( that.cteTable ) + && cteDefinition.equals( that.cteDefinition ) && materialization == that.materialization && searchClauseKind == that.searchClauseKind && Objects.equals( searchBySpecifications, that.searchBySpecifications ) @@ -424,8 +428,50 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hash( cteTable, cteDefinition, materialization, - searchClauseKind, searchBySpecifications, searchAttributeName, - cycleAttributes, cycleMarkAttributeName, cyclePathAttributeName, cycleValue, noCycleValue ); + int result = cteTable.hashCode(); + result = 31 * result + cteDefinition.hashCode(); + result = 31 * result + materialization.hashCode(); + result = 31 * result + Objects.hashCode( searchClauseKind ); + result = 31 * result + Objects.hashCode( searchBySpecifications ); + result = 31 * result + Objects.hashCode( searchAttributeName ); + result = 31 * result + Objects.hashCode( cycleAttributes ); + result = 31 * result + Objects.hashCode( cycleMarkAttributeName ); + result = 31 * result + Objects.hashCode( cyclePathAttributeName ); + result = 31 * result + Objects.hashCode( cycleValue ); + result = 31 * result + Objects.hashCode( noCycleValue ); + return result; } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmCteStatement that + && cteTable.isCompatible( that.cteTable ) + && cteDefinition.isCompatible( that.cteDefinition ) + && materialization == that.materialization + && searchClauseKind == that.searchClauseKind + && SqmCacheable.areCompatible( searchBySpecifications, that.searchBySpecifications ) + && Objects.equals( searchAttributeName, that.searchAttributeName ) + && SqmCacheable.areCompatible( cycleAttributes, that.cycleAttributes ) + && Objects.equals( cycleMarkAttributeName, that.cycleMarkAttributeName ) + && Objects.equals( cyclePathAttributeName, that.cyclePathAttributeName ) + && SqmCacheable.areCompatible( cycleValue, that.cycleValue ) + && SqmCacheable.areCompatible( noCycleValue, that.noCycleValue ); + } + + @Override + public int cacheHashCode() { + int result = cteTable.cacheHashCode(); + result = 31 * result + cteDefinition.cacheHashCode(); + result = 31 * result + materialization.hashCode(); + result = 31 * result + Objects.hashCode( searchClauseKind ); + result = 31 * result + SqmCacheable.cacheHashCode( searchBySpecifications ); + result = 31 * result + Objects.hashCode( searchAttributeName ); + result = 31 * result + SqmCacheable.cacheHashCode( cycleAttributes ); + result = 31 * result + Objects.hashCode( cycleMarkAttributeName ); + result = 31 * result + Objects.hashCode( cyclePathAttributeName ); + result = 31 * result + SqmCacheable.cacheHashCode( cycleValue ); + result = 31 * result + SqmCacheable.cacheHashCode( noCycleValue ); + return result; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java index fddbea66efd8..70407dccf970 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java @@ -6,12 +6,14 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import org.hibernate.metamodel.mapping.SqlTypedMapping; import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.query.criteria.JpaCteCriteriaAttribute; import org.hibernate.query.criteria.JpaCteCriteriaType; import org.hibernate.query.sqm.SqmBindableType; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tuple.internal.AnonymousTupleSimpleSqmPathSource; import org.hibernate.query.sqm.tuple.internal.AnonymousTupleType; import org.hibernate.query.sqm.tuple.internal.CteTupleTableGroupProducer; @@ -25,7 +27,7 @@ * @author Steve Ebersole * @author Christian Beikov */ -public class SqmCteTable extends AnonymousTupleType implements JpaCteCriteriaType { +public class SqmCteTable extends AnonymousTupleType implements JpaCteCriteriaType, SqmCacheable { private final String name; private final SqmCteStatement cteStatement; private final List columns; @@ -147,4 +149,32 @@ private BasicType determineRecursiveCteAttributeType(String name) { } return null; } + + @Override + public boolean equals(Object o) { + return o instanceof SqmCteTable that + && Objects.equals( name, that.name ) + && Objects.equals( columns, that.columns ); + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + Objects.hashCode( columns ); + return result; + } + + @Override + public boolean isCompatible(Object o) { + return o instanceof SqmCteTable that + && Objects.equals( name, that.name ) + && SqmCacheable.areCompatible( columns, that.columns ); + } + + @Override + public int cacheHashCode() { + int result = name.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( columns ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTableColumn.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTableColumn.java index 42ef85bf4fb6..23a47e07b3b7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTableColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTableColumn.java @@ -8,12 +8,13 @@ import org.hibernate.query.criteria.JpaCteCriteriaType; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.SqmExpressible; +import org.hibernate.query.sqm.tree.SqmCacheable; /** * @author Steve Ebersole * @author Christian Beikov */ -public class SqmCteTableColumn implements JpaCteCriteriaAttribute { +public class SqmCteTableColumn implements JpaCteCriteriaAttribute, SqmCacheable { private final SqmCteTable cteTable; private final String columnName; private final SqmBindableType typeExpressible; @@ -53,4 +54,28 @@ public String getName() { public Class getJavaType() { return typeExpressible == null ? null : typeExpressible.getJavaType(); } + + @Override + public boolean equals(Object o) { + return o instanceof SqmCteTableColumn that + && columnName.equals( that.columnName ) + && typeExpressible.equals( that.typeExpressible ); + } + + @Override + public int hashCode() { + int result = columnName.hashCode(); + result = 31 * result + typeExpressible.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object o) { + return equals( o ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmSearchClauseSpecification.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmSearchClauseSpecification.java index 06d1b01a27e6..80c80f2f235e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmSearchClauseSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmSearchClauseSpecification.java @@ -8,12 +8,15 @@ import org.hibernate.query.SortDirection; import org.hibernate.query.criteria.JpaCteCriteriaAttribute; import org.hibernate.query.criteria.JpaSearchOrder; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; +import java.util.Objects; + /** * @author Christian Beikov */ -public class SqmSearchClauseSpecification implements JpaSearchOrder { +public class SqmSearchClauseSpecification implements JpaSearchOrder, SqmCacheable { private final SqmCteTableColumn cteColumn; private final SortDirection sortOrder; private Nulls nullPrecedence; @@ -70,4 +73,36 @@ public SortDirection getSortOrder() { public Nulls getNullPrecedence() { return nullPrecedence; } + + @Override + public boolean equals(Object o) { + return o instanceof SqmSearchClauseSpecification that + && cteColumn.equals( that.cteColumn ) + && sortOrder == that.sortOrder + && nullPrecedence == that.nullPrecedence; + } + + @Override + public int hashCode() { + int result = cteColumn.hashCode(); + result = 31 * result + Objects.hashCode( sortOrder ); + result = 31 * result + Objects.hashCode( nullPrecedence ); + return result; + } + + @Override + public boolean isCompatible(Object o) { + return o instanceof SqmSearchClauseSpecification that + && cteColumn.isCompatible( that.cteColumn ) + && sortOrder == that.sortOrder + && nullPrecedence == that.nullPrecedence; + } + + @Override + public int cacheHashCode() { + int result = cteColumn.cacheHashCode(); + result = 31 * result + Objects.hashCode( sortOrder ); + result = 31 * result + Objects.hashCode( nullPrecedence ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java index 60425e7989f6..875686cd5bf7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java @@ -5,7 +5,6 @@ package org.hibernate.query.sqm.tree.delete; import java.util.Map; -import java.util.Objects; import java.util.Set; import org.hibernate.query.criteria.JpaCriteriaDelete; @@ -114,20 +113,6 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { super.appendHqlString( hql, context ); } - @Override - public boolean equals(Object node) { - return node instanceof SqmDeleteStatement that - && super.equals( node ) - && Objects.equals( this.getTarget(), that.getTarget() ) - && Objects.equals( this.getWhereClause(), that.getWhereClause() ) - && Objects.equals( this.getCteStatements(), that.getCteStatements() ); - } - - @Override - public int hashCode() { - return Objects.hash( super.hashCode(), getTarget(), getWhereClause(), getCteStatements() ); - } - @Override public Subquery subquery(EntityType type) { throw new UnsupportedOperationException( "DELETE query cannot be sub-query" ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmAttributeJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmAttributeJoin.java index b7a2da56d889..e5ec88934d3b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmAttributeJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmAttributeJoin.java @@ -21,7 +21,6 @@ import jakarta.persistence.criteria.JoinType; -import java.util.Objects; /** * Models a join based on a mapped attribute reference. @@ -154,16 +153,6 @@ public JoinType getJoinType() { @Override public abstract SqmTreatedAttributeJoin treatAs(EntityDomainType treatTarget, String alias, boolean fetched); - @Override - public boolean equals(Object object) { - return object instanceof AbstractSqmAttributeJoin that - && super.equals( object ) - && this.implicitJoin == that.implicitJoin - && this.fetchJoin == that.fetchJoin; - } - - @Override - public int hashCode() { - return Objects.hash( super.hashCode(), implicitJoin, fetchJoin ); - } + // No need for equals/hashCode or isCompatible/cacheHashCode, because the base implementation using NavigablePath + // is fine for the purpose of matching nodes "syntactically". Deep equality is determined through SqmFromClause } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java index 60ac51c91270..a35a04ab2d06 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java @@ -988,4 +988,7 @@ else if ( alias.startsWith( "var_" ) ) { } return prefix + "_" + (++aliasCounter); } + + // No need for equals/hashCode or isCompatible/cacheHashCode, because the base implementation using NavigablePath + // is fine for the purpose of matching nodes "syntactically". Deep equality is determined through SqmFromClause } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmJoin.java index 3d2765610b38..fba534d32477 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmJoin.java @@ -21,7 +21,6 @@ import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.Predicate; -import java.util.Objects; /** * @author Steve Ebersole @@ -154,24 +153,6 @@ public SqmEntityJoin join(Class targetEntityClass, SqmJoinType join return super.join( targetEntityClass, joinType ); } - @Override - public boolean equals(Object object) { - // Note that this implementation of equals() is only used for - // and is only correct when comparing use of AbstractSqmJoin - // within path expressions. See SqmFromClause.equalsJoins(). - return object instanceof AbstractSqmJoin - && super.equals( object ); - // We do not need to include these in the comparison because - // this is taken care of in SqmFromClause.equalsJoins(), which - // exists because including the onClausePredicate would result - // in a circularity when comparing AbstractSqmJoin in path - // expressions. -// && this.joinType == that.joinType -// && Objects.equals( this.onClausePredicate, that.onClausePredicate ); - } - - @Override - public int hashCode() { - return Objects.hash( super.hashCode(), joinType ); - } + // No need for equals/hashCode or isCompatible/cacheHashCode, because the base implementation using NavigablePath + // is fine for the purpose of matching nodes "syntactically". Deep equality is determined through SqmFromClause } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java index 35821767ff79..cc8311c724e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java @@ -340,21 +340,31 @@ public > SqmExpression get(MapAttribute) attribute ); } + // The equals/hashCode and isCompatible/cacheHashCode implementations are based on NavigablePath to match paths + // "syntactically" for regular uses in expressions and predicates. Deep equality is determined through SqmFromClause + @Override public boolean equals(Object object) { return object instanceof AbstractSqmPath that && this.getClass() == that.getClass() - && Objects.equals( this.navigablePath, that.navigablePath ) - && Objects.equals( this.getExplicitAlias(), that.getExplicitAlias() ); -// && Objects.equals( this.lhs, that.lhs ); + && Objects.equals( this.navigablePath, that.navigablePath ); } @Override public int hashCode() { - return Objects.hash( navigablePath, getExplicitAlias() ); -// return lhs == null ? 0 : lhs.hashCode(); -// return navigablePath.hashCode(); -// return Objects.hash( navigablePath, lhs ); + return navigablePath.hashCode(); + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof AbstractSqmPath that + && this.getClass() == that.getClass() + && Objects.equals( this.navigablePath, that.navigablePath ); + } + + @Override + public int cacheHashCode() { + return navigablePath.hashCode(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedBagJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedBagJoin.java index 9c1fec5c738f..19f6bd57d8f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedBagJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedBagJoin.java @@ -95,4 +95,26 @@ public SqmRoot getCorrelatedRoot() { return correlatedRootJoin; } + @Override + public boolean equals(Object other) { + return other instanceof SqmCorrelatedBagJoin that + && correlationParent.equals( that.correlationParent ); + } + + @Override + public int hashCode() { + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedBagJoin that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCrossJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCrossJoin.java index 8ad6bb7da289..1ec8f2ee686d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCrossJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCrossJoin.java @@ -98,4 +98,26 @@ public X accept(SemanticQueryWalker walker) { return walker.visitCorrelatedCrossJoin( this ); } + @Override + public boolean equals(Object other) { + return other instanceof SqmCorrelatedCrossJoin that + && correlationParent.equals( that.correlationParent ); + } + + @Override + public int hashCode() { + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedCrossJoin that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java index 3f8fea40eb6f..155de6498f85 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java @@ -95,4 +95,26 @@ public X accept(SemanticQueryWalker walker) { return walker.visitCorrelatedCteJoin( this ); } + @Override + public boolean equals(Object other) { + return other instanceof SqmCorrelatedCteJoin that + && correlationParent.equals( that.correlationParent ); + } + + @Override + public int hashCode() { + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedCteJoin that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java index a38c06c07b74..54df5791c52c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java @@ -100,4 +100,26 @@ public X accept(SemanticQueryWalker walker) { return walker.visitCorrelatedDerivedJoin( this ); } + @Override + public boolean equals(Object other) { + return other instanceof SqmCorrelatedDerivedJoin that + && correlationParent.equals( that.correlationParent ); + } + + @Override + public int hashCode() { + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedDerivedJoin that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedEntityJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedEntityJoin.java index e26ba5326748..fe32c69529c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedEntityJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedEntityJoin.java @@ -114,4 +114,25 @@ public SqmCorrelatedEntityJoin makeCopy(SqmCreationProcessingState creation ); } + @Override + public boolean equals(Object other) { + return other instanceof SqmCorrelatedEntityJoin that + && correlationParent.equals( that.correlationParent ); + } + + @Override + public int hashCode() { + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedEntityJoin that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedListJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedListJoin.java index 587be718ffb0..58728c95c9d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedListJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedListJoin.java @@ -94,4 +94,26 @@ public SqmRoot getCorrelatedRoot() { public X accept(SemanticQueryWalker walker) { return walker.visitCorrelatedListJoin( this ); } + + @Override + public boolean equals(Object other) { + return other instanceof SqmCorrelatedListJoin that + && correlationParent.equals( that.correlationParent ); + } + + @Override + public int hashCode() { + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedListJoin that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedMapJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedMapJoin.java index 53675376a5bc..64bcaba2f59e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedMapJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedMapJoin.java @@ -94,4 +94,26 @@ public SqmRoot getCorrelatedRoot() { public X accept(SemanticQueryWalker walker) { return walker.visitCorrelatedMapJoin( this ); } + + @Override + public boolean equals(Object other) { + return other instanceof SqmCorrelatedMapJoin that + && correlationParent.equals( that.correlationParent ); + } + + @Override + public int hashCode() { + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedMapJoin that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedPluralPartJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedPluralPartJoin.java index 7d5377e7972e..8e27271ea6b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedPluralPartJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedPluralPartJoin.java @@ -69,4 +69,26 @@ public SqmRoot getCorrelatedRoot() { return correlatedRootJoin; } + @Override + public boolean equals(Object other) { + return other instanceof SqmCorrelatedPluralPartJoin that + && correlationParent.equals( that.correlationParent ); + } + + @Override + public int hashCode() { + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedPluralPartJoin that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java index a909e476fb88..d8655e35add4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java @@ -12,8 +12,6 @@ import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.spi.NavigablePath; -import java.util.Objects; - /** * @author Steve Ebersole */ @@ -92,14 +90,24 @@ public X accept(SemanticQueryWalker walker) { } @Override - public boolean equals(Object object) { - return object instanceof SqmCorrelatedRoot that - && super.equals( object ) - && Objects.equals( this.correlationParent, that.correlationParent ); + public boolean equals(Object other) { + return other instanceof SqmCorrelatedRoot that + && correlationParent.equals( that.correlationParent ); } @Override public int hashCode() { - return Objects.hash( super.hashCode(), correlationParent ); + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedRoot that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSetJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSetJoin.java index a37ae637b2e5..95342c986966 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSetJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSetJoin.java @@ -94,4 +94,26 @@ public SqmRoot getCorrelatedRoot() { public X accept(SemanticQueryWalker walker) { return walker.visitCorrelatedSetJoin( this ); } + + @Override + public boolean equals(Object other) { + return other instanceof SqmCorrelatedSetJoin that + && correlationParent.equals( that.correlationParent ); + } + + @Override + public int hashCode() { + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedSetJoin that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSingularJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSingularJoin.java index a8bc78f1a293..29fac99108ad 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSingularJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSingularJoin.java @@ -94,4 +94,26 @@ public SqmRoot getCorrelatedRoot() { public X accept(SemanticQueryWalker walker) { return walker.visitCorrelatedSingularJoin( this ); } + + @Override + public boolean equals(Object other) { + return other instanceof SqmCorrelatedSingularJoin that + && correlationParent.equals( that.correlationParent ); + } + + @Override + public int hashCode() { + return correlationParent.hashCode(); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmCorrelatedSingularJoin that + && correlationParent.isCompatible( that.correlationParent ); + } + + @Override + public int cacheHashCode() { + return correlationParent.cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java index 8c19295d459e..d0ee91e5e036 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java @@ -104,13 +104,29 @@ public SqmCorrelatedRoot createCorrelation() { @Override public boolean equals(Object object) { - return object instanceof SqmCteRoot that - && super.equals( object ) - && Objects.equals( this.cte, that.cte ); + return super.equals( object ) + && object instanceof SqmCteRoot that + && Objects.equals( this.cte.getCteTable().getCteName(), that.cte.getCteTable().getCteName() ); } @Override public int hashCode() { - return Objects.hash( super.hashCode(), cte ); + int result = super.hashCode(); + result = 31 * result + cte.getCteTable().getCteName().hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return super.isCompatible( object ) + && object instanceof SqmCteRoot that + && Objects.equals( this.cte.getCteTable().getCteName(), that.cte.getCteTable().getCteName() ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + cte.getCteTable().getCteName().hashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java index e15005d21d6f..ca9e443a8275 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java @@ -17,7 +17,6 @@ import org.hibernate.query.sqm.tree.select.SqmSubQuery; import org.hibernate.spi.NavigablePath; -import java.util.Objects; /** * @author Christian Beikov @@ -128,13 +127,29 @@ public SqmTreatedRoot treatAs(EntityDomainType treatTarget, Str @Override public boolean equals(Object object) { - return object instanceof SqmDerivedRoot that - && super.equals( object ) - && Objects.equals( this.subQuery, that.subQuery ); + return super.equals( object ) + && object instanceof SqmDerivedRoot that + && subQuery.equals( that.subQuery ); } @Override public int hashCode() { - return Objects.hash( super.hashCode(), subQuery ); + int result = super.hashCode(); + result = 31 * result + subQuery.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return super.isCompatible( object ) + && object instanceof SqmDerivedRoot that + && subQuery.isCompatible( that.subQuery ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + subQuery.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmElementAggregateFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmElementAggregateFunction.java index a77b9c3c27ee..913abd96ecef 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmElementAggregateFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmElementAggregateFunction.java @@ -128,12 +128,27 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmElementAggregateFunction that && Objects.equals( this.functionName, that.functionName ) - && Objects.equals( this.getExplicitAlias(), that.getExplicitAlias() ) - && Objects.equals( this.getLhs(), that.getLhs() ); + && getLhs().equals( that.getLhs() ); } @Override public int hashCode() { - return Objects.hash( getLhs(), functionName ); + int result = getLhs().hashCode(); + result = 31 * result + functionName.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmElementAggregateFunction that + && Objects.equals( this.functionName, that.functionName ) + && getLhs().isCompatible( that.getLhs() ); + } + + @Override + public int cacheHashCode() { + int result = getLhs().cacheHashCode(); + result = 31 * result + functionName.hashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFkExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFkExpression.java index d53bb6698ef0..87acd610e130 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFkExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFkExpression.java @@ -16,7 +16,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.spi.NavigablePath; -import java.util.Objects; /** * Reference to the key-side (as opposed to the target-side) of the @@ -66,8 +65,7 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmFkExpression that - && Objects.equals( this.getExplicitAlias(), that.getExplicitAlias() ) - && Objects.equals( this.getLhs(), that.getLhs() ); + && getLhs().equals( that.getLhs() ); } @Override @@ -75,6 +73,17 @@ public int hashCode() { return getLhs().hashCode(); } + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmFkExpression that + && getLhs().isCompatible( that.getLhs() ); + } + + @Override + public int cacheHashCode() { + return getLhs().cacheHashCode(); + } + @Override public SqmFkExpression copy(SqmCopyContext context) { final SqmFkExpression existing = context.getCopy( this ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionPath.java index c264558b8f81..79e78a214a40 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionPath.java @@ -29,7 +29,6 @@ import jakarta.persistence.metamodel.ManagedType; import jakarta.persistence.metamodel.Type; -import java.util.Objects; import static java.util.Arrays.asList; @@ -167,11 +166,27 @@ public SqmTreatedPath treatAs(EntityDomainType treatTarget public boolean equals(Object object) { return object instanceof SqmFunctionPath that && super.equals( object ) - && Objects.equals( function, that.function ); + && function.equals( that.function ); } @Override public int hashCode() { - return Objects.hash( super.hashCode(), function ); + int result = super.hashCode(); + result = 31 * result + function.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmFunctionPath that + && super.isCompatible( object ) + && function.isCompatible( that.function ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + function.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionRoot.java index a04c5a7ed6a2..a437880991da 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionRoot.java @@ -17,7 +17,6 @@ import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.spi.NavigablePath; -import java.util.Objects; /** @@ -146,13 +145,29 @@ public SqmTreatedRoot treatAs(EntityDomainType treatTarget, Str @Override public boolean equals(Object object) { - return object instanceof SqmFunctionRoot that - && super.equals( object ) - && Objects.equals( this.function, that.function ); + return super.equals( object ) + && object instanceof SqmFunctionRoot that + && function.equals( that.function ); } @Override public int hashCode() { - return Objects.hash( super.hashCode(), function ); + int result = super.hashCode(); + result = 31 * result + function.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return super.isCompatible( object ) + && object instanceof SqmFunctionRoot that + && function.isCompatible( that.function ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + function.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexAggregateFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexAggregateFunction.java index 46633c0eb510..cc076ffa6e0e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexAggregateFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexAggregateFunction.java @@ -128,18 +128,31 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { hql.append( ')' ); } - @Override public boolean equals(Object object) { return object instanceof SqmIndexAggregateFunction that && Objects.equals( this.functionName, that.functionName ) - && Objects.equals( this.getExplicitAlias(), that.getExplicitAlias() ) - && Objects.equals( this.getLhs(), that.getLhs() ); - + && getLhs().equals( that.getLhs() ); } @Override public int hashCode() { - return Objects.hash( getLhs(), functionName ); + int result = getLhs().hashCode(); + result = 31 * result + functionName.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmIndexAggregateFunction that + && Objects.equals( this.functionName, that.functionName ) + && getLhs().isCompatible( that.getLhs() ); + } + + @Override + public int cacheHashCode() { + int result = getLhs().cacheHashCode(); + result = 31 * result + functionName.hashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java index b5edfcc480a9..0041e536612b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java @@ -14,7 +14,6 @@ import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; -import java.util.Objects; /** * @author Steve Ebersole @@ -105,13 +104,28 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmIndexedCollectionAccessPath that - && Objects.equals( this.getExplicitAlias(), that.getExplicitAlias() ) - && Objects.equals( this.getLhs(), that.getLhs() ) - && Objects.equals( this.selectorExpression, that.selectorExpression ); + && getLhs().equals( that.getLhs() ) + && selectorExpression.equals( that.selectorExpression ); } @Override public int hashCode() { - return Objects.hash( getLhs(), selectorExpression ); + int result = getLhs().hashCode(); + result = 31 * result + selectorExpression.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmIndexedCollectionAccessPath that + && getLhs().isCompatible( that.getLhs() ) + && selectorExpression.isCompatible( that.selectorExpression ); + } + + @Override + public int cacheHashCode() { + int result = getLhs().cacheHashCode(); + result = 31 * result + selectorExpression.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java index 340574f7a924..d5966657bfbf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java @@ -6,6 +6,7 @@ import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Predicate; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; @@ -40,7 +41,7 @@ public class SqmMapEntryReference private final JavaType> mapEntryTypeDescriptor; - private String explicitAlias; + private @Nullable String explicitAlias; public SqmMapEntryReference( SqmPath mapPath, @@ -151,13 +152,29 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmMapEntryReference that - && Objects.equals( mapPath, that.mapPath ) + && mapPath.equals( that.mapPath ) && Objects.equals( explicitAlias, that.explicitAlias ); } @Override public int hashCode() { - return Objects.hash( mapPath, explicitAlias ); + int result = mapPath.hashCode(); + result = 31 * result + Objects.hashCode( explicitAlias ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmMapEntryReference that + && mapPath.isCompatible( that.mapPath ) + && Objects.equals( explicitAlias, that.explicitAlias ); + } + + @Override + public int cacheHashCode() { + int result = mapPath.cacheHashCode(); + result = 31 * result + Objects.hashCode( explicitAlias ); + return result; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java index a7c53755471b..588c5e9e77b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java @@ -142,6 +142,16 @@ public int hashCode() { return Objects.hash( treatTarget.getTypeName(), wrappedPath.getNavigablePath() ); } + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } + @Override public SqmTreatedBagJoin on(JpaExpression restriction) { return (SqmTreatedBagJoin) super.on( restriction ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedCrossJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedCrossJoin.java index 2943b091532f..9332011fd97b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedCrossJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedCrossJoin.java @@ -115,6 +115,16 @@ public int hashCode() { return Objects.hash( treatTarget.getName(), wrappedPath.getNavigablePath() ); } + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } + @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public SqmTreatedCrossJoin treatAs(Class treatJavaType, String alias) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEmbeddedValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEmbeddedValuedSimplePath.java index 1cae905dfe6f..344333f4d438 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEmbeddedValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEmbeddedValuedSimplePath.java @@ -132,4 +132,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash( treatTarget.getTypeName(), wrappedPath.getNavigablePath() ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java index c77c6bde01a7..5d84f0c39b28 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java @@ -120,4 +120,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash( treatTarget.getName(), wrappedPath.getNavigablePath() ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityValuedSimplePath.java index 32260007422d..8691df7dd5ce 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityValuedSimplePath.java @@ -156,4 +156,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash( treatTarget.getTypeName(), wrappedPath.getNavigablePath() ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java index 34889817a14a..d7b7c993e18b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java @@ -172,4 +172,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash( treatTarget.getTypeName(), wrappedPath.getNavigablePath() ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java index 89234ce2fe1b..94a593cab63e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java @@ -181,4 +181,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash( treatTarget.getTypeName(), wrappedPath.getNavigablePath() ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedPluralPartJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedPluralPartJoin.java index 21e0c8aedc81..881c4bb294e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedPluralPartJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedPluralPartJoin.java @@ -159,4 +159,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash( treatTarget.getName(), wrappedPath.getNavigablePath() ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java index cbbce94c825e..781314e394f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java @@ -139,4 +139,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash( wrappedPath.getNavigablePath(), treatTarget.getName() ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java index 6a384205152a..67e23a1601fe 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java @@ -142,6 +142,16 @@ public int hashCode() { return Objects.hash( treatTarget.getTypeName(), wrappedPath.getNavigablePath() ); } + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } + @Override public SqmTreatedSetJoin on(JpaExpression restriction) { return (SqmTreatedSetJoin) super.on( restriction ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java index f737728a021e..a5517f4865a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java @@ -158,4 +158,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash( treatTarget.getName(), wrappedPath.getNavigablePath() ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java index 28e3d24fd484..1e42ff7f29ce 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java @@ -145,6 +145,16 @@ public int hashCode() { return Objects.hash( treatTarget.getTypeName(), wrappedPath.getNavigablePath() ); } + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } + @Override public SqmTreatedSingularJoin treatAs(Class treatJavaType) { return (SqmTreatedSingularJoin) super.treatAs( treatJavaType ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmJsonPathExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmJsonPathExpression.java index 336bc5d228b0..4dab4e48f0f7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmJsonPathExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AbstractSqmJsonPathExpression.java @@ -8,6 +8,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import org.hibernate.Incubating; import org.hibernate.internal.util.QuotingHelper; @@ -19,6 +20,7 @@ import org.hibernate.query.sqm.produce.function.ArgumentsValidator; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; @@ -124,4 +126,32 @@ protected void appendPassingExpressionHqlString(StringBuilder sb, SqmRenderConte } } } + + @Override + public boolean equals(Object other) { + return super.equals( other ) + && other instanceof AbstractSqmJsonPathExpression that + && Objects.equals( passingExpressions, that.passingExpressions ); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + Objects.hashCode( passingExpressions ); + return result; + } + + @Override + public boolean isCompatible(Object other) { + return super.isCompatible( other ) + && other instanceof AbstractSqmJsonPathExpression that + && SqmCacheable.areCompatible( passingExpressions, that.passingExpressions ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( passingExpressions ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AsWrapperSqmExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AsWrapperSqmExpression.java index 3114605b4e8a..307532714cb5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AsWrapperSqmExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/AsWrapperSqmExpression.java @@ -56,12 +56,24 @@ public BasicType getNodeType() { @Override public boolean equals(Object object) { return object instanceof AsWrapperSqmExpression that - && Objects.equals( this.expression, that.expression ) + && this.expression.equals( that.expression ) && Objects.equals( this.getNodeType(), that.getNodeType() ); } @Override public int hashCode() { - return Objects.hashCode( expression ); + return expression.hashCode(); + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof AsWrapperSqmExpression that + && this.expression.isCompatible( that.expression ) + && Objects.equals( this.getNodeType(), that.getNodeType() ); + } + + @Override + public int cacheHashCode() { + return expression.cacheHashCode(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java index 2cccfc35aa2b..0d2285ba6693 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java @@ -144,26 +144,29 @@ private String name(SqmRenderContext context) { return name == null ? context.resolveParameterName( this ) : name; } - @Override - public int compareTo(SqmParameter parameter) { - return parameter instanceof JpaCriteriaParameter - ? Integer.compare( hashCode(), parameter.hashCode() ) - : 1; - } - - // we can use value equality if the parameter has a name - // otherwise we must fall back to identity equality - @Override public boolean equals(Object object) { return this == object || object instanceof JpaCriteriaParameter that - && this.name != null && that.name != null - && Objects.equals( this.name, that.name ); + && name != null + && Objects.equals( name, that.name ); } @Override public int hashCode() { return name == null ? super.hashCode() : name.hashCode(); } + + // For caching, we can consider two parameters to be compatible if are unnamed, or they have the same name + + @Override + public boolean isCompatible(Object object) { + return getClass() == object.getClass() + && Objects.equals( name, ((JpaCriteriaParameter) object).name ); + } + + @Override + public int cacheHashCode() { + return name == null ? 0 : name.hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAliasedNodeRef.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAliasedNodeRef.java index 427596fb249d..4b467178ba34 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAliasedNodeRef.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAliasedNodeRef.java @@ -4,6 +4,7 @@ */ package org.hibernate.query.sqm.tree.expression; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.spi.NavigablePath; @@ -22,7 +23,7 @@ public class SqmAliasedNodeRef extends AbstractSqmExpression { private final int position; // The navigable path is optionally set in case this is a reference to an attribute of a selection - private final NavigablePath navigablePath; + private final @Nullable NavigablePath navigablePath; public SqmAliasedNodeRef(int position, SqmBindableType intType, NodeBuilder criteriaBuilder) { super( intType, criteriaBuilder ); @@ -94,4 +95,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash( position, navigablePath == null ? null : navigablePath.getLocalName() ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java index 8017455723f1..2e085657ecbd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java @@ -13,7 +13,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; -import java.util.Objects; /** * @author Gavin King @@ -72,11 +71,22 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmAny sqmAny - && Objects.equals( subquery, sqmAny.subquery ); + && subquery.equals( sqmAny.subquery ); } @Override public int hashCode() { return subquery.hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmAny sqmAny + && subquery.isCompatible( sqmAny.subquery ); + } + + @Override + public int cacheHashCode() { + return subquery.cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAnyDiscriminatorValue.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAnyDiscriminatorValue.java index 0ee0b6b5892b..0d527173c73c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAnyDiscriminatorValue.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAnyDiscriminatorValue.java @@ -107,6 +107,22 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hash( value.getName(), pathName ); + int result = value.getName().hashCode(); + result = 31 * result + pathName.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmAnyDiscriminatorValue that + && Objects.equals( this.value.getName(), that.value.getName() ) + && Objects.equals( this.pathName, that.pathName ); + } + + @Override + public int cacheHashCode() { + int result = value.getName().hashCode(); + result = 31 * result + pathName.hashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmBinaryArithmetic.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmBinaryArithmetic.java index b46b775d7452..2ab8ba751dcb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmBinaryArithmetic.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmBinaryArithmetic.java @@ -12,7 +12,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.select.SqmSelectableNode; -import java.util.Objects; import static org.hibernate.query.sqm.BinaryArithmeticOperator.ADD; import static org.hibernate.query.sqm.BinaryArithmeticOperator.SUBTRACT; @@ -151,12 +150,31 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmBinaryArithmetic that && this.operator == that.operator - && Objects.equals( this.lhsOperand, that.lhsOperand ) - && Objects.equals( this.rhsOperand, that.rhsOperand ); + && this.lhsOperand.equals( that.lhsOperand ) + && this.rhsOperand.equals( that.rhsOperand ); } @Override public int hashCode() { - return Objects.hash( lhsOperand, rhsOperand, operator ); + int result = lhsOperand.hashCode(); + result = 31 * result + operator.hashCode(); + result = 31 * result + rhsOperand.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmBinaryArithmetic that + && this.operator == that.operator + && this.lhsOperand.isCompatible( that.lhsOperand ) + && this.rhsOperand.isCompatible( that.rhsOperand ); + } + + @Override + public int cacheHashCode() { + int result = lhsOperand.cacheHashCode(); + result = 31 * result + operator.hashCode(); + result = 31 * result + rhsOperand.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmByUnit.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmByUnit.java index 8e8e3d49825c..b43c80926275 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmByUnit.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmByUnit.java @@ -10,8 +10,6 @@ import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; -import java.util.Objects; - /** * @author Gavin King */ @@ -71,12 +69,28 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmByUnit that - && Objects.equals( this.unit, that.unit ) - && Objects.equals( this.duration, that.duration ); + && this.unit.equals( that.unit ) + && this.duration.equals( that.duration ); } @Override public int hashCode() { - return Objects.hash( unit, duration ); + int result = unit.hashCode(); + result = 31 * result + duration.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmByUnit that + && this.unit.isCompatible( that.unit ) + && this.duration.isCompatible( that.duration ); + } + + @Override + public int cacheHashCode() { + int result = unit.cacheHashCode(); + result = 31 * result + duration.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java index c87fc1aca4a0..5383dc1094cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java @@ -8,11 +8,13 @@ import java.util.List; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.query.criteria.JpaSearchedCase; import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmBindableType; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.predicate.SqmPredicate; @@ -26,7 +28,7 @@ public class SqmCaseSearched extends AbstractSqmExpression implements JpaSearchedCase { private final List> whenFragments; - private SqmExpression otherwise; + private @Nullable SqmExpression otherwise; public SqmCaseSearched(NodeBuilder nodeBuilder) { this( null, nodeBuilder ); @@ -123,7 +125,7 @@ public String asLoggableText() { return ""; } - public static class WhenFragment { + public static class WhenFragment implements SqmCacheable { private final SqmPredicate predicate; private final SqmExpression result; @@ -139,6 +141,34 @@ public SqmPredicate getPredicate() { public SqmExpression getResult() { return result; } + + @Override + public boolean equals(Object object) { + return object instanceof WhenFragment that + && predicate.equals( that.predicate ) + && result.equals( that.result ); + } + + @Override + public int hashCode() { + int result = predicate.hashCode(); + result = 31 * result + this.result.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof WhenFragment that + && predicate.isCompatible( that.predicate ) + && result.isCompatible( that.result ); + } + + @Override + public int cacheHashCode() { + int result = predicate.cacheHashCode(); + result = 31 * result + this.result.cacheHashCode(); + return result; + } } @Override @@ -167,7 +197,23 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hash( whenFragments, otherwise ); + int result = Objects.hashCode( whenFragments ); + result = 31 * result + Objects.hashCode( otherwise ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmCaseSearched that + && SqmCacheable.areCompatible( this.whenFragments, that.whenFragments ) + && SqmCacheable.areCompatible( this.otherwise, that.otherwise ); + } + + @Override + public int cacheHashCode() { + int result = SqmCacheable.cacheHashCode( whenFragments ); + result = 31 * result + SqmCacheable.cacheHashCode( otherwise ); + return result; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java index dc349567a449..9dbf0ccfa193 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java @@ -8,12 +8,14 @@ import java.util.List; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaSimpleCase; import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import jakarta.persistence.criteria.Expression; @@ -27,7 +29,7 @@ public class SqmCaseSimple implements JpaSimpleCase { private final SqmExpression fixture; private final List> whenFragments; - private SqmExpression otherwise; + private @Nullable SqmExpression otherwise; public SqmCaseSimple(SqmExpression fixture, NodeBuilder nodeBuilder) { this( fixture, null, 10, nodeBuilder ); @@ -136,7 +138,7 @@ public String asLoggableText() { return ""; } - public static class WhenFragment { + public static class WhenFragment implements SqmCacheable { private final SqmExpression checkValue; private final SqmExpression result; @@ -152,6 +154,34 @@ public SqmExpression getCheckValue() { public SqmExpression getResult() { return result; } + + @Override + public boolean equals(Object object) { + return object instanceof WhenFragment that + && checkValue.equals( that.checkValue ) + && result.equals( that.result ); + } + + @Override + public int hashCode() { + int result = checkValue.hashCode(); + result = 31 * result + this.result.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof WhenFragment that + && checkValue.isCompatible( that.checkValue ) + && result.isCompatible( that.result ); + } + + @Override + public int cacheHashCode() { + int result = checkValue.cacheHashCode(); + result = 31 * result + this.result.cacheHashCode(); + return result; + } } @Override @@ -175,14 +205,33 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmCaseSimple that - && Objects.equals( this.fixture, that.fixture ) + && this.fixture.equals( that.fixture ) && Objects.equals( this.whenFragments, that.whenFragments ) && Objects.equals( this.otherwise, that.otherwise ); } @Override public int hashCode() { - return Objects.hash( fixture, whenFragments, otherwise ); + int result = fixture.hashCode(); + result = 31 * result + Objects.hashCode( whenFragments ); + result = 31 * result + Objects.hashCode( otherwise ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmCaseSimple that + && this.fixture.isCompatible( that.fixture ) + && SqmCacheable.areCompatible( this.whenFragments, that.whenFragments ) + && SqmCacheable.areCompatible( this.otherwise, that.otherwise ); + } + + @Override + public int cacheHashCode() { + int result = fixture.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( whenFragments ); + result = 31 * result + SqmCacheable.cacheHashCode( otherwise ); + return result; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCastTarget.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCastTarget.java index fcfe277d1c80..f4428f2cbfef 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCastTarget.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCastTarget.java @@ -127,4 +127,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash( type, length, precision, scale ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java index 28fd59903fe3..4983d855e62d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java @@ -14,6 +14,7 @@ import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.function.SqmFunctionDescriptor; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import jakarta.persistence.criteria.Expression; @@ -107,7 +108,18 @@ public boolean equals(Object object) { @Override public int hashCode() { - return arguments.hashCode(); + return Objects.hashCode( arguments ); + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmCoalesce that + && SqmCacheable.areCompatible( this.arguments, that.arguments ); + } + + @Override + public int cacheHashCode() { + return SqmCacheable.cacheHashCode( arguments ); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCollectionSize.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCollectionSize.java index c48974b4f815..0b6842fb571a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCollectionSize.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCollectionSize.java @@ -11,7 +11,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.domain.SqmPath; -import java.util.Objects; /** * Represents the {@code SIZE()} function. @@ -73,11 +72,22 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmCollectionSize that - && Objects.equals( this.pluralPath, that.pluralPath ); + && this.pluralPath.equals( that.pluralPath ); } @Override public int hashCode() { return pluralPath.hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmCollectionSize that + && this.pluralPath.isCompatible( that.pluralPath ); + } + + @Override + public int cacheHashCode() { + return pluralPath.cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDistinct.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDistinct.java index 175fe9960c80..ecc2a1f971f0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDistinct.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDistinct.java @@ -12,7 +12,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; -import java.util.Objects; /** * @author Gavin King @@ -64,11 +63,22 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmDistinct that - && Objects.equals( this.expression, that.expression ); + && expression.equals( that.expression ); } @Override public int hashCode() { - return Objects.hashCode( expression ); + return expression.hashCode(); + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmDistinct that + && expression.isCompatible( that.expression ); + } + + @Override + public int cacheHashCode() { + return expression.cacheHashCode(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDurationUnit.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDurationUnit.java index 671d2d68e92f..0a15debe9fea 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDurationUnit.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDurationUnit.java @@ -66,4 +66,14 @@ public boolean equals(Object object) { public int hashCode() { return unit.hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java index ee2c98f37ead..fdceac7ad2be 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java @@ -29,7 +29,6 @@ * @author Steve Ebersole */ public class SqmEnumLiteral> extends SqmLiteral implements SqmBindableType, SemanticPathPart { - private final E enumValue; private final EnumJavaType referencedEnumTypeDescriptor; private final String enumValueName; @@ -38,8 +37,7 @@ public SqmEnumLiteral( EnumJavaType referencedEnumTypeDescriptor, String enumValueName, NodeBuilder nodeBuilder) { - super( null, nodeBuilder ); - this.enumValue = enumValue; + super( null, enumValue, nodeBuilder ); this.referencedEnumTypeDescriptor = referencedEnumTypeDescriptor; this.enumValueName = enumValueName; setExpressibleType( this ); @@ -54,7 +52,7 @@ public SqmEnumLiteral copy(SqmCopyContext context) { final SqmEnumLiteral expression = context.registerCopy( this, new SqmEnumLiteral<>( - enumValue, + getEnumValue(), referencedEnumTypeDescriptor, enumValueName, nodeBuilder() @@ -80,7 +78,7 @@ public SqmDomainType getSqmType() { } public E getEnumValue() { - return enumValue; + return getLiteralValue(); } @Override @@ -128,7 +126,7 @@ public SqmPath resolveIndexedAccess( } private Integer ordinalValue() { - return getExpressibleJavaType().toOrdinal( enumValue ); + return getExpressibleJavaType().toOrdinal( getEnumValue() ); } @Override @@ -163,7 +161,7 @@ public SqmExpression asBigInteger() { @Override public SqmExpression asString() { - return nodeBuilder().literal( getExpressibleJavaType().toName( enumValue ) ); + return nodeBuilder().literal( getExpressibleJavaType().toName( getEnumValue() ) ); } @Override @@ -173,7 +171,7 @@ public X accept(SemanticQueryWalker walker) { @Override public void appendHqlString(StringBuilder hql, SqmRenderContext context) { - hql.append( enumValue.getDeclaringClass().getTypeName() ); + hql.append( getEnumValue().getDeclaringClass().getTypeName() ); hql.append( '.' ); hql.append( enumValueName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java index 106a517a158b..80df24a60361 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java @@ -13,8 +13,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; -import java.util.Objects; - /** * @author Gavin King */ @@ -68,11 +66,22 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmEvery sqmAny - && Objects.equals( this.subquery, sqmAny.subquery ); + && this.subquery.equals( sqmAny.subquery ); } @Override public int hashCode() { return subquery.hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmEvery sqmAny + && this.subquery.isCompatible( sqmAny.subquery ); + } + + @Override + public int cacheHashCode() { + return subquery.cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmExtractUnit.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmExtractUnit.java index aad3dbf7a719..465de7162a98 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmExtractUnit.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmExtractUnit.java @@ -14,6 +14,8 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; +import java.util.Objects; + /** * @author Gavin King */ @@ -54,4 +56,28 @@ public SqmBindableType getNodeType() { public void appendHqlString(StringBuilder hql, SqmRenderContext context) { hql.append( unit ); } + + @Override + public boolean equals(Object object) { + return object instanceof SqmExtractUnit that + && unit == that.unit + && Objects.equals( type, that.type ); + } + + @Override + public int hashCode() { + int result = Objects.hashCode( unit ); + result = 31 * result + Objects.hashCode( type ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java index a956f3035722..09594d827fdf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java @@ -38,7 +38,7 @@ */ public class SqmFieldLiteral implements SqmExpression, SqmBindableType, SqmSelectableNode, SemanticPathPart { - private final T value; + private final @Nullable T value; private final JavaType fieldJavaType; private final String fieldName; private final NodeBuilder nodeBuilder; @@ -148,17 +148,6 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { SqmLiteral.appendHqlString( hql, getJavaTypeDescriptor(), getValue() ); } - @Override - public boolean equals(Object object) { - return object instanceof SqmFieldLiteral that - && Objects.equals( value, that.value ); - } - - @Override - public int hashCode() { - return Objects.hash( value ); - } - @Override public NodeBuilder nodeBuilder() { return nodeBuilder; @@ -323,4 +312,24 @@ public SqmDomainType getSqmType() { return null; } + @Override + public boolean equals(Object object) { + return object instanceof SqmFieldLiteral that + && Objects.equals( value, that.value ); + } + + @Override + public int hashCode() { + return Objects.hash( value ); + } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java index 3f2a4fc1599f..12633cdcf860 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java @@ -15,6 +15,7 @@ import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.function.SqmFunctionDescriptor; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.domain.SqmFunctionPath; @@ -206,20 +207,31 @@ public SqmPath resolveIndexedAccess( @Override public boolean equals(Object other) { - if ( this == other ) { - return true; - } - if ( other == null || getClass() != other.getClass() ) { - return false; - } - - final SqmFunction that = (SqmFunction) other; - return Objects.equals( this.functionName, that.functionName ) - && Objects.equals( this.arguments, that.arguments ); + return other instanceof SqmFunction that + && getClass() == other.getClass() + && functionName.equals( that.functionName ) + && Objects.equals( arguments, that.arguments ); } @Override public int hashCode() { - return Objects.hash( functionName, arguments ); + int result = functionName.hashCode(); + result = 31 * result + Objects.hashCode( arguments ); + return result; + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmFunction that + && getClass() == other.getClass() + && functionName.equals( that.functionName ) + && SqmCacheable.areCompatible( arguments, that.arguments ); + } + + @Override + public int cacheHashCode() { + int result = functionName.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( arguments ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmHqlNumericLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmHqlNumericLiteral.java index 360aa3d4cd3a..dc78a6a4c37c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmHqlNumericLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmHqlNumericLiteral.java @@ -86,6 +86,30 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { } ); } + @Override + public boolean equals(Object object) { + return object instanceof SqmHqlNumericLiteral that + && literalValue.equals( that.literalValue ) + && typeCategory.equals( that.typeCategory ); + } + + @Override + public int hashCode() { + int result = literalValue.hashCode(); + result = 31 * result + typeCategory.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } + @Override public String asLoggableText() { final StringBuilder stringBuilder = new StringBuilder(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java index cf4fc3a28d18..2f13fe994913 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java @@ -28,13 +28,19 @@ public class SqmJpaCriteriaParameterWrapper extends AbstractSqmExpression implements SqmParameter { private final JpaCriteriaParameter jpaCriteriaParameter; + private final int criteriaParameterId; + private final int unnamedParameterId; public SqmJpaCriteriaParameterWrapper( BindableType type, JpaCriteriaParameter jpaCriteriaParameter, + int criteriaParameterId, + int unnamedParameterId, NodeBuilder criteriaBuilder) { super( toSqmType( type, criteriaBuilder ), criteriaBuilder ); this.jpaCriteriaParameter = jpaCriteriaParameter; + this.criteriaParameterId = criteriaParameterId; + this.unnamedParameterId = unnamedParameterId; } @Override @@ -48,6 +54,8 @@ public SqmJpaCriteriaParameterWrapper copy(SqmCopyContext context) { new SqmJpaCriteriaParameterWrapper<>( getNodeType(), jpaCriteriaParameter.copy( context ), + criteriaParameterId, + unnamedParameterId, nodeBuilder() ) ); @@ -68,6 +76,27 @@ public JpaCriteriaParameter getJpaCriteriaParameter() { return jpaCriteriaParameter; } + /** + * The 0-based encounter of a {@link JpaCriteriaParameter} instance in a + * {@link org.hibernate.query.sqm.SqmQuerySource#CRITERIA} query. + * + * @see org.hibernate.query.sqm.tree.jpa.ParameterCollector + */ + public int getCriteriaParameterId() { + return criteriaParameterId; + } + + /** + * The 0-based encounter of an unnamed {@link JpaCriteriaParameter} instance in a + * {@link org.hibernate.query.sqm.SqmQuerySource#CRITERIA} query. + * If the {@link #getJpaCriteriaParameter()} has a name, returns -1. + * + * @see org.hibernate.query.sqm.tree.jpa.ParameterCollector + */ + public int getUnnamedParameterId() { + return unnamedParameterId; + } + @Override public Class getParameterType() { return jpaCriteriaParameter.getParameterType(); @@ -88,6 +117,8 @@ public SqmParameter copy() { return new SqmJpaCriteriaParameterWrapper<>( getNodeType(), jpaCriteriaParameter, + criteriaParameterId, + unnamedParameterId, nodeBuilder() ); } @@ -129,20 +160,23 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { } @Override - public int compareTo(SqmParameter parameter) { - return parameter instanceof SqmJpaCriteriaParameterWrapper wrapper - ? getJpaCriteriaParameter().compareTo( wrapper.getJpaCriteriaParameter() ) - : 1; - } - -// @Override -// public boolean equals(Object object) { -// return object instanceof SqmJpaCriteriaParameterWrapper that -// && Objects.equals( this.jpaCriteriaParameter, that.jpaCriteriaParameter ); -// } -// -// @Override -// public int hashCode() { -// return jpaCriteriaParameter.hashCode(); -// } + public final boolean equals(Object o) { + return o instanceof SqmJpaCriteriaParameterWrapper that + && criteriaParameterId == that.criteriaParameterId; + } + + @Override + public int hashCode() { + return criteriaParameterId; + } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonExistsExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonExistsExpression.java index 51aae5631373..6dae832b9fa7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonExistsExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonExistsExpression.java @@ -188,4 +188,32 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { } ); hql.append( ')' ); } + + @Override + public boolean equals(Object other) { + return super.equals( other ) + && other instanceof SqmJsonExistsExpression that + && errorBehavior == that.errorBehavior; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + errorBehavior.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object other) { + return super.isCompatible( other ) + && other instanceof SqmJsonExistsExpression that + && errorBehavior == that.errorBehavior; + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + errorBehavior.hashCode(); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonNullBehavior.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonNullBehavior.java index c91a27dd0c6e..7cab17b40b01 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonNullBehavior.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonNullBehavior.java @@ -59,4 +59,14 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { hql.append( " absent on null" ); } } + + @Override + public boolean isCompatible(Object object) { + return this == object; + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonObjectAggUniqueKeysBehavior.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonObjectAggUniqueKeysBehavior.java index 4d07c6944b62..19a3b2998409 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonObjectAggUniqueKeysBehavior.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonObjectAggUniqueKeysBehavior.java @@ -59,4 +59,14 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { hql.append( " without unique keys" ); } } + + @Override + public boolean isCompatible(Object object) { + return this == object; + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonQueryExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonQueryExpression.java index b899ac0490c6..b5dcd91e1301 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonQueryExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonQueryExpression.java @@ -291,4 +291,40 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { } hql.append( ')' ); } + + @Override + public boolean equals(Object other) { + return super.equals( other ) + && other instanceof SqmJsonQueryExpression that + && wrapMode == that.wrapMode + && errorBehavior == that.errorBehavior + && emptyBehavior == that.emptyBehavior; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + wrapMode.hashCode(); + result = 31 * result + errorBehavior.hashCode(); + result = 31 * result + emptyBehavior.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object other) { + return super.isCompatible( other ) + && other instanceof SqmJsonQueryExpression that + && wrapMode == that.wrapMode + && errorBehavior == that.errorBehavior + && emptyBehavior == that.emptyBehavior; + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + wrapMode.hashCode(); + result = 31 * result + errorBehavior.hashCode(); + result = 31 * result + emptyBehavior.hashCode(); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonTableFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonTableFunction.java index 1f1babb8d689..2a8475d498dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonTableFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonTableFunction.java @@ -15,6 +15,7 @@ import org.hibernate.query.criteria.JpaJsonTableFunction; import org.hibernate.query.criteria.JpaJsonValueNode; import org.hibernate.query.sqm.SqmBindableType; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tuple.internal.AnonymousTupleType; import org.hibernate.query.sqm.NodeBuilder; @@ -54,6 +55,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import static org.hibernate.internal.util.NullnessUtil.castNonNull; @@ -286,7 +288,43 @@ private void checkTypeResolved() { } } - sealed interface ColumnDefinition { + @Override + public boolean equals(Object object) { + return object instanceof SqmJsonTableFunction that + && super.equals( object ) + && columns.equals( that.columns ) + && Objects.equals( passingExpressions, that.passingExpressions ) + && errorBehavior == that.errorBehavior; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + columns.hashCode(); + result = 31 * result + Objects.hashCode( passingExpressions ); + result = 31 * result + errorBehavior.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmJsonTableFunction that + && super.isCompatible( object ) + && columns.isCompatible( that.columns ) + && SqmCacheable.areCompatible( passingExpressions, that.passingExpressions ) + && errorBehavior == that.errorBehavior; + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + columns.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( passingExpressions ); + result = 31 * result + errorBehavior.hashCode(); + return result; + } + + sealed interface ColumnDefinition extends SqmCacheable { ColumnDefinition copy(SqmCopyContext context); JsonTableColumnDefinition convertToSqlAst(SqmToSqlAstConverter walker); @@ -388,6 +426,34 @@ public JpaJsonExistsNode falseOnError() { errorBehavior = ErrorBehavior.FALSE; return this; } + + @Override + public boolean equals(Object object) { + return object instanceof ExistsColumnDefinition that + && name.equals( that.name ) + && type.equals( that.type ) + && Objects.equals( jsonPath, that.jsonPath ) + && errorBehavior == that.errorBehavior; + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + type.hashCode(); + result = 31 * result + Objects.hashCode( jsonPath ); + result = 31 * result + errorBehavior.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } static final class QueryColumnDefinition implements ColumnDefinition, JpaJsonQueryNode { @@ -579,6 +645,38 @@ public JpaJsonQueryNode emptyObjectOnEmpty() { emptyBehavior = EmptyBehavior.EMPTY_OBJECT; return this; } + + @Override + public boolean equals(Object object) { + return object instanceof QueryColumnDefinition that + && name.equals( that.name ) + && type.equals( that.type ) + && Objects.equals( jsonPath, that.jsonPath ) + && wrapMode == that.wrapMode + && errorBehavior == that.errorBehavior + && emptyBehavior == that.emptyBehavior; + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + type.hashCode(); + result = 31 * result + Objects.hashCode( jsonPath ); + result = 31 * result + wrapMode.hashCode(); + result = 31 * result + errorBehavior.hashCode(); + result = 31 * result + emptyBehavior.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } static final class ValueColumnDefinition implements ColumnDefinition, JpaJsonValueNode { @@ -768,6 +866,54 @@ public JpaJsonValueNode defaultOnEmpty(Expression expression) { this.emptyBehavior = EmptyBehavior.DEFAULT; return this; } + + @Override + public boolean equals(Object object) { + return object instanceof ValueColumnDefinition that + && name.equals( that.name ) + && type.equals( that.type ) + && Objects.equals( jsonPath, that.jsonPath ) + && errorBehavior == that.errorBehavior + && emptyBehavior == that.emptyBehavior + && Objects.equals( errorDefaultExpression, that.errorDefaultExpression ) + && Objects.equals( emptyDefaultExpression, that.emptyDefaultExpression ); + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + type.hashCode(); + result = 31 * result + Objects.hashCode( jsonPath ); + result = 31 * result + errorBehavior.hashCode(); + result = 31 * result + emptyBehavior.hashCode(); + result = 31 * result + Objects.hashCode( errorDefaultExpression ); + result = 31 * result + Objects.hashCode( emptyDefaultExpression ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof ValueColumnDefinition that + && name.equals( that.name ) + && type.equals( that.type ) + && Objects.equals( jsonPath, that.jsonPath ) + && errorBehavior == that.errorBehavior + && emptyBehavior == that.emptyBehavior + && SqmCacheable.areCompatible( errorDefaultExpression, that.errorDefaultExpression ) + && SqmCacheable.areCompatible( emptyDefaultExpression, that.emptyDefaultExpression ); + } + + @Override + public int cacheHashCode() { + int result = name.hashCode(); + result = 31 * result + type.hashCode(); + result = 31 * result + Objects.hashCode( jsonPath ); + result = 31 * result + errorBehavior.hashCode(); + result = 31 * result + emptyBehavior.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( errorDefaultExpression ); + result = 31 * result + SqmCacheable.cacheHashCode( emptyDefaultExpression ); + return result; + } } record OrdinalityColumnDefinition(String name, BasicType type) implements ColumnDefinition { @@ -793,6 +939,16 @@ public int populateTupleType(int offset, String[] componentNames, SqmExpressible componentTypes[offset] = type; return 1; } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } static sealed class NestedColumns implements ColumnDefinition, JpaJsonTableColumnsNode { @@ -946,6 +1102,34 @@ else if ( columnDefinition instanceof NestedColumns nestedColumns ) { // No-op since this object is going to be visible as function argument return null; } + + @Override + public boolean equals(Object object) { + return object instanceof NestedColumns that + && jsonPath.equals( that.jsonPath ) + && Objects.equals( columnDefinitions, that.columnDefinitions ); + } + + @Override + public int hashCode() { + int result = jsonPath.hashCode(); + result = 31 * result + Objects.hashCode( columnDefinitions ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof NestedColumns that + && jsonPath.equals( that.jsonPath ) + && SqmCacheable.areCompatible( columnDefinitions, that.columnDefinitions ); + } + + @Override + public int cacheHashCode() { + int result = jsonPath.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( columnDefinitions ); + return result; + } } public static final class Columns extends NestedColumns implements SqmTypedNode { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonValueExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonValueExpression.java index e263c41ddf5a..31303560e7b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonValueExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonValueExpression.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import org.hibernate.Incubating; import org.hibernate.metamodel.model.domain.ReturnableType; @@ -19,6 +20,7 @@ import org.hibernate.query.sqm.produce.function.ArgumentsValidator; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; @@ -30,6 +32,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static org.hibernate.internal.util.NullnessUtil.castNonNull; + /** * Special expression for the json_value function that also captures special syntax elements like error and empty behavior. * @@ -38,9 +42,9 @@ @Incubating public class SqmJsonValueExpression extends AbstractSqmJsonPathExpression implements JpaJsonValueExpression { private ErrorBehavior errorBehavior = ErrorBehavior.UNSPECIFIED; - private SqmExpression errorDefaultExpression; + private @Nullable SqmExpression errorDefaultExpression; private EmptyBehavior emptyBehavior = EmptyBehavior.UNSPECIFIED; - private SqmExpression emptyDefaultExpression; + private @Nullable SqmExpression emptyDefaultExpression; public SqmJsonValueExpression( SqmFunctionDescriptor descriptor, @@ -225,14 +229,14 @@ public Expression convertToSqlAst(SqmToSqlAstConverter walker) { case NULL -> arguments.add( JsonValueErrorBehavior.NULL ); case ERROR -> arguments.add( JsonValueErrorBehavior.ERROR ); case DEFAULT -> arguments.add( JsonValueErrorBehavior.defaultOnError( - (Expression) errorDefaultExpression.accept( walker ) + (Expression) castNonNull( errorDefaultExpression ).accept( walker ) ) ); } switch ( emptyBehavior ) { case NULL -> arguments.add( JsonValueEmptyBehavior.NULL ); case ERROR -> arguments.add( JsonValueEmptyBehavior.ERROR ); case DEFAULT -> arguments.add( JsonValueEmptyBehavior.defaultOnEmpty( - (Expression) emptyDefaultExpression.accept( walker ) + (Expression) castNonNull( emptyDefaultExpression ).accept( walker ) ) ); } return new SelfRenderingFunctionSqlAstExpression( @@ -260,6 +264,7 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { case NULL -> hql.append( " null on error" ); case ERROR -> hql.append( " error on error" ); case DEFAULT -> { + assert errorDefaultExpression != null; hql.append( " default " ); errorDefaultExpression.appendHqlString( hql, context ); hql.append( " on error" ); @@ -269,6 +274,7 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { case NULL -> hql.append( " null on empty" ); case ERROR -> hql.append( " error on empty" ); case DEFAULT -> { + assert emptyDefaultExpression != null; hql.append( " default " ); emptyDefaultExpression.appendHqlString( hql, context ); hql.append( " on empty" ); @@ -276,4 +282,44 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { } hql.append( ')' ); } + + @Override + public boolean equals(Object other) { + return super.equals( other ) + && other instanceof SqmJsonValueExpression that + && errorBehavior == that.errorBehavior + && Objects.equals( errorDefaultExpression, that.errorDefaultExpression ) + && emptyBehavior == that.emptyBehavior + && Objects.equals( emptyDefaultExpression, that.emptyDefaultExpression ); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + errorBehavior.hashCode(); + result = 31 * result + Objects.hashCode( errorDefaultExpression ); + result = 31 * result + emptyBehavior.hashCode(); + result = 31 * result + Objects.hashCode( emptyDefaultExpression ); + return result; + } + + @Override + public boolean isCompatible(Object other) { + return super.isCompatible( other ) + && other instanceof SqmJsonValueExpression that + && errorBehavior == that.errorBehavior + && SqmCacheable.areCompatible( errorDefaultExpression, that.errorDefaultExpression ) + && emptyBehavior == that.emptyBehavior + && SqmCacheable.areCompatible( emptyDefaultExpression, that.emptyDefaultExpression ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + errorBehavior.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( errorDefaultExpression ); + result = 31 * result + emptyBehavior.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( emptyDefaultExpression ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteral.java index 81b77428d2a1..0bc23f8e6a4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteral.java @@ -44,6 +44,12 @@ protected SqmLiteral(SqmBindableType inherentType, NodeBuilder nodeBuilder) { this.value = null; } + // Constructor for SqmEnumLiteral + SqmLiteral(SqmBindableType inherentType, T value, NodeBuilder nodeBuilder) { + super( inherentType, nodeBuilder ); + this.value = value; + } + @Override public SqmLiteral copy(SqmCopyContext context) { final SqmLiteral existing = context.getCopy( this ); @@ -104,6 +110,7 @@ else if ( value instanceof Enum enumValue ) { @Override public boolean equals(Object object) { return object instanceof SqmLiteral that + && getClass() == that.getClass() && Objects.equals( value, that.value ); } @@ -111,4 +118,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hashCode( value ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEmbeddableType.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEmbeddableType.java index c075147c4b8a..b4792dbb6443 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEmbeddableType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEmbeddableType.java @@ -100,4 +100,14 @@ public boolean equals(Object object) { public int hashCode() { return embeddableDomainType.getTypeName().hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java index 526d8b5af3a2..8fab96c4a413 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java @@ -111,4 +111,14 @@ public boolean equals(Object object) { public int hashCode() { return entityType.getName().hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralNull.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralNull.java index be753c6bc60b..6030fdf049d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralNull.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralNull.java @@ -65,4 +65,14 @@ public boolean equals(Object object) { public int hashCode() { return 1; } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmModifiedSubQueryExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmModifiedSubQueryExpression.java index 952e525908f8..211ef3c1c7be 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmModifiedSubQueryExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmModifiedSubQueryExpression.java @@ -11,7 +11,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.select.SqmSubQuery; -import java.util.Objects; /** * Represents a {@link Modifier#ALL}, {@link Modifier#ANY}, {@link Modifier#SOME} modifier applied to a subquery as @@ -95,11 +94,27 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmModifiedSubQueryExpression that && modifier == that.modifier - && Objects.equals( subQuery, that.subQuery ); + && subQuery.equals( that.subQuery ); } @Override public int hashCode() { - return Objects.hash( subQuery, modifier ); + int result = subQuery.hashCode(); + result = 31 * result + modifier.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmModifiedSubQueryExpression that + && modifier == that.modifier + && subQuery.isCompatible( that.subQuery ); + } + + @Override + public int cacheHashCode() { + int result = subQuery.cacheHashCode(); + result = 31 * result + modifier.hashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedExpression.java index 1f9bf5508f9f..acf8dfa0da97 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedExpression.java @@ -67,11 +67,27 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmNamedExpression that && Objects.equals( this.name, that.name ) - && Objects.equals( this.expression, that.expression ); + && this.expression.equals( that.expression ); } @Override public int hashCode() { - return Objects.hash( expression, name ); + int result = expression.hashCode(); + result = 31 * result + name.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmNamedExpression that + && Objects.equals( this.name, that.name ) + && this.expression.isCompatible( that.expression ); + } + + @Override + public int cacheHashCode() { + int result = expression.cacheHashCode(); + result = 31 * result + name.hashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java index b15d03637426..77965486e34a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java @@ -82,13 +82,6 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { hql.append( ':' ).append( getName() ); } - @Override - public int compareTo(SqmParameter parameter) { - return parameter instanceof SqmNamedParameter namedParameter - ? getName().compareTo( namedParameter.getName() ) - : -1; - } - @Override public boolean equals(Object object) { return object instanceof SqmNamedParameter that @@ -99,4 +92,14 @@ public boolean equals(Object object) { public int hashCode() { return name.hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java index 5514795f2a2d..860bc4bb0b11 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java @@ -5,7 +5,6 @@ package org.hibernate.query.sqm.tree.expression; import java.util.List; -import java.util.Objects; import org.hibernate.query.common.FrameExclusion; import org.hibernate.query.common.FrameKind; @@ -108,12 +107,28 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmOver sqmOver - && Objects.equals( expression, sqmOver.expression ) - && Objects.equals( window, sqmOver.window ); + && expression.equals( sqmOver.expression ) + && window.equals( sqmOver.window ); } @Override public int hashCode() { - return Objects.hash( expression, window ); + int result = expression.hashCode(); + result = 31 * result + window.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmOver sqmOver + && expression.isCompatible( sqmOver.expression ) + && window.isCompatible( sqmOver.window ); + } + + @Override + public int cacheHashCode() { + int result = expression.cacheHashCode(); + result = 31 * result + window.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOverflow.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOverflow.java index fcfb8b31df06..67ea61b5fc25 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOverflow.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOverflow.java @@ -4,7 +4,9 @@ */ package org.hibernate.query.sqm.tree.expression; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; @@ -16,7 +18,7 @@ public class SqmOverflow extends AbstractSqmExpression { private final SqmExpression separatorExpression; - private final SqmExpression fillerExpression; + private final @Nullable SqmExpression fillerExpression; private final boolean withCount; public SqmOverflow(SqmExpression separatorExpression, SqmExpression fillerExpression, boolean withCount) { @@ -84,12 +86,31 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmOverflow that && this.withCount == that.withCount - && Objects.equals( this.separatorExpression, that.separatorExpression ) + && this.separatorExpression.equals( that.separatorExpression ) && Objects.equals( this.fillerExpression, that.fillerExpression ); } @Override public int hashCode() { - return Objects.hash( separatorExpression, fillerExpression, withCount ); + int result = separatorExpression.hashCode(); + result = 31 * result + Objects.hashCode( fillerExpression ); + result = 31 * result + Boolean.hashCode( withCount ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmOverflow that + && this.withCount == that.withCount + && this.separatorExpression.isCompatible( that.separatorExpression ) + && SqmCacheable.areCompatible( this.fillerExpression, that.fillerExpression ); + } + + @Override + public int cacheHashCode() { + int result = separatorExpression.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( fillerExpression ); + result = 31 * result + Boolean.hashCode( withCount ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameter.java index 9263eab1c8e6..c62449f74133 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameter.java @@ -9,6 +9,8 @@ import org.hibernate.query.criteria.JpaParameterExpression; import org.hibernate.query.sqm.tree.SqmCopyContext; +import java.util.Comparator; + /** * Models a parameter expression declared in the query. * @@ -20,7 +22,23 @@ * * @author Steve Ebersole */ -public interface SqmParameter extends SqmExpression, JpaParameterExpression, Comparable> { +public interface SqmParameter extends SqmExpression, JpaParameterExpression { + Comparator> COMPARATOR = new Comparator<>() { + @Override + public int compare(SqmParameter o1, SqmParameter o2) { + if ( o1 instanceof SqmNamedParameter one ) { + return o2 instanceof SqmNamedParameter + ? one.getName().compareTo( o2.getName() ) + : -1; + } + else if ( o1 instanceof SqmPositionalParameter one ) { + return o2 instanceof SqmPositionalParameter + ? one.getPosition().compareTo( o2.getPosition() ) + : 1; + } + throw new HibernateException( "Unexpected SqmParameter type for comparison : " + this + " & " + o2 ); + } + }; /** * If this represents a named parameter, return that parameter name; * otherwise return {@code null}. @@ -69,27 +87,4 @@ public interface SqmParameter extends SqmExpression, JpaParameterExpressio @Override SqmParameter copy(SqmCopyContext context); - - /** - * @implSpec Defined as default since this is an SPI to - * support any previous extensions - */ - @Override - default int compareTo(SqmParameter parameter) { - if ( this instanceof SqmNamedParameter one ) { - return parameter instanceof SqmNamedParameter - ? one.getName().compareTo( parameter.getName() ) - : -1; - } - else if ( this instanceof SqmPositionalParameter one ) { - return parameter instanceof SqmPositionalParameter - ? one.getPosition().compareTo( parameter.getPosition() ) - : 1; - } - else if ( this instanceof SqmJpaCriteriaParameterWrapper - && parameter instanceof SqmJpaCriteriaParameterWrapper ) { - return Integer.compare( this.hashCode(), parameter.hashCode() ); - } - throw new HibernateException( "Unexpected SqmParameter type for comparison : " + this + " & " + parameter ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java index 30407b61fcbb..3e61f8f91821 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java @@ -11,7 +11,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.select.SqmSelectableNode; -import java.util.Objects; /** * Entity type expression based on a parameter - `TYPE( :someParam )` @@ -68,11 +67,22 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmParameterizedEntityType that - && Objects.equals( discriminatorSource, that.discriminatorSource ); + && discriminatorSource.equals( that.discriminatorSource ); } @Override public int hashCode() { return discriminatorSource.hashCode(); } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmParameterizedEntityType that + && discriminatorSource.isCompatible( that.discriminatorSource ); + } + + @Override + public int cacheHashCode() { + return discriminatorSource.cacheHashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPositionalParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPositionalParameter.java index b93b71c9e032..05edb56be1f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPositionalParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPositionalParameter.java @@ -84,13 +84,6 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { hql.append( getPosition() ); } - @Override - public int compareTo(SqmParameter parameter) { - return parameter instanceof SqmPositionalParameter positionalParameter - ? getPosition().compareTo( positionalParameter.getPosition() ) - : 1; - } - @Override public boolean equals(Object object) { return object instanceof SqmPositionalParameter that @@ -101,4 +94,14 @@ public boolean equals(Object object) { public int hashCode() { return position; } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSelfRenderingExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSelfRenderingExpression.java index 64297a29fa9c..8b03729d3e2b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSelfRenderingExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSelfRenderingExpression.java @@ -55,4 +55,14 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { // No equals() / hashCode() because this stuff is only // ever used internally and is irrelevant for caching, // so basing equality on the object identity is fine + + @Override + public boolean isCompatible(Object object) { + return this == object; + } + + @Override + public int cacheHashCode() { + return System.identityHashCode( this ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSetReturningFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSetReturningFunction.java index 28a99ae41b56..90ddd4f65ab5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSetReturningFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSetReturningFunction.java @@ -9,6 +9,7 @@ import org.hibernate.Incubating; import org.hibernate.query.criteria.JpaSetReturningFunction; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tuple.internal.AnonymousTupleType; import org.hibernate.query.sqm.NodeBuilder; @@ -97,17 +98,32 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { } @Override - // TODO: override on all subtypes - public boolean equals(Object other) { - return other instanceof SqmSetReturningFunction that - && Objects.equals( this.functionName, that.functionName ) - && Objects.equals( this.arguments, that.arguments ) + public boolean equals(Object object) { + return object instanceof SqmSetReturningFunction that && this.getClass() == that.getClass() - && Objects.equals( this.toHqlString(), that.toHqlString() ); + && this.functionName.equals( that.functionName ) + && Objects.equals( this.arguments, that.arguments ); } @Override public int hashCode() { - return Objects.hash( functionName, arguments, getClass() ); + int result = functionName.hashCode(); + result = 31 * result + Objects.hashCode( arguments ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmSetReturningFunction that + && this.getClass() == that.getClass() + && this.functionName.equals( that.functionName ) + && SqmCacheable.areCompatible( this.arguments, that.arguments ); + } + + @Override + public int cacheHashCode() { + int result = functionName.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( arguments ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmStar.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmStar.java index 5b1afef12212..8cf6dde65903 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmStar.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmStar.java @@ -37,12 +37,22 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { } @Override - public boolean equals(Object other) { - return other instanceof SqmStar; + public boolean equals(Object object) { + return object instanceof SqmStar; } @Override public int hashCode() { return 1; } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSummarization.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSummarization.java index 092c1e528781..9763941dc892 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSummarization.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSummarization.java @@ -6,9 +6,11 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; @@ -78,4 +80,31 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { hql.append( ')' ); } + @Override + public boolean equals(Object object) { + return object instanceof SqmSummarization that + && kind == that.kind + && Objects.equals( groupings, that.groupings ); + } + + @Override + public int hashCode() { + int result = kind.hashCode(); + result = 31 * result + Objects.hashCode( groupings ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmSummarization that + && kind == that.kind + && SqmCacheable.areCompatible( groupings, that.groupings ); + } + + @Override + public int cacheHashCode() { + int result = kind.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( groupings ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmToDuration.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmToDuration.java index 4f37b0550809..0f796cfa20ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmToDuration.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmToDuration.java @@ -10,7 +10,6 @@ import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; -import java.util.Objects; /** * @author Gavin King @@ -76,12 +75,28 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmToDuration that - && Objects.equals( magnitude, that.magnitude ) - && Objects.equals( unit, that.unit ); + && magnitude.equals( that.magnitude ) + && unit.equals( that.unit ); } @Override public int hashCode() { - return Objects.hash( magnitude, unit ); + int result = magnitude.hashCode(); + result = 31 * result + unit.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmToDuration that + && magnitude.isCompatible( that.magnitude ) + && unit.isCompatible( that.unit ); + } + + @Override + public int cacheHashCode() { + int result = magnitude.cacheHashCode(); + result = 31 * result + unit.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTrimSpecification.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTrimSpecification.java index 075fb5eab6cb..033877c49d3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTrimSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTrimSpecification.java @@ -68,4 +68,14 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hashCode( specification ); } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTuple.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTuple.java index a7fa33d4daec..e8a3df93a452 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTuple.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTuple.java @@ -15,6 +15,7 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmBindableType; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.select.SqmJpaCompoundSelection; @@ -106,6 +107,17 @@ public int hashCode() { return Objects.hashCode( groupedExpressions ); } + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmTuple that + && SqmCacheable.areCompatible( this.groupedExpressions, that.groupedExpressions ); + } + + @Override + public int cacheHashCode() { + return SqmCacheable.cacheHashCode( groupedExpressions ); + } + @Override public String asLoggableText() { return toString(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmUnaryOperation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmUnaryOperation.java index a600b96e7f20..51a94bf8f3f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmUnaryOperation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmUnaryOperation.java @@ -85,4 +85,32 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { hql.append( operation.getOperatorChar() ); operand.appendHqlString( hql, context ); } + + @Override + public boolean equals(Object object) { + return object instanceof SqmUnaryOperation that + && operation == that.getOperation() + && operand.equals( that.getOperand() ); + } + + @Override + public int hashCode() { + int result = operation.hashCode(); + result = 31 * result + operand.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmUnaryOperation that + && operation == that.getOperation() + && operand.isCompatible( that.getOperand() ); + } + + @Override + public int cacheHashCode() { + int result = operation.hashCode(); + result = 31 * result + operand.cacheHashCode(); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmWindow.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmWindow.java index 92c3a8d0e7ca..a873698c91ce 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmWindow.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmWindow.java @@ -17,6 +17,7 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.tree.AbstractSqmNode; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmVisitableNode; @@ -290,10 +291,8 @@ private static void renderFrameKind(StringBuilder sb, FrameKind kind, SqmExpress @Override public boolean equals(Object object) { - if ( !(object instanceof SqmWindow sqmWindow) ) { - return false; - } - return Objects.equals( partitions, sqmWindow.partitions ) + return object instanceof SqmWindow sqmWindow + && Objects.equals( partitions, sqmWindow.partitions ) && Objects.equals( orderList, sqmWindow.orderList ) && mode == sqmWindow.mode && startKind == sqmWindow.startKind @@ -305,7 +304,40 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hash( partitions, orderList, mode, startKind, startExpression, endKind, endExpression, - exclusion ); + int result = Objects.hashCode( partitions ); + result = 31 * result + Objects.hashCode( orderList ); + result = 31 * result + mode.hashCode(); + result = 31 * result + startKind.hashCode(); + result = 31 * result + Objects.hashCode( startExpression ); + result = 31 * result + endKind.hashCode(); + result = 31 * result + Objects.hashCode( endExpression ); + result = 31 * result + exclusion.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmWindow sqmWindow + && SqmCacheable.areCompatible( partitions, sqmWindow.partitions ) + && SqmCacheable.areCompatible( orderList, sqmWindow.orderList ) + && mode == sqmWindow.mode + && startKind == sqmWindow.startKind + && SqmCacheable.areCompatible( startExpression, sqmWindow.startExpression ) + && endKind == sqmWindow.endKind + && SqmCacheable.areCompatible( endExpression, sqmWindow.endExpression ) + && exclusion == sqmWindow.exclusion; + } + + @Override + public int cacheHashCode() { + int result = SqmCacheable.cacheHashCode( partitions ); + result = 31 * result + SqmCacheable.cacheHashCode( orderList ); + result = 31 * result + mode.hashCode(); + result = 31 * result + startKind.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( startExpression ); + result = 31 * result + endKind.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( endExpression ); + result = 31 * result + exclusion.hashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmWindowFrame.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmWindowFrame.java index 6e44be624cf8..0315dd1d486c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmWindowFrame.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmWindowFrame.java @@ -54,4 +54,32 @@ public SqmWindowFrame copy(SqmCopyContext context) { ) ); } + + @Override + public boolean equals(Object object) { + return object instanceof SqmWindowFrame that + && kind == that.kind + && expression.equals( that.expression ); + } + + @Override + public int hashCode() { + int result = kind.hashCode(); + result = 31 * result + expression.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmWindowFrame that + && kind == that.kind + && expression.isCompatible( that.expression ); + } + + @Override + public int cacheHashCode() { + int result = kind.hashCode(); + result = 31 * result + expression.cacheHashCode(); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlAttributesExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlAttributesExpression.java index a4ce67750b06..2362723db7da 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlAttributesExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlAttributesExpression.java @@ -6,11 +6,13 @@ import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; import org.hibernate.Incubating; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmBindableType; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; @@ -92,4 +94,26 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { } hql.append( ')' ); } + + @Override + public boolean equals(Object object) { + return object instanceof SqmXmlAttributesExpression that + && Objects.equals( attributes, that.attributes ); + } + + @Override + public int hashCode() { + return Objects.hashCode( attributes ); + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmXmlAttributesExpression that + && SqmCacheable.areCompatible( attributes, that.attributes ); + } + + @Override + public int cacheHashCode() { + return SqmCacheable.cacheHashCode( attributes ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlTableFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlTableFunction.java index 55dde0def8a5..4e88cbe0172f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlTableFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlTableFunction.java @@ -11,6 +11,7 @@ import org.hibernate.query.criteria.JpaXmlTableColumnNode; import org.hibernate.query.criteria.JpaXmlTableFunction; import org.hibernate.query.sqm.SqmBindableType; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tuple.internal.AnonymousTupleType; import org.hibernate.query.sqm.NodeBuilder; @@ -38,6 +39,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -188,7 +190,21 @@ private void checkTypeResolved() { } } - sealed interface ColumnDefinition { + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmXmlTableFunction that + && super.isCompatible( object ) + && columns.isCompatible( that.columns ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + columns.cacheHashCode(); + return result; + } + + sealed interface ColumnDefinition extends SqmCacheable { String name(); @@ -284,6 +300,24 @@ public String name() { return name; } + @Override + public boolean isCompatible(Object object) { + return object instanceof QueryColumnDefinition that + && name.equals( that.name ) + && type.equals( that.type ) + && Objects.equals( xpath, that.xpath ) + && SqmCacheable.areCompatible( defaultExpression, that.defaultExpression ); + } + + @Override + public int cacheHashCode() { + int result = name.hashCode(); + result = 31 * result + type.hashCode(); + result = 31 * result + Objects.hashCode( xpath ); + result = 31 * result + SqmCacheable.cacheHashCode( defaultExpression ); + return result; + } + } static final class ValueColumnDefinition implements ColumnDefinition, JpaXmlTableColumnNode { @@ -370,6 +404,24 @@ public String name() { return name; } + @Override + public boolean isCompatible(Object object) { + return object instanceof ValueColumnDefinition that + && name.equals( that.name ) + && type.equals( that.type ) + && Objects.equals( xpath, that.xpath ) + && SqmCacheable.areCompatible( defaultExpression, that.defaultExpression ); + } + + @Override + public int cacheHashCode() { + int result = name.hashCode(); + result = 31 * result + type.hashCode(); + result = 31 * result + Objects.hashCode( xpath ); + result = 31 * result + SqmCacheable.cacheHashCode( defaultExpression ); + return result; + } + } record OrdinalityColumnDefinition(String name, BasicType type) implements ColumnDefinition { @@ -395,6 +447,16 @@ public int populateTupleType(int offset, String[] componentNames, SqmExpressible componentTypes[offset] = type; return 1; } + + @Override + public boolean isCompatible(Object object) { + return equals( object ); + } + + @Override + public int cacheHashCode() { + return hashCode(); + } } public static final class Columns implements SqmTypedNode { @@ -484,5 +546,27 @@ else if ( columnDefinition instanceof SqmXmlTableFunction.QueryColumnDefinition // No-op since this object is going to be visible as function argument return null; } + + @Override + public boolean equals(Object object) { + return object instanceof Columns that + && Objects.equals( columnDefinitions, that.columnDefinitions ); + } + + @Override + public int hashCode() { + return Objects.hashCode( columnDefinitions ); + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof Columns that + && SqmCacheable.areCompatible( columnDefinitions, that.columnDefinitions ); + } + + @Override + public int cacheHashCode() { + return SqmCacheable.cacheHashCode( columnDefinitions ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java index 551041b1fbc1..be1d6842b813 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java @@ -10,6 +10,7 @@ import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; +import java.util.Objects; /** @@ -53,18 +54,38 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { SqmLiteral.appendHqlString( hql, getJavaTypeDescriptor(), value ); } - @Override - public int compareTo(SqmParameter parameter) { - return Integer.compare( hashCode(), parameter.hashCode() ); - } + // For caching purposes, any two ValueBindJpaCriteriaParameter objects are compatible as ensured by the parent impl, + // but for equals/hashCode, use equals/hashCode of the underlying value, if available, from the nodes JavaType @Override - public boolean equals(Object object) { - return this == object; + public final boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o instanceof ValueBindJpaCriteriaParameter that ) { + if ( value == null ) { + return that.value == null && Objects.equals( getNodeType(), that.getNodeType() ); + } + final var javaType = getJavaTypeDescriptor(); + if ( that.value != null ) { + if ( javaType != null ) { + //noinspection unchecked + return javaType.equals( that.getJavaTypeDescriptor() ) && javaType.areEqual( value, (T) that.value ); + } + else { + return that.getJavaTypeDescriptor() == null && value.equals( that.value ); + } + } + } + return false; } @Override public int hashCode() { - return super.hashCode(); + if ( value == null ) { + return 0; + } + final var javaType = getJavaTypeDescriptor(); + return javaType == null ? value.hashCode() : javaType.extractHashCode( value ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java index e6a1f6e9bfc8..ee6a6b0fcada 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java @@ -213,15 +213,4 @@ public SqmTreatedCrossJoin treatAs(EntityDomainType treatAsType return treatAs( treatAsType, null ); } - @Override - public boolean equals(Object object) { - return object instanceof SqmCrossJoin - && super.equals( object ); - } - - @Override - // needed to make static code analyzer happy - public int hashCode() { - return super.hashCode(); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java index 3af9c4b780d9..4a28cdefb8c7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java @@ -160,13 +160,29 @@ public JoinType getJoinType() { @Override public boolean equals(Object object) { - return object instanceof SqmCteJoin that - && super.equals( object ) - && Objects.equals( this.cte.getName(), that.cte.getName() ); + return super.equals( object ) + && object instanceof SqmCteJoin that + && Objects.equals( this.cte.getCteTable().getCteName(), that.cte.getCteTable().getCteName() ); } @Override public int hashCode() { - return Objects.hash( super.hashCode(), cte.getName() ); + int result = super.hashCode(); + result = 31 * result + cte.getCteTable().getCteName().hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return super.isCompatible( object ) + && object instanceof SqmCteJoin that + && Objects.equals( this.cte.getCteTable().getCteName(), that.cte.getCteTable().getCteName() ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + cte.getCteTable().getCteName().hashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java index 484adb55a12d..824499b67b42 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java @@ -27,7 +27,6 @@ import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.Predicate; -import java.util.Objects; /** * @author Christian Beikov @@ -222,14 +221,29 @@ public JoinType getJoinType() { @Override public boolean equals(Object object) { - return object instanceof SqmDerivedJoin that - && super.equals( object ) - && this.lateral == that.lateral - && Objects.equals( this.subQuery, that.subQuery ); + return super.equals( object ) + && object instanceof SqmDerivedJoin that + && subQuery.equals( that.subQuery ); } @Override public int hashCode() { - return Objects.hash( super.hashCode(), subQuery, lateral ); + int result = super.hashCode(); + result = 31 * result + subQuery.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return super.isCompatible( object ) + && object instanceof SqmDerivedJoin that + && subQuery.isCompatible( that.subQuery ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + subQuery.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java index ddb8fb741793..463ee03b5c33 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java @@ -30,8 +30,6 @@ import jakarta.persistence.criteria.Predicate; import jakarta.persistence.metamodel.EntityType; -import java.util.Objects; - /** * @author Steve Ebersole */ @@ -229,16 +227,4 @@ public SqmEntityJoin makeCopy(SqmCreationProcessingState creationProcessing pathRegistry.findFromByPath( getRoot().getNavigablePath() ) ); } - - @Override - public boolean equals(Object other) { - return other instanceof SqmEntityJoin that - && Objects.equals( this.getEntityName(), that.getEntityName() ) - && super.equals( other ); - } - - @Override - public int hashCode() { - return Objects.hash( super.hashCode(), getEntityName() ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFromClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFromClause.java index e28e92b05825..16c44a181a27 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFromClause.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFromClause.java @@ -10,6 +10,8 @@ import java.util.Objects; import java.util.function.Consumer; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; @@ -26,7 +28,7 @@ * * @author Steve Ebersole */ -public class SqmFromClause implements Serializable { +public class SqmFromClause implements Serializable, SqmCacheable { private List> domainRoots; public SqmFromClause() { @@ -192,46 +194,144 @@ public static void appendTreatJoins(SqmFrom sqmFrom, StringBuilder sb, Sqm public boolean equals(Object object) { return object instanceof SqmFromClause that && this.getNumberOfRoots() == that.getNumberOfRoots() - // calling equals() here leads to circularity, - // and so we need to flatten out the comparison + // The equals() method for SqmFrom does not do a deep check, + // so this method must check the full structure now to check compatibility && equalRoots( this.getRoots(), that.getRoots() ); } - // both lists must be the same size private boolean equalRoots(List> theseRoots, List> thoseRoots) { for ( int i = 0; i < theseRoots.size(); i++ ) { var thisRoot = theseRoots.get( i ); var thatRoot = thoseRoots.get( i ); - if ( !Objects.equals( thisRoot.getEntityName(), thatRoot.getEntityName() ) - || !Objects.equals( thisRoot.getExplicitAlias(), thatRoot.getExplicitAlias() ) - || !Objects.equals( thisRoot, thatRoot ) // needed for SqmDerivedRoots - || thisRoot.getNumberOfJoins() != thatRoot.getNumberOfJoins() - || !equalsJoins( thisRoot.getSqmJoins(), thatRoot.getSqmJoins() ) ) { + if ( !thisRoot.equals( thatRoot ) + || !equalOrderedJoins( thisRoot.getOrderedJoins(), thatRoot.getOrderedJoins() ) + || !equalJoins( thisRoot.getSqmJoins(), thatRoot.getSqmJoins() ) ) { return false; } } return true; } - private boolean equalsJoins(List> theseJoins, List> thoseJoins) { + private boolean equalOrderedJoins(@Nullable List> theseJoins, @Nullable List> thoseJoins) { + if ( theseJoins == null ) { + return thoseJoins == null; + } + else if ( thoseJoins == null || theseJoins.size() != thoseJoins.size() ) { + return false; + } for ( int i = 0; i < theseJoins.size(); i++ ) { var thisJoin = theseJoins.get( i ); var thatJoin = thoseJoins.get( i ); - if ( !Objects.equals( thisJoin.getNavigablePath(), thatJoin.getNavigablePath() ) - || !Objects.equals( thisJoin.getExplicitAlias(), thatJoin.getExplicitAlias() ) - || !Objects.equals( thisJoin.getJoinType(), thatJoin.getJoinType() ) - || !Objects.equals( thisJoin, thatJoin ) // needed for SqmDerivedRoots - || thisJoin.getNumberOfJoins() != thatJoin.getNumberOfJoins() + if ( !thisJoin.equals( thatJoin ) + || !joinKindsEqual( thisJoin, thatJoin ) + || !Objects.equals( thisJoin.getOn(), thatJoin.getOn() ) ) { + return false; + } + } + return true; + } + + private boolean equalJoins(List> theseJoins, List> thoseJoins) { + if ( theseJoins.size() != thoseJoins.size() ) { + return false; + } + for ( int i = 0; i < theseJoins.size(); i++ ) { + var thisJoin = theseJoins.get( i ); + var thatJoin = thoseJoins.get( i ); + if ( !thisJoin.equals( thatJoin ) + || !joinKindsEqual( thisJoin, thatJoin ) || !Objects.equals( thisJoin.getOn(), thatJoin.getOn() ) - || !equalsJoins( thisJoin.getSqmJoins(), thatJoin.getSqmJoins() ) ) { + || !equalJoins( thisJoin.getSqmJoins(), thatJoin.getSqmJoins() ) ) { return false; } } return true; } + private static boolean joinKindsEqual(SqmJoin thisJoin, SqmJoin thatJoin) { + if ( !Objects.equals( thisJoin.getJoinType(), thatJoin.getJoinType() ) ) { + return false; + } + if ( thisJoin instanceof SqmAttributeJoin thisAttributeJoin + && thisAttributeJoin.isFetched() != ((SqmAttributeJoin) thatJoin).isFetched() ) { + return false; + } + if ( thisJoin instanceof SqmFunctionJoin thisFunctionJoin + && thisFunctionJoin.isLateral() != ((SqmFunctionJoin) thatJoin).isLateral() ) { + return false; + } + if ( thisJoin instanceof SqmDerivedJoin thisDerivedJoin + && thisDerivedJoin.isLateral() != ((SqmDerivedJoin) thatJoin).isLateral() ) { + return false; + } + return true; + } + @Override public int hashCode() { return getNumberOfRoots(); } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmFromClause that + && this.getNumberOfRoots() == that.getNumberOfRoots() + // The isCompatible() method for SqmFrom does not do a deep check, + // so this method must check the full structure now to check compatibility + && compatibleRoots( this.getRoots(), that.getRoots() ); + } + + private boolean compatibleRoots(List> theseRoots, List> thoseRoots) { + for ( int i = 0; i < theseRoots.size(); i++ ) { + var thisRoot = theseRoots.get( i ); + var thatRoot = thoseRoots.get( i ); + if ( !thisRoot.isCompatible( thatRoot ) + || !compatibleOrderedJoins( thisRoot.getOrderedJoins(), thatRoot.getOrderedJoins() ) + || !compatibleJoins( thisRoot.getSqmJoins(), thatRoot.getSqmJoins() ) ) { + return false; + } + } + return true; + } + + private boolean compatibleOrderedJoins(@Nullable List> theseJoins, @Nullable List> thoseJoins) { + if ( theseJoins == null ) { + return thoseJoins == null; + } + else if ( thoseJoins == null || theseJoins.size() != thoseJoins.size() ) { + return false; + } + for ( int i = 0; i < theseJoins.size(); i++ ) { + var thisJoin = theseJoins.get( i ); + var thatJoin = thoseJoins.get( i ); + if ( !thisJoin.isCompatible( thatJoin ) + || !joinKindsEqual( thisJoin, thatJoin ) + || !SqmCacheable.areCompatible( thisJoin.getOn(), thatJoin.getOn() ) ) { + return false; + } + } + return true; + } + + private boolean compatibleJoins(List> theseJoins, List> thoseJoins) { + if ( theseJoins.size() != thoseJoins.size() ) { + return false; + } + for ( int i = 0; i < theseJoins.size(); i++ ) { + var thisJoin = theseJoins.get( i ); + var thatJoin = thoseJoins.get( i ); + if ( !thisJoin.isCompatible( thatJoin ) + || !joinKindsEqual( thisJoin, thatJoin ) + || !SqmCacheable.areCompatible( thisJoin.getOn(), thatJoin.getOn() ) + || !compatibleJoins( thisJoin.getSqmJoins(), thatJoin.getSqmJoins() ) ) { + return false; + } + } + return true; + } + + @Override + public int cacheHashCode() { + return getNumberOfRoots(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFunctionJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFunctionJoin.java index e84fe08513e1..08f75a9a419c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFunctionJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFunctionJoin.java @@ -27,7 +27,6 @@ import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.Predicate; -import java.util.Objects; /** * @author Christian Beikov @@ -231,14 +230,29 @@ public JoinType getJoinType() { @Override public boolean equals(Object object) { - return object instanceof SqmFunctionJoin that - && super.equals( object ) - && this.lateral == that.lateral - && Objects.equals( this.function, that.function ); + return super.equals( object ) + && object instanceof SqmFunctionJoin that + && function.equals( that.function ); } @Override public int hashCode() { - return Objects.hash( super.hashCode(), function, lateral ); + int result = super.hashCode(); + result = 31 * result + function.cacheHashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return super.isCompatible( object ) + && object instanceof SqmFunctionJoin that + && function.isCompatible( that.function ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + function.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java index df1fa1324568..50f7be76e056 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Internal; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.sqm.TreatException; @@ -110,7 +111,7 @@ public boolean isAllowJoins() { return allowJoins; } - public List> getOrderedJoins() { + public @Nullable List> getOrderedJoins() { return orderedJoins; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java index 482f04daba0e..7d8b47038318 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java @@ -9,6 +9,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.function.Consumer; @@ -19,6 +20,7 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.tree.AbstractSqmDmlStatement; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; @@ -197,4 +199,36 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { hql.append( ')' ); } } + + @Override + public boolean equals(Object object) { + return object instanceof AbstractSqmInsertStatement that + && super.equals( that ) + && Objects.equals( getInsertionTargetPaths(), that.getInsertionTargetPaths() ) + && Objects.equals( getConflictClause(), that.getConflictClause() ); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + Objects.hashCode( getInsertionTargetPaths() ); + result = 31 * result + Objects.hashCode( getConflictClause() ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof AbstractSqmInsertStatement that + && super.isCompatible( that ) + && SqmCacheable.areCompatible( getInsertionTargetPaths(), that.getInsertionTargetPaths() ) + && SqmCacheable.areCompatible( getConflictClause(), that.getConflictClause() ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( getInsertionTargetPaths() ); + result = 31 * result + SqmCacheable.cacheHashCode( getConflictClause() ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java index dbbcb6ea2455..5adfefbf6a69 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java @@ -14,6 +14,7 @@ import org.hibernate.query.criteria.JpaConflictUpdateAction; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmVisitableNode; @@ -217,7 +218,7 @@ private static void appendUnqualifiedPath(StringBuilder sb, SqmPath path) { @Override public boolean equals(Object object) { return object instanceof SqmConflictClause that - && Objects.equals( excludedRoot, that.excludedRoot ) + && excludedRoot.equals( that.excludedRoot ) && Objects.equals( constraintName, that.constraintName ) && Objects.equals( constraintPaths, that.constraintPaths ) && Objects.equals( updateAction, that.updateAction ); @@ -225,6 +226,28 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hash( excludedRoot, constraintName, constraintPaths, updateAction ); + int result = excludedRoot.hashCode(); + result = 31 * result + Objects.hashCode( constraintName ); + result = 31 * result + Objects.hashCode( constraintPaths ); + result = 31 * result + Objects.hashCode( updateAction ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmConflictClause that + && excludedRoot.isCompatible( that.excludedRoot ) + && Objects.equals( constraintName, that.constraintName ) + && SqmCacheable.areCompatible( constraintPaths, that.constraintPaths ) + && SqmCacheable.areCompatible( updateAction, that.updateAction ); + } + + @Override + public int cacheHashCode() { + int result = excludedRoot.cacheHashCode(); + result = 31 * result + Objects.hashCode( constraintName ); + result = 31 * result + SqmCacheable.cacheHashCode( constraintPaths ); + result = 31 * result + SqmCacheable.cacheHashCode( updateAction ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictUpdateAction.java index 93543750dbd8..8dd17850cd29 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictUpdateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictUpdateAction.java @@ -6,6 +6,7 @@ import org.hibernate.query.criteria.JpaConflictUpdateAction; import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmNode; import org.hibernate.query.sqm.tree.SqmRenderContext; @@ -23,6 +24,8 @@ import jakarta.persistence.metamodel.SingularAttribute; import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.Objects; + import static org.hibernate.query.sqm.internal.TypecheckUtil.assertAssignable; /** @@ -166,4 +169,32 @@ public void appendHqlString(StringBuilder sb, SqmRenderContext context) { whereClause.getPredicate().appendHqlString( sb, context ); } } + + @Override + public boolean equals(Object object) { + return object instanceof SqmConflictUpdateAction that + && setClause.equals( that.getSetClause() ) + && Objects.equals( whereClause, that.getWhereClause() ); + } + + @Override + public int hashCode() { + int result = setClause.hashCode(); + result = 31 * result + Objects.hashCode( whereClause ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmConflictUpdateAction that + && setClause.isCompatible( that.getSetClause() ) + && SqmCacheable.areCompatible( whereClause, that.getWhereClause() ); + } + + @Override + public int cacheHashCode() { + int result = setClause.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( whereClause ); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java index 1fdbcecfd475..97a84c046fbb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import org.hibernate.Incubating; @@ -176,18 +175,29 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { - if ( !(object instanceof SqmInsertSelectStatement that) ) { - return false; - } - return Objects.equals( selectQueryPart, that.selectQueryPart ) - && Objects.equals( this.getTarget(), that.getTarget() ) - && Objects.equals( this.getInsertionTargetPaths(), that.getInsertionTargetPaths() ) - && Objects.equals( this.getConflictClause(), that.getConflictClause() ) - && Objects.equals( this.getCteStatements(), that.getCteStatements() ); + return object instanceof SqmInsertSelectStatement that + && super.equals( that ) + && selectQueryPart.equals( that.selectQueryPart ); } @Override public int hashCode() { - return Objects.hash( selectQueryPart, getTarget(), getInsertionTargetPaths(), getConflictClause(), getCteStatements() ); + int result = super.hashCode(); + result = 31 * result + selectQueryPart.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmInsertSelectStatement that + && super.isCompatible( that ) + && selectQueryPart.isCompatible( that.selectQueryPart ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + selectQueryPart.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java index da4a7c61b604..75f2129668c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java @@ -19,6 +19,7 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmQuerySource; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.cte.SqmCteStatement; @@ -212,18 +213,29 @@ private static void appendValues(SqmValues sqmValues, StringBuilder sb, SqmRende @Override public boolean equals(Object object) { - if ( !(object instanceof SqmInsertValuesStatement that) ) { - return false; - } - return Objects.equals( valuesList, that.valuesList ) - && Objects.equals( this.getTarget(), that.getTarget() ) - && Objects.equals( this.getInsertionTargetPaths(), that.getInsertionTargetPaths() ) - && Objects.equals( this.getConflictClause(), that.getConflictClause() ) - && Objects.equals( this.getCteStatements(), that.getCteStatements() ); + return object instanceof SqmInsertValuesStatement that + && super.equals( that ) + && Objects.equals( valuesList, that.valuesList ); } @Override public int hashCode() { - return Objects.hash( valuesList, getTarget(), getInsertionTargetPaths(), getConflictClause(), getCteStatements() ); + int result = super.hashCode(); + result = 31 * result + Objects.hashCode( valuesList ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmInsertValuesStatement that + && super.isCompatible( that ) + && SqmCacheable.areCompatible( valuesList, that.valuesList ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( valuesList ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmValues.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmValues.java index c396ecb2988a..0ead35ad5ae6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmValues.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmValues.java @@ -5,6 +5,7 @@ package org.hibernate.query.sqm.tree.insert; import org.hibernate.query.criteria.JpaValues; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; @@ -12,11 +13,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; /** * @author Gavin King */ -public class SqmValues implements JpaValues, Serializable { +public class SqmValues implements JpaValues, Serializable, SqmCacheable { private final List> expressions; public SqmValues(List> expressions) { @@ -38,4 +40,26 @@ public SqmValues copy(SqmCopyContext context) { public List> getExpressions() { return Collections.unmodifiableList( expressions ); } + + @Override + public boolean equals(Object object) { + return object instanceof SqmValues that + && Objects.equals( expressions, that.expressions ); + } + + @Override + public int hashCode() { + return Objects.hashCode( expressions ); + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmValues that + && SqmCacheable.areCompatible( expressions, that.expressions ); + } + + @Override + public int cacheHashCode() { + return SqmCacheable.cacheHashCode( expressions ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/ParameterCollector.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/ParameterCollector.java index ea302c20026a..c3f882e838a4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/ParameterCollector.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/ParameterCollector.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Set; import java.util.function.Consumer; @@ -66,6 +67,8 @@ private ParameterCollector(Consumer> consumer) { private Set> parameterExpressions; private final Consumer> consumer; + private int criteriaParameterId; + private IdentityHashMap, Integer> unnamedParameterIdMap; @Override public Object visitPositionalParameterExpression(SqmPositionalParameter expression) { @@ -88,10 +91,28 @@ public Object visitNamedParameterExpression(SqmNamedParameter expression) { */ @Override public SqmJpaCriteriaParameterWrapper visitJpaCriteriaParameter(JpaCriteriaParameter expression) { + final int unnamedParameterId; + if ( expression.getName() == null ) { + if ( unnamedParameterIdMap == null ) { + unnamedParameterIdMap = new IdentityHashMap<>(); + } + final var index = unnamedParameterIdMap.get( expression ); + if ( index == null ) { + unnamedParameterIdMap.put( expression, unnamedParameterId = unnamedParameterIdMap.size() ); + } + else { + unnamedParameterId = index; + } + } + else { + unnamedParameterId = -1; + } return visitParameter( new SqmJpaCriteriaParameterWrapper<>( getInferredParameterType( expression ), expression, + criteriaParameterId++, + unnamedParameterId, expression.nodeBuilder() ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/AbstractNegatableSqmPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/AbstractNegatableSqmPredicate.java index fc4554b35ad7..a2d278653440 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/AbstractNegatableSqmPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/AbstractNegatableSqmPredicate.java @@ -7,7 +7,6 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SqmBindableType; -import java.util.Objects; /** * @author Steve Ebersole @@ -46,19 +45,4 @@ public SqmNegatablePredicate not() { // a new instance. return createNegatedNode(); } - - @Override - // for safety only, overridden on all subtypes - public boolean equals(Object other) { - return other instanceof AbstractNegatableSqmPredicate that - && this.negated == that.negated - && this.getClass() == that.getClass() - && Objects.equals( this.toHqlString(), that.toHqlString() ); - } - - @Override - // for safety only, overridden on all subtypes - public int hashCode() { - return Objects.hash( getClass(), negated ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBetweenPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBetweenPredicate.java index 4e88fd7650ad..2637c683d5c0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBetweenPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBetweenPredicate.java @@ -12,7 +12,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; -import java.util.Objects; import static org.hibernate.query.sqm.internal.TypecheckUtil.assertComparable; @@ -102,14 +101,36 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmBetweenPredicate that && this.isNegated() == that.isNegated() - && Objects.equals( expression, that.expression ) - && Objects.equals( lowerBound, that.lowerBound ) - && Objects.equals( upperBound, that.upperBound ); + && expression.equals( that.expression ) + && lowerBound.equals( that.lowerBound ) + && upperBound.equals( that.upperBound ); } @Override public int hashCode() { - return Objects.hash( isNegated(), expression, lowerBound, upperBound ); + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + expression.hashCode(); + result = 31 * result + lowerBound.hashCode(); + result = 31 * result + upperBound.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmBetweenPredicate that + && this.isNegated() == that.isNegated() + && expression.isCompatible( that.expression ) + && lowerBound.isCompatible( that.lowerBound ) + && upperBound.isCompatible( that.upperBound ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + expression.cacheHashCode(); + result = 31 * result + lowerBound.cacheHashCode(); + result = 31 * result + upperBound.cacheHashCode(); + return result; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBooleanExpressionPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBooleanExpressionPredicate.java index fa627320e1f9..d5db0d66fdb5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBooleanExpressionPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmBooleanExpressionPredicate.java @@ -6,7 +6,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; @@ -87,12 +86,28 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmBooleanExpressionPredicate that && this.isNegated() == that.isNegated() - && Objects.equals( this.booleanExpression, that.booleanExpression ); + && this.booleanExpression.equals( that.booleanExpression ); } @Override public int hashCode() { - return Objects.hash( booleanExpression, isNegated() ); + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + booleanExpression.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmBooleanExpressionPredicate that + && this.isNegated() == that.isNegated() + && this.booleanExpression.isCompatible( that.booleanExpression ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + booleanExpression.cacheHashCode(); + return result; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmComparisonPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmComparisonPredicate.java index be50c0a0f913..a2e25776e25a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmComparisonPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmComparisonPredicate.java @@ -13,7 +13,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; -import java.util.Objects; import static org.hibernate.query.sqm.internal.TypecheckUtil.assertComparable; @@ -124,12 +123,34 @@ public boolean equals(Object object) { return object instanceof SqmComparisonPredicate that && this.isNegated() == that.isNegated() && this.operator == that.operator - && Objects.equals( this.leftHandExpression, that.leftHandExpression ) - && Objects.equals( this.rightHandExpression, that.rightHandExpression ); + && this.leftHandExpression.equals( that.leftHandExpression ) + && this.rightHandExpression.equals( that.rightHandExpression ); } @Override public int hashCode() { - return Objects.hash( isNegated(), operator, leftHandExpression, rightHandExpression ); + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + operator.hashCode(); + result = 31 * result + leftHandExpression.hashCode(); + result = 31 * result + rightHandExpression.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmComparisonPredicate that + && this.isNegated() == that.isNegated() + && this.operator == that.operator + && this.leftHandExpression.isCompatible( that.leftHandExpression ) + && this.rightHandExpression.isCompatible( that.rightHandExpression ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + operator.hashCode(); + result = 31 * result + leftHandExpression.cacheHashCode(); + result = 31 * result + rightHandExpression.cacheHashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmEmptinessPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmEmptinessPredicate.java index 912ee0538c22..2c7e05032006 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmEmptinessPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmEmptinessPredicate.java @@ -10,7 +10,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; -import java.util.Objects; /** * @author Steve Ebersole @@ -68,12 +67,28 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmEmptinessPredicate that && this.isNegated() == that.isNegated() - && Objects.equals( pluralPath, that.pluralPath ); + && pluralPath.equals( that.pluralPath ); } @Override public int hashCode() { - return Objects.hash( isNegated(), pluralPath ); + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + pluralPath.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmEmptinessPredicate that + && this.isNegated() == that.isNegated() + && pluralPath.isCompatible( that.pluralPath ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + pluralPath.cacheHashCode(); + return result; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmExistsPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmExistsPredicate.java index f99af9155593..116aa31caca0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmExistsPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmExistsPredicate.java @@ -10,7 +10,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; -import java.util.Objects; /** * @author Gavin King @@ -76,12 +75,28 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmExistsPredicate that && this.isNegated() == that.isNegated() - && Objects.equals( this.expression, that.expression ); + && this.expression.equals( that.expression ); } @Override public int hashCode() { - return Objects.hash( isNegated(), expression ); + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + expression.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmExistsPredicate that + && this.isNegated() == that.isNegated() + && this.expression.isCompatible( that.expression ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + expression.cacheHashCode(); + return result; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmGroupedPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmGroupedPredicate.java index e970d82e5f57..b45185ede22b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmGroupedPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmGroupedPredicate.java @@ -6,7 +6,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; @@ -79,11 +78,22 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmGroupedPredicate that - && Objects.equals( subPredicate, that.subPredicate ); + && subPredicate.equals( that.subPredicate ); } @Override public int hashCode() { - return Objects.hashCode( subPredicate ); + return subPredicate.hashCode(); + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmGroupedPredicate that + && subPredicate.isCompatible( that.subPredicate ); + } + + @Override + public int cacheHashCode() { + return subPredicate.cacheHashCode(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java index 70eccf33b105..33456c919981 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java @@ -14,6 +14,7 @@ import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; @@ -166,13 +167,32 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmInListPredicate that && this.isNegated() == that.isNegated() - && Objects.equals( this.testExpression, that.testExpression ) + && this.testExpression.equals( that.testExpression ) && Objects.equals( this.listExpressions, that.listExpressions ); } @Override public int hashCode() { - return Objects.hash( isNegated(), testExpression, listExpressions ); + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + testExpression.hashCode(); + result = 31 * result + Objects.hashCode( listExpressions ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmInListPredicate that + && this.isNegated() == that.isNegated() + && this.testExpression.isCompatible( that.testExpression ) + && SqmCacheable.areCompatible( this.listExpressions, that.listExpressions ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + testExpression.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( listExpressions ); + return result; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java index 22f9a3912437..591487d3b94a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java @@ -16,7 +16,6 @@ import jakarta.persistence.criteria.Expression; -import java.util.Objects; import static org.hibernate.query.sqm.internal.TypecheckUtil.assertComparable; @@ -131,12 +130,31 @@ protected SqmNegatablePredicate createNegatedNode() { public boolean equals(Object object) { return object instanceof SqmInSubQueryPredicate that && this.isNegated() == that.isNegated() - && Objects.equals( this.testExpression, that.testExpression ) - && Objects.equals( this.subQueryExpression, that.subQueryExpression ); + && this.testExpression.equals( that.testExpression ) + && this.subQueryExpression.equals( that.subQueryExpression ); } @Override public int hashCode() { - return Objects.hash( testExpression, subQueryExpression, isNegated() ); + int result = testExpression.hashCode(); + result = 31 * result + subQueryExpression.hashCode(); + result = 31 * result + Boolean.hashCode( isNegated() ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmInSubQueryPredicate that + && this.isNegated() == that.isNegated() + && this.testExpression.isCompatible( that.testExpression ) + && this.subQueryExpression.isCompatible( that.subQueryExpression ); + } + + @Override + public int cacheHashCode() { + int result = testExpression.cacheHashCode(); + result = 31 * result + subQueryExpression.cacheHashCode(); + result = 31 * result + Boolean.hashCode( isNegated() ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmJunctionPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmJunctionPredicate.java index 5f494cfc2ca5..1503ef2ae754 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmJunctionPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmJunctionPredicate.java @@ -11,6 +11,7 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmBindableType; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import jakarta.persistence.criteria.Expression; @@ -138,15 +139,29 @@ private void appendJunctionHqlString(SqmPredicate p, StringBuilder sb, SqmRender @Override public boolean equals(Object object) { - if ( !(object instanceof SqmJunctionPredicate that) ) { - return false; - } - return booleanOperator == that.booleanOperator + return object instanceof SqmJunctionPredicate that + && booleanOperator == that.booleanOperator && Objects.equals( predicates, that.predicates ); } @Override public int hashCode() { - return Objects.hash( booleanOperator, predicates ); + int result = booleanOperator.hashCode(); + result = 31 * result + Objects.hashCode( predicates ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmJunctionPredicate that + && booleanOperator == that.booleanOperator + && SqmCacheable.areCompatible( predicates, that.predicates ); + } + + @Override + public int cacheHashCode() { + int result = booleanOperator.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( predicates ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmLikePredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmLikePredicate.java index 978bca76ba4d..10e54eb832b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmLikePredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmLikePredicate.java @@ -4,14 +4,17 @@ */ package org.hibernate.query.sqm.tree.predicate; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmBindableType; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; + import java.util.Objects; import static org.hibernate.query.sqm.internal.TypecheckUtil.assertString; @@ -22,13 +25,13 @@ public class SqmLikePredicate extends AbstractNegatableSqmPredicate { private final SqmExpression matchExpression; private final SqmExpression pattern; - private final SqmExpression escapeCharacter; + private final @Nullable SqmExpression escapeCharacter; private final boolean isCaseSensitive; public SqmLikePredicate( SqmExpression matchExpression, SqmExpression pattern, - SqmExpression escapeCharacter, + @Nullable SqmExpression escapeCharacter, NodeBuilder nodeBuilder) { this( matchExpression, pattern, escapeCharacter, false, nodeBuilder ); } @@ -36,7 +39,7 @@ public SqmLikePredicate( public SqmLikePredicate( SqmExpression matchExpression, SqmExpression pattern, - SqmExpression escapeCharacter, + @Nullable SqmExpression escapeCharacter, boolean negated, NodeBuilder nodeBuilder) { this( matchExpression, pattern, escapeCharacter, negated, true, nodeBuilder ); @@ -45,7 +48,7 @@ public SqmLikePredicate( public SqmLikePredicate( SqmExpression matchExpression, SqmExpression pattern, - SqmExpression escapeCharacter, + @Nullable SqmExpression escapeCharacter, boolean negated, boolean isCaseSensitive, NodeBuilder nodeBuilder) { @@ -115,7 +118,7 @@ public SqmExpression getPattern() { return pattern; } - public SqmExpression getEscapeCharacter() { + public @Nullable SqmExpression getEscapeCharacter() { return escapeCharacter; } @@ -147,14 +150,39 @@ public boolean equals(Object object) { return object instanceof SqmLikePredicate that && this.isNegated() == that.isNegated() && isCaseSensitive == that.isCaseSensitive - && Objects.equals( this.matchExpression, that.matchExpression ) - && Objects.equals( pattern, that.pattern ) + && matchExpression.equals( that.matchExpression ) + && pattern.equals( that.pattern ) && Objects.equals( escapeCharacter, that.escapeCharacter ); } @Override public int hashCode() { - return Objects.hash( isNegated(), matchExpression, pattern, escapeCharacter, isCaseSensitive ); + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + matchExpression.hashCode(); + result = 31 * result + pattern.hashCode(); + result = 31 * result + Objects.hashCode( escapeCharacter ); + result = 31 * result + Boolean.hashCode( isCaseSensitive ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmLikePredicate that + && this.isNegated() == that.isNegated() + && isCaseSensitive == that.isCaseSensitive + && matchExpression.isCompatible( that.matchExpression ) + && pattern.isCompatible( that.pattern ) + && SqmCacheable.areCompatible( escapeCharacter, that.escapeCharacter ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + matchExpression.cacheHashCode(); + result = 31 * result + pattern.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( escapeCharacter ); + result = 31 * result + Boolean.hashCode( isCaseSensitive ); + return result; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmMemberOfPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmMemberOfPredicate.java index de97db5e2128..4df9364d3a21 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmMemberOfPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmMemberOfPredicate.java @@ -14,7 +14,6 @@ import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; import org.hibernate.query.sqm.tree.expression.SqmExpression; -import java.util.Objects; import static org.hibernate.query.sqm.internal.TypecheckUtil.areTypesComparable; @@ -101,13 +100,32 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmMemberOfPredicate that && this.isNegated() == that.isNegated() - && Objects.equals( leftHandExpression, that.leftHandExpression ) - && Objects.equals( pluralPath, that.pluralPath ); + && leftHandExpression.equals( that.leftHandExpression ) + && pluralPath.equals( that.pluralPath ); } @Override public int hashCode() { - return Objects.hash( isNegated(), leftHandExpression, pluralPath ); + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + leftHandExpression.hashCode(); + result = 31 * result + pluralPath.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmMemberOfPredicate that + && this.isNegated() == that.isNegated() + && leftHandExpression.isCompatible( that.leftHandExpression ) + && pluralPath.isCompatible( that.pluralPath ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + leftHandExpression.cacheHashCode(); + result = 31 * result + pluralPath.cacheHashCode(); + return result; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmNegatedPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmNegatedPredicate.java index 4779363bbaea..b66f4ef742db 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmNegatedPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmNegatedPredicate.java @@ -6,7 +6,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; @@ -18,19 +17,11 @@ /** * @author Steve Ebersole */ -public class SqmNegatedPredicate extends AbstractNegatableSqmPredicate { +public class SqmNegatedPredicate extends AbstractSqmPredicate { private final SqmPredicate wrappedPredicate; public SqmNegatedPredicate(SqmPredicate wrappedPredicate, NodeBuilder nodeBuilder) { - super( nodeBuilder ); - this.wrappedPredicate = wrappedPredicate; - } - - public SqmNegatedPredicate( - SqmPredicate wrappedPredicate, - boolean negated, - NodeBuilder nodeBuilder) { - super( negated, nodeBuilder ); + super( nodeBuilder.getBooleanType(), nodeBuilder ); this.wrappedPredicate = wrappedPredicate; } @@ -44,7 +35,6 @@ public SqmNegatedPredicate copy(SqmCopyContext context) { this, new SqmNegatedPredicate( wrappedPredicate.copy( context ), - isNegated(), nodeBuilder() ) ); @@ -78,17 +68,32 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmNegatedPredicate that - && Objects.equals( wrappedPredicate, that.wrappedPredicate ); + && wrappedPredicate.equals( that.wrappedPredicate ); } @Override public int hashCode() { - return Objects.hash( wrappedPredicate ); + return wrappedPredicate.hashCode(); } @Override - protected SqmNegatablePredicate createNegatedNode() { - return new SqmNegatedPredicate( this, nodeBuilder() ); + public boolean isCompatible(Object object) { + return object instanceof SqmNegatedPredicate that + && wrappedPredicate.isCompatible( that.wrappedPredicate ); + } + + @Override + public int cacheHashCode() { + return wrappedPredicate.cacheHashCode(); + } + + @Override + public boolean isNegated() { + return true; } + @Override + public SqmPredicate not() { + return new SqmNegatedPredicate( this, nodeBuilder() ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmNullnessPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmNullnessPredicate.java index 74eabd0121b3..b33334c5b8e5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmNullnessPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmNullnessPredicate.java @@ -10,7 +10,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; -import java.util.Objects; /** * @author Steve Ebersole @@ -69,12 +68,28 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof SqmNullnessPredicate that && this.isNegated() == that.isNegated() - && Objects.equals( this.expression, that.expression ); + && this.expression.equals( that.expression ); } @Override public int hashCode() { - return Objects.hash( isNegated(), expression ); + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + expression.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmNullnessPredicate that + && this.isNegated() == that.isNegated() + && this.expression.isCompatible( that.expression ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + expression.cacheHashCode(); + return result; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmTruthnessPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmTruthnessPredicate.java index 61ba80f84c5f..d4e67e8029ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmTruthnessPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmTruthnessPredicate.java @@ -10,7 +10,6 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; -import java.util.Objects; /** * @author Gavin King @@ -72,12 +71,31 @@ public boolean equals(Object object) { return object instanceof SqmTruthnessPredicate that && this.isNegated() == that.isNegated() && this.value == that.value - && Objects.equals( this.expression, that.expression ); + && this.expression.equals( that.expression ); } @Override public int hashCode() { - return Objects.hash( isNegated(), value, expression ); + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + expression.hashCode(); + result = 31 * result + Boolean.hashCode( value ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmTruthnessPredicate that + && this.isNegated() == that.isNegated() + && this.value == that.value + && this.expression.isCompatible( that.expression ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( isNegated() ); + result = 31 * result + expression.cacheHashCode(); + result = 31 * result + Boolean.hashCode( value ); + return result; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmWhereClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmWhereClause.java index 92cd9abe03ed..244bfade5b97 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmWhereClause.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmWhereClause.java @@ -8,12 +8,13 @@ import java.util.Objects; import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; /** * @author Steve Ebersole */ -public class SqmWhereClause implements SqmPredicateCollection { +public class SqmWhereClause implements SqmPredicateCollection, SqmCacheable { private final NodeBuilder nodeBuilder; private SqmPredicate predicate; @@ -79,6 +80,17 @@ public boolean equals(Object other) { @Override public int hashCode() { - return predicate == null ? 0 : predicate.hashCode(); + return Objects.hashCode( this.predicate ); + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmWhereClause that + && SqmCacheable.areCompatible( this.predicate, that.predicate ); + } + + @Override + public int cacheHashCode() { + return SqmCacheable.cacheHashCode( this.predicate ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java index e3d6165dd25c..6c068514e2e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java @@ -22,6 +22,7 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.spi.SqmCreationHelper; import org.hibernate.query.sqm.tree.AbstractSqmNode; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.cte.SqmCteStatement; @@ -443,13 +444,30 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { public boolean equals(Object object) { return object instanceof AbstractSqmSelectQuery that && Objects.equals( this.resultType, that.resultType ) // for performance! - && Objects.equals( this.sqmQueryPart, that.sqmQueryPart ) + && this.sqmQueryPart.equals( that.sqmQueryPart ) && Objects.equals( this.cteStatements, that.cteStatements ); } @Override public int hashCode() { - return Objects.hash( cteStatements, sqmQueryPart ); + int result = Objects.hashCode( cteStatements ); + result = 31 * result + sqmQueryPart.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof AbstractSqmSelectQuery that + && Objects.equals( this.resultType, that.resultType ) // for performance! + && this.sqmQueryPart.isCompatible( that.sqmQueryPart ) + && SqmCacheable.areCompatible( this.cteStatements, that.cteStatements ); + } + + @Override + public int cacheHashCode() { + int result = SqmCacheable.cacheHashCode( cteStatements ); + result = 31 * result + sqmQueryPart.cacheHashCode(); + return result; } @SuppressWarnings("unchecked") diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java index 99fffdbcc038..12ce1c3faa0b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java @@ -16,6 +16,7 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmBindableType; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.domain.SqmDomainType; @@ -305,7 +306,23 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hash( instantiationTarget, arguments ); + int result = instantiationTarget.hashCode(); + result = 31 * result + Objects.hashCode( arguments ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmDynamicInstantiation that + && Objects.equals( instantiationTarget, that.instantiationTarget ) + && SqmCacheable.areCompatible( arguments, that.arguments ); + } + + @Override + public int cacheHashCode() { + int result = instantiationTarget.hashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( arguments ); + return result; } @SuppressWarnings("unused") diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiationArgument.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiationArgument.java index a6c733504f82..ad3deae6fe2b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiationArgument.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiationArgument.java @@ -69,12 +69,28 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmDynamicInstantiationArgument that - && Objects.equals( selectableNode, that.selectableNode ) + && selectableNode.equals( that.selectableNode ) && Objects.equals( alias, that.alias ); } @Override public int hashCode() { - return Objects.hash( selectableNode, alias ); + int result = selectableNode.hashCode(); + result = 31 * result + Objects.hashCode( alias ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmDynamicInstantiationArgument that + && selectableNode.isCompatible( that.selectableNode ) + && Objects.equals( alias, that.alias ); + } + + @Override + public int cacheHashCode() { + int result = selectableNode.cacheHashCode(); + result = 31 * result + Objects.hashCode( alias ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java index af12a56e9b71..c1050cc00cba 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java @@ -14,6 +14,7 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmExpressible; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.domain.SqmDomainType; @@ -123,22 +124,58 @@ public X accept(SemanticQueryWalker walker) { @Override public void appendHqlString(StringBuilder hql, SqmRenderContext context) { - selectableNodes.get( 0 ).appendHqlString( hql, context ); + appendSelectableNode( hql, context, selectableNodes.get( 0 ) ); for ( int i = 1; i < selectableNodes.size(); i++ ) { hql.append(", "); - selectableNodes.get( i ).appendHqlString( hql, context ); + appendSelectableNode( hql, context, selectableNodes.get( i ) ); + } + } + + private static void appendSelectableNode(StringBuilder hql, SqmRenderContext context, SqmSelectableNode sqmSelectableNode) { + sqmSelectableNode.appendHqlString( hql, context ); + if ( sqmSelectableNode.getAlias() != null ) { + hql.append( " as " ).append( sqmSelectableNode.getAlias() ); } } @Override public boolean equals(Object object) { - return object instanceof SqmJpaCompoundSelection that - && Objects.equals( selectableNodes, that.selectableNodes ); + if ( !(object instanceof SqmJpaCompoundSelection that) + || selectableNodes.size() != that.selectableNodes.size() ) { + return false; + } + for ( SqmSelectableNode selectableNode : selectableNodes ) { + if ( !selectableNode.equals( that ) + || !Objects.equals( selectableNode.getAlias(), that.getAlias() ) ) { + return false; + } + } + return true; } @Override public int hashCode() { - return Objects.hash( selectableNodes ); + return Objects.hashCode( selectableNodes ); + } + + @Override + public boolean isCompatible(Object object) { + if ( !(object instanceof SqmJpaCompoundSelection that) + || selectableNodes.size() != that.selectableNodes.size() ) { + return false; + } + for ( SqmSelectableNode selectableNode : selectableNodes ) { + if ( !selectableNode.isCompatible( that ) + || !Objects.equals( selectableNode.getAlias(), that.getAlias() ) ) { + return false; + } + } + return true; + } + + @Override + public int cacheHashCode() { + return SqmCacheable.cacheHashCode( selectableNodes ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmOrderByClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmOrderByClause.java index 5f1afd24c123..2db5a49e2fc4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmOrderByClause.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmOrderByClause.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Objects; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef; import org.hibernate.query.sqm.tree.expression.SqmExpression; @@ -19,7 +20,7 @@ /** * @author Steve Ebersole */ -public class SqmOrderByClause implements Serializable { +public class SqmOrderByClause implements Serializable, SqmCacheable { private boolean hasPositionalSortItem; private List sortSpecifications; @@ -95,6 +96,17 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hash( sortSpecifications ); + return Objects.hashCode( sortSpecifications ); + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmOrderByClause that + && SqmCacheable.areCompatible( this.sortSpecifications, that.sortSpecifications ); + } + + @Override + public int cacheHashCode() { + return SqmCacheable.cacheHashCode( sortSpecifications ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryGroup.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryGroup.java index b3a73133d3c7..6c28886c0409 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryGroup.java @@ -18,6 +18,7 @@ import org.hibernate.query.criteria.JpaQueryGroup; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmTypedNode; @@ -257,6 +258,25 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hash( super.hashCode(), queryParts, setOperator ); + int result = super.hashCode(); + result = 31 * result + Objects.hashCode( queryParts ); + result = 31 * result + setOperator.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmQueryGroup that + && super.isCompatible( that ) + && this.setOperator == that.setOperator + && SqmCacheable.areCompatible( this.queryParts, that.queryParts ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( queryParts ); + result = 31 * result + setOperator.hashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryPart.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryPart.java index 15a71af2e65c..4f1d1577c74b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryPart.java @@ -12,6 +12,7 @@ import org.hibernate.query.criteria.JpaOrder; import org.hibernate.query.criteria.JpaQueryPart; import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.SqmVisitableNode; @@ -206,6 +207,7 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public boolean equals(Object object) { return object instanceof SqmQueryPart that + && getClass() == that.getClass() && Objects.equals( orderByClause, that.orderByClause ) && Objects.equals( offsetExpression, that.offsetExpression ) && Objects.equals( fetchExpression, that.fetchExpression ) @@ -214,6 +216,29 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hash( orderByClause, offsetExpression, fetchExpression, fetchClauseType ); + int result = Objects.hashCode( orderByClause ); + result = 31 * result + Objects.hashCode( offsetExpression ); + result = 31 * result + Objects.hashCode( fetchExpression ); + result = 31 * result + fetchClauseType.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmQueryPart that + && getClass() == that.getClass() + && SqmCacheable.areCompatible( orderByClause, that.orderByClause ) + && SqmCacheable.areCompatible( offsetExpression, that.offsetExpression ) + && SqmCacheable.areCompatible( fetchExpression, that.fetchExpression ) + && fetchClauseType == that.fetchClauseType; + } + + @Override + public int cacheHashCode() { + int result = SqmCacheable.cacheHashCode( orderByClause ); + result = 31 * result + SqmCacheable.cacheHashCode( offsetExpression ); + result = 31 * result + SqmCacheable.cacheHashCode( fetchExpression ); + result = 31 * result + fetchClauseType.hashCode(); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java index a6f2fbf6dd00..35ebf502b445 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java @@ -27,6 +27,7 @@ import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmNode; import org.hibernate.query.sqm.tree.SqmRenderContext; @@ -710,20 +711,46 @@ private boolean isSameOrParent(NavigablePath navigablePath, List } @Override - public boolean equals(Object other) { - return other instanceof SqmQuerySpec that - && super.equals( other ) - && Objects.equals( this.fromClause, that.fromClause ) - && Objects.equals( this.selectClause, that.selectClause ) - && Objects.equals( this.whereClause, that.whereClause ) - && Objects.equals( this.groupByClauseExpressions, that.groupByClauseExpressions ) - && Objects.equals( this.havingClausePredicate, that.havingClausePredicate ); + public boolean equals(Object object) { + return object instanceof SqmQuerySpec that + && super.equals( object ) + && Objects.equals( fromClause, that.fromClause ) + && Objects.equals( selectClause, that.selectClause ) + && Objects.equals( whereClause, that.whereClause ) + && Objects.equals( groupByClauseExpressions, that.groupByClauseExpressions ) + && Objects.equals( havingClausePredicate, that.havingClausePredicate ); } @Override public int hashCode() { - return Objects.hash( super.hashCode(), - fromClause, selectClause, whereClause, - groupByClauseExpressions, havingClausePredicate ); + int result = super.hashCode(); + result = 31 * result + Objects.hashCode( fromClause ); + result = 31 * result + Objects.hashCode( selectClause ); + result = 31 * result + Objects.hashCode( whereClause ); + result = 31 * result + Objects.hashCode( groupByClauseExpressions ); + result = 31 * result + Objects.hashCode( havingClausePredicate ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmQuerySpec that + && super.isCompatible( object ) + && SqmCacheable.areCompatible( fromClause, that.fromClause ) + && SqmCacheable.areCompatible( selectClause, that.selectClause ) + && SqmCacheable.areCompatible( whereClause, that.whereClause ) + && SqmCacheable.areCompatible( groupByClauseExpressions, that.groupByClauseExpressions ) + && SqmCacheable.areCompatible( havingClausePredicate, that.havingClausePredicate ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + SqmCacheable.cacheHashCode( fromClause ); + result = 31 * result + SqmCacheable.cacheHashCode( selectClause ); + result = 31 * result + SqmCacheable.cacheHashCode( whereClause ); + result = 31 * result + SqmCacheable.cacheHashCode( groupByClauseExpressions ); + result = 31 * result + SqmCacheable.cacheHashCode( havingClausePredicate ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectClause.java index b622be205d3a..5813ba60ce9c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectClause.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectClause.java @@ -11,6 +11,7 @@ import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.tree.AbstractSqmNode; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.type.descriptor.java.JavaType; @@ -154,6 +155,22 @@ public boolean equals(Object other) { @Override public int hashCode() { - return Objects.hash( distinct, selections ); + int result = Boolean.hashCode( distinct ); + result = 31 * result + Objects.hashCode( selections ); + return result; + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmSelectClause that + && distinct == that.distinct + && SqmCacheable.areCompatible( this.selections, that.selections ); + } + + @Override + public int cacheHashCode() { + int result = Boolean.hashCode( distinct ); + result = 31 * result + SqmCacheable.cacheHashCode( selections ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelection.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelection.java index 6aab5965c738..f96d838f781d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelection.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelection.java @@ -78,16 +78,31 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { } } - @Override public boolean equals(Object object) { return object instanceof SqmSelection that - && Objects.equals( this.selectableNode, that.selectableNode ) - && Objects.equals( this.alias, that.alias ); + && selectableNode.equals( that.selectableNode ) + && Objects.equals( alias, that.alias ); } @Override public int hashCode() { - return Objects.hash( selectableNode, alias ); + int result = selectableNode.hashCode(); + result = 31 * result + Objects.hashCode( alias ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmSelection that + && selectableNode.isCompatible( that.selectableNode ) + && Objects.equals( alias, that.alias ); + } + + @Override + public int cacheHashCode() { + int result = selectableNode.cacheHashCode(); + result = 31 * result + Objects.hashCode( alias ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java index e7c836595903..a618744d2bee 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSortSpecification.java @@ -10,6 +10,7 @@ import org.hibernate.query.SortDirection; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaOrder; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; @@ -19,7 +20,7 @@ /** * @author Steve Ebersole */ -public class SqmSortSpecification implements JpaOrder { +public class SqmSortSpecification implements JpaOrder, SqmCacheable { @SuppressWarnings("rawtypes") private final SqmExpression sortExpression; private final SortDirection sortOrder; @@ -142,18 +143,33 @@ else if ( nullPrecedence != null ) { @Override public boolean equals(Object other) { - if ( this == other ) { - return true; - } - // used in SqmInterpretationsKey.equals() return other instanceof SqmSortSpecification that - && Objects.equals( this.sortExpression, that.sortExpression ) + && sortExpression.equals( that.sortExpression ) && this.sortOrder == that.sortOrder && this.nullPrecedence == that.nullPrecedence; } @Override public int hashCode() { - return Objects.hash( sortExpression, sortOrder, nullPrecedence ); + int result = sortExpression.hashCode(); + result = 31 * result + sortOrder.hashCode(); + result = 31 * result + Objects.hashCode( nullPrecedence ); + return result; + } + + @Override + public boolean isCompatible(Object other) { + return other instanceof SqmSortSpecification that + && sortExpression.isCompatible( that.sortExpression ) + && this.sortOrder == that.sortOrder + && this.nullPrecedence == that.nullPrecedence; + } + + @Override + public int cacheHashCode() { + int result = sortExpression.cacheHashCode(); + result = 31 * result + sortOrder.hashCode(); + result = 31 * result + Objects.hashCode( nullPrecedence ); + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java index c47f330a5870..9f7b869f16b3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java @@ -785,7 +785,23 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hash( super.hashCode(), alias ); + int result = super.hashCode(); + result = 31 * result + Objects.hashCode( alias ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmSubQuery that + && Objects.equals( this.alias, that.alias ) + && super.isCompatible( object ); + } + + @Override + public int cacheHashCode() { + int result = super.cacheHashCode(); + result = 31 * result + Objects.hashCode( alias ); + return result; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmAssignment.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmAssignment.java index 8fdd14907652..1b5a561c6029 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmAssignment.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmAssignment.java @@ -4,6 +4,7 @@ */ package org.hibernate.query.sqm.tree.update; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.expression.SqmExpression; @@ -11,7 +12,7 @@ /** * @author Steve Ebersole */ -public class SqmAssignment { +public class SqmAssignment implements SqmCacheable { private final SqmPath targetPath; private final SqmExpression value; @@ -38,4 +39,32 @@ public SqmPath getTargetPath() { public SqmExpression getValue() { return value; } + + @Override + public boolean equals(Object object) { + return object instanceof SqmAssignment that + && targetPath.equals( that.targetPath ) + && value.equals( that.value ); + } + + @Override + public int hashCode() { + int result = targetPath.hashCode(); + result = 31 * result + value.hashCode(); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmAssignment that + && targetPath.isCompatible( that.targetPath ) + && value.isCompatible( that.value ); + } + + @Override + public int cacheHashCode() { + int result = targetPath.cacheHashCode(); + result = 31 * result + value.cacheHashCode(); + return result; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmSetClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmSetClause.java index 6b7c3f6f7c40..c31f7ad5aae5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmSetClause.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmSetClause.java @@ -7,7 +7,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.domain.SqmPath; @@ -16,7 +18,7 @@ /** * @author Steve Ebersole */ -public class SqmSetClause { +public class SqmSetClause implements SqmCacheable { private final List> assignments; public SqmSetClause() { @@ -61,4 +63,26 @@ private static void appendAssignment(SqmAssignment sqmAssignment, StringBuild sb.append( " = " ); sqmAssignment.getValue().appendHqlString( sb, context ); } + + @Override + public boolean equals(Object object) { + return object instanceof SqmSetClause that + && Objects.equals( assignments, that.assignments ); + } + + @Override + public int hashCode() { + return Objects.hashCode( assignments ); + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmSetClause that + && SqmCacheable.areCompatible( assignments, that.assignments ); + } + + @Override + public int cacheHashCode() { + return SqmCacheable.cacheHashCode( assignments ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java index 727ff25e00e7..525f24f58089 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java @@ -23,6 +23,7 @@ import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder; import org.hibernate.query.sqm.tree.AbstractSqmRestrictedDmlStatement; +import org.hibernate.query.sqm.tree.SqmCacheable; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.SqmRenderContext; @@ -292,18 +293,34 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { } @Override - public boolean equals(Object node) { - return node instanceof SqmUpdateStatement that - && super.equals( node ) + public boolean equals(Object object) { + return object instanceof SqmUpdateStatement that + && super.equals( that ) && this.versioned == that.versioned - && Objects.equals( this.setClause, that.setClause ) - && Objects.equals( this.getTarget(), that.getTarget() ) - && Objects.equals( this.getWhereClause(), that.getWhereClause() ) - && Objects.equals( this.getCteStatements(), that.getCteStatements() ); + && Objects.equals( setClause, that.setClause ); } @Override public int hashCode() { - return Objects.hash( versioned, setClause, getTarget(), getWhereClause(), getCteStatements() ); + int result = getTarget().hashCode(); + result = 31 * result + Boolean.hashCode( versioned ); + result = 31 * result + Objects.hashCode( setClause ); + return result; + } + + @Override + public boolean isCompatible(Object object) { + return object instanceof SqmUpdateStatement that + && super.isCompatible( that ) + && this.versioned == that.versioned + && SqmCacheable.areCompatible( setClause, that.setClause ); + } + + @Override + public int cacheHashCode() { + int result = getTarget().cacheHashCode(); + result = 31 * result + Boolean.hashCode( versioned ); + result = 31 * result + SqmCacheable.cacheHashCode( setClause ); + return result; } } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java index 0e86826aab89..48bda455ef94 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java @@ -61,8 +61,8 @@ public boolean hasStringAttribute() { @Override public String getAttributeDeclarationString() { - final TreeSet> sortedParameters = - new TreeSet<>( select.getSqmParameters() ); + final TreeSet> sortedParameters = new TreeSet<>( SqmParameter.COMPARATOR ); + sortedParameters.addAll( select.getSqmParameters() ); StringBuilder declaration = new StringBuilder(); comment( declaration ); modifiers( declaration );