Skip to content

Commit 180bb04

Browse files
committed
HHH-17002, HHH-18820, HHH-19391, HHH-18514 equals() and hashCode() for SQM nodes
- finally enables efficient caching of criteria query plans - also reconsider how alias generation is done - aliases should only be unique to a given query, NOT globally unique, since that results in interpretation cache misses - ran into and fixed several other problems along the way - note that the previous solution based on translating to HQL was not working at all, partly because the translation to HQL is not very correct - but anyway this is more efficient, since hashCodes are in general more flexible from an efficiency perspective - there is still a remaining problem where NavigablePaths elements are assigned globally unique aliases resulting in cache misses
1 parent 6dc2e7b commit 180bb04

File tree

134 files changed

+1911
-437
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

134 files changed

+1911
-437
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/function/array/H2UnnestFunction.java

-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ public H2UnnestFunction(int maximumArraySize) {
6363
protected <T> SelfRenderingSqmSetReturningFunction<T> generateSqmSetReturningFunctionExpression(
6464
List<? extends SqmTypedNode<?>> arguments,
6565
QueryEngine queryEngine) {
66-
//noinspection unchecked
6766
return new SelfRenderingSqmSetReturningFunction<>(
6867
this,
6968
this,

hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPath.java

+14
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import org.hibernate.query.sqm.tree.domain.SqmPath;
1414
import org.hibernate.spi.NavigablePath;
1515

16+
import java.util.Objects;
17+
1618
public class AnyDiscriminatorSqmPath<T> extends AbstractSqmPath<T> implements DiscriminatorSqmPath<T> {
1719

1820
protected AnyDiscriminatorSqmPath(
@@ -45,4 +47,16 @@ public <X> X accept(SemanticQueryWalker<X> walker) {
4547
public AnyDiscriminatorSqmPathSource<T> getExpressible() {
4648
return (AnyDiscriminatorSqmPathSource<T>) getNodeType();
4749
}
50+
51+
52+
@Override
53+
public boolean equals(Object object) {
54+
return object instanceof AnyDiscriminatorSqmPath<?> that
55+
&& Objects.equals( this.getLhs(), that.getLhs() );
56+
}
57+
58+
@Override
59+
public int hashCode() {
60+
return getLhs().hashCode();
61+
}
4862
}

hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedDiscriminatorSqmPath.java

+13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import org.hibernate.query.sqm.tree.domain.SqmPath;
1515
import org.hibernate.spi.NavigablePath;
1616

17+
import java.util.Objects;
18+
1719
/**
1820
* {@link SqmPath} specialization for an embeddable discriminator
1921
*
@@ -59,4 +61,15 @@ public EmbeddedDiscriminatorSqmPath<T> copy(SqmCopyContext context) {
5961
public <X> X accept(SemanticQueryWalker<X> walker) {
6062
return walker.visitDiscriminatorPath( this );
6163
}
64+
65+
@Override
66+
public boolean equals(Object object) {
67+
return object instanceof EmbeddedDiscriminatorSqmPath<?> that
68+
&& Objects.equals( this.getLhs(), that.getLhs() );
69+
}
70+
71+
@Override
72+
public int hashCode() {
73+
return getLhs().hashCode();
74+
}
6275
}

hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java

+15-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import org.hibernate.query.sqm.tree.domain.SqmEntityDomainType;
1818
import org.hibernate.spi.NavigablePath;
1919

20+
import java.util.Objects;
21+
2022
/**
2123
* {@link SqmPath} specialization for an entity discriminator
2224
*
@@ -66,10 +68,19 @@ public EntityDiscriminatorSqmPath copy(SqmCopyContext context) {
6668

6769
@Override
6870
public <X> X accept(SemanticQueryWalker<X> walker) {
69-
if ( ! entityDescriptor.hasSubclasses() ) {
70-
return walker.visitEntityTypeLiteralExpression( new SqmLiteralEntityType( entityDomainType, nodeBuilder() ) );
71-
}
71+
return entityDescriptor.hasSubclasses()
72+
? walker.visitDiscriminatorPath( this )
73+
: walker.visitEntityTypeLiteralExpression( new SqmLiteralEntityType( entityDomainType, nodeBuilder() ) );
74+
}
7275

73-
return walker.visitDiscriminatorPath( this );
76+
@Override
77+
public boolean equals(Object object) {
78+
return object instanceof EntityDiscriminatorSqmPath<?> that
79+
&& Objects.equals( this.getLhs(), that.getLhs() );
80+
}
81+
82+
@Override
83+
public int hashCode() {
84+
return getLhs().hashCode();
7485
}
7586
}

hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathTerminal.java

+8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.math.BigInteger;
99
import java.util.Collection;
1010
import java.util.List;
11+
import java.util.Objects;
1112
import java.util.function.Function;
1213

1314
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -163,6 +164,13 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) {
163164
hql.append( getLocalName() );
164165
}
165166

167+
@Override
168+
public boolean equals(Object node) {
169+
return node instanceof FullyQualifiedReflectivePathTerminal<?> that
170+
&& Objects.equals( getParent().getFullPath(), that.getParent().getFullPath() )
171+
&& Objects.equals( getLocalName(), that.getLocalName() );
172+
}
173+
166174
@Override
167175
public SqmExpression<Long> asLong() {
168176
return null;

hibernate-core/src/main/java/org/hibernate/query/hql/internal/QuerySplitter.java

+14-18
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
*/
55
package org.hibernate.query.hql.internal;
66

7-
import java.util.Set;
8-
97
import org.hibernate.metamodel.model.domain.EntityDomainType;
108
import org.hibernate.query.sqm.tree.SqmCopyContext;
119
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
@@ -30,24 +28,24 @@ public static <R> SqmSelectStatement<R>[] split(SqmSelectStatement<R> statement)
3028
// We only allow unmapped polymorphism in a very restricted way. Specifically,
3129
// the unmapped polymorphic reference can only be a root and can be the only
3230
// root. Use that restriction to locate the unmapped polymorphic reference
33-
final SqmRoot<?> unmappedPolymorphicReference = findUnmappedPolymorphicReference( statement.getQueryPart() );
34-
31+
final SqmRoot<?> unmappedPolymorphicReference =
32+
findUnmappedPolymorphicReference( statement.getQueryPart() );
3533
if ( unmappedPolymorphicReference == null ) {
3634
@SuppressWarnings("unchecked")
3735
SqmSelectStatement<R>[] sqmSelectStatement = new SqmSelectStatement[] { statement };
3836
return sqmSelectStatement;
3937
}
4038

41-
final SqmPolymorphicRootDescriptor<R> unmappedPolymorphicDescriptor = (SqmPolymorphicRootDescriptor<R>) unmappedPolymorphicReference.getReferencedPathSource();
42-
final Set<EntityDomainType<? extends R>> implementors = unmappedPolymorphicDescriptor.getImplementors();
39+
final var unmappedPolymorphicDescriptor =
40+
(SqmPolymorphicRootDescriptor<R>)
41+
unmappedPolymorphicReference.getReferencedPathSource();
42+
var implementors = unmappedPolymorphicDescriptor.getImplementors();
4343
@SuppressWarnings("unchecked")
4444
final SqmSelectStatement<R>[] expanded = new SqmSelectStatement[ implementors.size() ];
45-
4645
int i = 0;
4746
for ( EntityDomainType<?> mappedDescriptor : implementors ) {
4847
expanded[i++] = copyStatement( statement, unmappedPolymorphicReference, mappedDescriptor );
4948
}
50-
5149
return expanded;
5250
}
5351

@@ -97,31 +95,29 @@ public static <R> SqmDeleteStatement<R>[] split(SqmDeleteStatement<R> statement)
9795
// We only allow unmapped polymorphism in a very restricted way. Specifically,
9896
// the unmapped polymorphic reference can only be a root and can be the only
9997
// root. Use that restriction to locate the unmapped polymorphic reference
100-
final SqmRoot<?> unmappedPolymorphicReference = findUnmappedPolymorphicReference( statement );
101-
98+
final SqmRoot<?> unmappedPolymorphicReference =
99+
findUnmappedPolymorphicReference( statement );
102100
if ( unmappedPolymorphicReference == null ) {
103101
@SuppressWarnings("unchecked")
104102
SqmDeleteStatement<R>[] sqmDeleteStatement = new SqmDeleteStatement[] { statement };
105103
return sqmDeleteStatement;
106104
}
107105

108-
final SqmPolymorphicRootDescriptor<R> unmappedPolymorphicDescriptor =
109-
(SqmPolymorphicRootDescriptor<R>) unmappedPolymorphicReference.getReferencedPathSource();
110-
final Set<EntityDomainType<? extends R>> implementors = unmappedPolymorphicDescriptor.getImplementors();
106+
final var unmappedPolymorphicDescriptor =
107+
(SqmPolymorphicRootDescriptor<R>)
108+
unmappedPolymorphicReference.getReferencedPathSource();
109+
final var implementors = unmappedPolymorphicDescriptor.getImplementors();
111110
@SuppressWarnings("unchecked")
112111
final SqmDeleteStatement<R>[] expanded = new SqmDeleteStatement[ implementors.size() ];
113-
114112
int i = 0;
115113
for ( EntityDomainType<?> mappedDescriptor : implementors ) {
116114
expanded[i++] = copyStatement( statement, unmappedPolymorphicReference, mappedDescriptor );
117115
}
118-
119116
return expanded;
120117
}
121118

122119
private static SqmRoot<?> findUnmappedPolymorphicReference(SqmDeleteOrUpdateStatement<?> queryPart) {
123-
return queryPart.getTarget().getReferencedPathSource() instanceof SqmPolymorphicRootDescriptor<?>
124-
? queryPart.getTarget()
125-
: null;
120+
final SqmRoot<?> target = queryPart.getTarget();
121+
return target.getReferencedPathSource() instanceof SqmPolymorphicRootDescriptor<?> ? target : null;
126122
}
127123
}

hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1229,7 +1229,7 @@ private SqmFromClause buildInferredFromClause(HqlParser.SelectClauseContext sele
12291229
final EntityDomainType<R> entityDescriptor = getResultEntity();
12301230
if ( entityDescriptor != null ) {
12311231
final SqmRoot<R> sqmRoot =
1232-
new SqmRoot<>( entityDescriptor, null, false, creationContext.getNodeBuilder() );
1232+
new SqmRoot<>( entityDescriptor, "_0", false, creationContext.getNodeBuilder() );
12331233
processingStateStack.getCurrent().getPathRegistry().register( sqmRoot );
12341234
fromClause.addRoot( sqmRoot );
12351235
}

hibernate-core/src/main/java/org/hibernate/query/hql/internal/SqmTreeCreationHelper.java

+6-9
Original file line numberDiff line numberDiff line change
@@ -134,24 +134,21 @@ public class SqmTreeCreationHelper {
134134
*/
135135
public static <E> void handleRootAsCrossJoin(
136136
HqlParser.EntityWithJoinsContext entityWithJoinsContext,
137-
SqmRoot<?> sqmPrimaryRoot,
137+
SqmRoot<E> sqmPrimaryRoot,
138138
SemanticQueryBuilder<?> sqmBuilder) {
139-
final HqlParser.RootEntityContext fromRootContext = (HqlParser.RootEntityContext) entityWithJoinsContext.fromRoot();
139+
final HqlParser.RootEntityContext fromRootContext =
140+
(HqlParser.RootEntityContext) entityWithJoinsContext.fromRoot();
140141

141142
//noinspection unchecked
142143
final SqmRoot<E> sqmRoot = (SqmRoot<E>) fromRootContext.accept( sqmBuilder );
143144
SqmTreeCreationLogger.LOGGER.debugf( "Handling secondary root path as cross-join - %s", sqmRoot.getEntityName() );
144-
145-
final String alias = extractAlias( fromRootContext.variable(), sqmBuilder );
146-
final SqmEntityJoin<?,E> pseudoCrossJoin = new SqmEntityJoin<>(
145+
final SqmEntityJoin<E,E> pseudoCrossJoin = new SqmEntityJoin<>(
147146
sqmRoot.getManagedType(),
148-
alias,
147+
extractAlias( fromRootContext.variable(), sqmBuilder ),
149148
SqmJoinType.CROSS,
150149
sqmPrimaryRoot
151150
);
152-
153-
//noinspection unchecked,rawtypes
154-
sqmPrimaryRoot.addSqmJoin( (SqmEntityJoin) pseudoCrossJoin );
151+
sqmPrimaryRoot.addSqmJoin( pseudoCrossJoin );
155152

156153
final SqmCreationProcessingState processingState = sqmBuilder.getProcessingStateStack().getCurrent();
157154
final SqmPathRegistry pathRegistry = processingState.getPathRegistry();

hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public <P> QueryParameterBinding<P> getBinding(String name) {
162162

163163
@Override
164164
public void validate() {
165-
for ( Map.Entry<QueryParameter<?>, QueryParameterBinding<?>> entry : parameterBindingMap.entrySet() ) {
165+
for ( var entry : parameterBindingMap.entrySet() ) {
166166
if ( !entry.getValue().isBound() ) {
167167
final QueryParameter<?> queryParameter = entry.getKey();
168168
if ( queryParameter.isNamed() ) {

hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmFunction.java

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import java.util.ArrayList;
88
import java.util.List;
9+
import java.util.Objects;
910
import java.util.function.Supplier;
1011

1112
import org.hibernate.metamodel.mapping.BasicValuedMapping;
@@ -256,4 +257,15 @@ public MappingModelExpressible<?> get() {
256257
}
257258
}
258259

260+
@Override
261+
// TODO: override on all subtypes
262+
public boolean equals(Object other) {
263+
return other instanceof SelfRenderingSqmAggregateFunction<?> that
264+
&& Objects.equals( this.toHqlString(), that.toHqlString() );
265+
}
266+
267+
@Override
268+
public int hashCode() {
269+
return toHqlString().hashCode();
270+
}
259271
}

hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmOrderedSetAggregateFunction.java

+19-16
Original file line numberDiff line numberDiff line change
@@ -138,28 +138,31 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) {
138138
final List<? extends SqmTypedNode<?>> arguments = getArguments();
139139
hql.append( getFunctionName() );
140140
hql.append( '(' );
141-
int i = 1;
142-
if ( arguments.get( 0 ) instanceof SqmDistinct<?> ) {
143-
arguments.get( 0 ).appendHqlString( hql, context );
144-
if ( arguments.size() > 1 ) {
145-
hql.append( ' ' );
146-
arguments.get( 1 ).appendHqlString( hql, context );
147-
i = 2;
141+
if ( !arguments.isEmpty() ) {
142+
int i = 1;
143+
if ( arguments.get( 0 ) instanceof SqmDistinct<?> ) {
144+
arguments.get( 0 ).appendHqlString( hql, context );
145+
if ( arguments.size() > 1 ) {
146+
hql.append( ' ' );
147+
arguments.get( 1 ).appendHqlString( hql, context );
148+
i = 2;
149+
}
150+
}
151+
for ( ; i < arguments.size(); i++ ) {
152+
hql.append( ", " );
153+
arguments.get( i ).appendHqlString( hql, context );
148154
}
149155
}
150-
for ( ; i < arguments.size(); i++ ) {
151-
hql.append(", ");
152-
arguments.get( i ).appendHqlString( hql, context );
153-
}
154-
155156
hql.append( ')' );
156157
if ( withinGroup != null ) {
157158
hql.append( " within group (order by " );
158159
final List<SqmSortSpecification> sortSpecifications = withinGroup.getSortSpecifications();
159-
sortSpecifications.get( 0 ).appendHqlString( hql, context );
160-
for ( int j = 1; j < sortSpecifications.size(); j++ ) {
161-
hql.append( ", " );
162-
sortSpecifications.get( j ).appendHqlString( hql, context );
160+
if ( !sortSpecifications.isEmpty() ) {
161+
sortSpecifications.get( 0 ).appendHqlString( hql, context );
162+
for ( int j = 1; j < sortSpecifications.size(); j++ ) {
163+
hql.append( ", " );
164+
sortSpecifications.get( j ).appendHqlString( hql, context );
165+
}
163166
}
164167
hql.append( ')' );
165168
}

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java

+7-24
Original file line numberDiff line numberDiff line change
@@ -205,38 +205,21 @@ public QuerySqmImpl(
205205
SqmStatement<R> criteria,
206206
boolean copyAst,
207207
Class<R> expectedResultType,
208-
SharedSessionContractImplementor producer) {
209-
super( producer );
208+
SharedSessionContractImplementor session) {
209+
super( session );
210210
hql = CRITERIA_HQL_STRING;
211-
if ( copyAst ) {
212-
sqm = criteria.copy( SqmCopyContext.simpleContext() );
213-
if ( producer.isCriteriaPlanCacheEnabled() ) {
214-
queryStringCacheKey = sqm.toHqlString();
215-
setQueryPlanCacheable( true );
216-
}
217-
else {
218-
queryStringCacheKey = sqm;
219-
}
220-
}
221-
else {
222-
sqm = criteria;
223-
if ( producer.isCriteriaPlanCacheEnabled() ) {
224-
queryStringCacheKey = sqm.toHqlString();
225-
}
226-
else {
227-
queryStringCacheKey = sqm;
228-
}
229-
// Cache immutable query plans by default
230-
setQueryPlanCacheable( true );
231-
}
211+
sqm = copyAst ? criteria.copy( SqmCopyContext.simpleContext() ) : criteria;
212+
queryStringCacheKey = sqm;
213+
// Cache immutable query plans by default
214+
setQueryPlanCacheable( !copyAst || session.isCriteriaPlanCacheEnabled() );
232215

233216
setComment( hql );
234217

235218
domainParameterXref = DomainParameterXref.from( sqm );
236219
parameterMetadata = !domainParameterXref.hasParameters()
237220
? ParameterMetadataImpl.EMPTY
238221
: new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
239-
parameterBindings = parameterMetadata.createBindings( producer.getFactory() );
222+
parameterBindings = parameterMetadata.createBindings( session.getFactory() );
240223

241224
// Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here
242225
for ( SqmParameter<?> sqmParameter : domainParameterXref.getParameterResolutions().getSqmParameters() ) {

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleSqmRenderContext.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ public SimpleSqmRenderContext() {
2323
@Override
2424
public String resolveAlias(SqmFrom<?, ?> from) {
2525
final String explicitAlias = from.getExplicitAlias();
26-
if ( explicitAlias != null ) {
27-
return explicitAlias;
28-
}
29-
return fromAliases.computeIfAbsent( from, f -> "alias_" + (fromId++) );
26+
return explicitAlias == null
27+
? fromAliases.computeIfAbsent( from, f -> "alias_" + (fromId++) )
28+
: explicitAlias;
3029
}
3130

3231
@Override

0 commit comments

Comments
 (0)