Skip to content

Commit 5ee29a6

Browse files
committed
Rework multi-table handling and non-select query plan code to allow caching and help Hibernate Reactive
1 parent 832c8d1 commit 5ee29a6

File tree

48 files changed

+4232
-3583
lines changed

Some content is hidden

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

48 files changed

+4232
-3583
lines changed

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,9 +1558,9 @@ else if ( modelPart instanceof VirtualModelPart ) {
15581558
}
15591559

15601560
public static Expression buildColumnReferenceExpression(
1561-
TableGroup tableGroup,
1561+
@Nullable TableGroup tableGroup,
15621562
ModelPart modelPart,
1563-
SqlExpressionResolver sqlExpressionResolver,
1563+
@Nullable SqlExpressionResolver sqlExpressionResolver,
15641564
SessionFactoryImplementor sessionFactory) {
15651565
final int jdbcTypeCount = modelPart.getJdbcTypeCount();
15661566
if ( modelPart instanceof EmbeddableValuedModelPart ) {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query.sqm.internal;
6+
7+
import org.hibernate.action.internal.BulkOperationCleanupAction;
8+
import org.hibernate.query.spi.DomainQueryExecutionContext;
9+
import org.hibernate.query.spi.NonSelectQueryPlan;
10+
import org.hibernate.query.sqm.mutation.spi.MultiTableHandler;
11+
import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult;
12+
import org.hibernate.query.sqm.tree.SqmDmlStatement;
13+
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
14+
15+
16+
/**
17+
* @since 7.1
18+
*/
19+
public abstract class AbstractMultiTableMutationQueryPlan<S extends SqmDmlStatement<?>, F> implements NonSelectQueryPlan {
20+
private final S statement;
21+
private final DomainParameterXref domainParameterXref;
22+
private final F strategy;
23+
24+
private volatile MultiTableHandler handler;
25+
26+
public AbstractMultiTableMutationQueryPlan(S statement, DomainParameterXref domainParameterXref, F strategy) {
27+
this.statement = statement;
28+
this.domainParameterXref = domainParameterXref;
29+
this.strategy = strategy;
30+
}
31+
32+
protected abstract MultiTableHandlerBuildResult buildHandler(
33+
S statement,
34+
DomainParameterXref domainParameterXref,
35+
F strategy,
36+
DomainQueryExecutionContext context);
37+
38+
@Override
39+
public int executeUpdate(DomainQueryExecutionContext context) {
40+
BulkOperationCleanupAction.schedule( context.getSession(), statement );
41+
MultiTableHandler localCopy = handler;
42+
JdbcParameterBindings jdbcParameterBindings = null;
43+
44+
if ( localCopy == null ) {
45+
synchronized (this) {
46+
localCopy = handler;
47+
if ( localCopy == null ) {
48+
final MultiTableHandlerBuildResult buildResult = buildHandler(
49+
statement,
50+
domainParameterXref,
51+
strategy,
52+
context
53+
);
54+
localCopy = buildResult.multiTableHandler();
55+
jdbcParameterBindings = buildResult.firstJdbcParameterBindings();
56+
handler = localCopy;
57+
}
58+
else {
59+
// todo: check if jdbc parameters are compatible
60+
}
61+
}
62+
}
63+
else {
64+
// todo: check if jdbc parameters are compatible
65+
}
66+
if ( jdbcParameterBindings == null ) {
67+
jdbcParameterBindings = localCopy.createJdbcParameterBindings( context );
68+
}
69+
return localCopy.execute( jdbcParameterBindings, context );
70+
}
71+
72+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query.sqm.internal;
6+
7+
import org.hibernate.metamodel.mapping.MappingModelExpressible;
8+
import org.hibernate.query.spi.QueryParameterImplementor;
9+
import org.hibernate.query.sqm.tree.expression.SqmParameter;
10+
import org.hibernate.sql.ast.tree.Statement;
11+
import org.hibernate.sql.exec.spi.JdbcOperationQuery;
12+
import org.hibernate.sql.exec.spi.JdbcParametersList;
13+
14+
import java.util.List;
15+
import java.util.Map;
16+
17+
/**
18+
* @since 7.1
19+
*/
20+
public record CacheableSqmInterpretation<S extends Statement, J extends JdbcOperationQuery>(
21+
S statement,
22+
J jdbcOperation,
23+
Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> jdbcParamsXref,
24+
Map<SqmParameter<?>, MappingModelExpressible<?>> sqmParameterMappingModelTypes) {
25+
26+
}

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

Lines changed: 34 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1919
import org.hibernate.engine.spi.SubselectFetch;
2020
import org.hibernate.internal.EmptyScrollableResults;
21+
import org.hibernate.internal.util.MutableObject;
2122
import org.hibernate.metamodel.mapping.MappingModelExpressible;
2223
import org.hibernate.query.QueryTypeMismatchException;
2324
import org.hibernate.query.TupleTransformer;
2425
import org.hibernate.query.spi.DomainQueryExecutionContext;
2526
import org.hibernate.query.spi.QueryOptions;
26-
import org.hibernate.query.spi.QueryParameterImplementor;
2727
import org.hibernate.query.spi.ScrollableResultsImplementor;
2828
import org.hibernate.query.spi.SelectQueryPlan;
2929
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
@@ -79,7 +79,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
7979
private final SqmInterpreter<List<R>, Void> listInterpreter;
8080
private final SqmInterpreter<ScrollableResultsImplementor<R>, ScrollMode> scrollInterpreter;
8181

82-
private volatile CacheableSqmInterpretation cacheableSqmInterpretation;
82+
private volatile CacheableSqmInterpretation<SelectStatement, JdbcOperationQuerySelect> cacheableSqmInterpretation;
8383

8484
public ConcreteSqmSelectQueryPlan(
8585
SqmSelectStatement<?> sqm,
@@ -97,16 +97,16 @@ public ConcreteSqmSelectQueryPlan(
9797
: ListResultsConsumer.UniqueSemantic.ALLOW;
9898
this.executeQueryInterpreter = (resultsConsumer, executionContext, sqmInterpretation, jdbcParameterBindings) -> {
9999
final SharedSessionContractImplementor session = executionContext.getSession();
100-
final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.getJdbcSelect();
100+
final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.jdbcOperation();
101101
try {
102102
final SubselectFetch.RegistrationHandler subSelectFetchKeyHandler = SubselectFetch.createRegistrationHandler(
103103
session.getPersistenceContext().getBatchFetchQueue(),
104-
sqmInterpretation.selectStatement,
104+
sqmInterpretation.statement(),
105105
JdbcParametersList.empty(),
106106
jdbcParameterBindings
107107
);
108108
session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames(), true );
109-
final Expression fetchExpression = sqmInterpretation.selectStatement.getQueryPart()
109+
final Expression fetchExpression = sqmInterpretation.statement().getQueryPart()
110110
.getFetchClauseExpression();
111111
final int resultCountEstimate = fetchExpression != null
112112
? interpretIntExpression( fetchExpression, jdbcParameterBindings )
@@ -127,17 +127,17 @@ public ConcreteSqmSelectQueryPlan(
127127
};
128128
this.listInterpreter = (unused, executionContext, sqmInterpretation, jdbcParameterBindings) -> {
129129
final SharedSessionContractImplementor session = executionContext.getSession();
130-
final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.getJdbcSelect();
130+
final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.jdbcOperation();
131131
try {
132132
final SubselectFetch.RegistrationHandler subSelectFetchKeyHandler = SubselectFetch.createRegistrationHandler(
133133
session.getPersistenceContext().getBatchFetchQueue(),
134-
sqmInterpretation.selectStatement,
134+
sqmInterpretation.statement(),
135135
JdbcParametersList.empty(),
136136
jdbcParameterBindings
137137
);
138138
session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames(), true );
139139
final Expression fetchExpression =
140-
sqmInterpretation.selectStatement.getQueryPart()
140+
sqmInterpretation.statement().getQueryPart()
141141
.getFetchClauseExpression();
142142
final int resultCountEstimate = fetchExpression != null
143143
? interpretIntExpression( fetchExpression, jdbcParameterBindings )
@@ -160,7 +160,7 @@ public ConcreteSqmSelectQueryPlan(
160160

161161
this.scrollInterpreter = (scrollMode, executionContext, sqmInterpretation, jdbcParameterBindings) -> {
162162
final SharedSessionContractImplementor session = executionContext.getSession();
163-
final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.getJdbcSelect();
163+
final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.jdbcOperation();
164164
try {
165165
// final SubselectFetch.RegistrationHandler subSelectFetchKeyHandler = SubselectFetch.createRegistrationHandler(
166166
// executionContext.getSession().getPersistenceContext().getBatchFetchQueue(),
@@ -173,7 +173,7 @@ public ConcreteSqmSelectQueryPlan(
173173
session.getFactory().getJdbcServices().getJdbcSelectExecutor();
174174
session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames(), true );
175175
final Expression fetchExpression =
176-
sqmInterpretation.selectStatement.getQueryPart()
176+
sqmInterpretation.statement().getQueryPart()
177177
.getFetchClauseExpression();
178178
final int resultCountEstimate = fetchExpression != null
179179
? interpretIntExpression( fetchExpression, jdbcParameterBindings )
@@ -401,7 +401,7 @@ private <T, X> T withCacheableSqmInterpretation(DomainQueryExecutionContext exec
401401
// to protect access. However, synchronized is much simpler here. We will verify
402402
// during throughput testing whether this is an issue and consider changes then
403403

404-
CacheableSqmInterpretation localCopy = cacheableSqmInterpretation;
404+
CacheableSqmInterpretation<SelectStatement, JdbcOperationQuerySelect> localCopy = cacheableSqmInterpretation;
405405
JdbcParameterBindings jdbcParameterBindings = null;
406406

407407
executionContext.getSession().autoPreFlush();
@@ -410,23 +410,23 @@ private <T, X> T withCacheableSqmInterpretation(DomainQueryExecutionContext exec
410410
synchronized ( this ) {
411411
localCopy = cacheableSqmInterpretation;
412412
if ( localCopy == null ) {
413-
localCopy = buildCacheableSqmInterpretation( sqm, domainParameterXref, executionContext );
414-
jdbcParameterBindings = localCopy.firstParameterBindings;
415-
localCopy.firstParameterBindings = null;
413+
final MutableObject<JdbcParameterBindings> mutableValue = new MutableObject<>();
414+
localCopy = buildInterpretation( sqm, domainParameterXref, executionContext, mutableValue );
415+
jdbcParameterBindings = mutableValue.get();
416416
cacheableSqmInterpretation = localCopy;
417417
}
418418
else {
419419
// If the translation depends on parameter bindings or it isn't compatible with the current query options,
420420
// we have to rebuild the JdbcSelect, which is still better than having to translate from SQM to SQL AST again
421-
if ( localCopy.jdbcSelect.dependsOnParameterBindings() ) {
421+
if ( localCopy.jdbcOperation().dependsOnParameterBindings() ) {
422422
jdbcParameterBindings = createJdbcParameterBindings( localCopy, executionContext );
423423
}
424424
// If the translation depends on the limit or lock options, we have to rebuild the JdbcSelect
425425
// We could avoid this by putting the lock options into the cache key
426-
if ( !localCopy.jdbcSelect.isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) {
427-
localCopy = buildCacheableSqmInterpretation( sqm, domainParameterXref, executionContext );
428-
jdbcParameterBindings = localCopy.firstParameterBindings;
429-
localCopy.firstParameterBindings = null;
426+
if ( !localCopy.jdbcOperation().isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) {
427+
final MutableObject<JdbcParameterBindings> mutableValue = new MutableObject<>();
428+
localCopy = buildInterpretation( sqm, domainParameterXref, executionContext, mutableValue );
429+
jdbcParameterBindings = mutableValue.get();
430430
cacheableSqmInterpretation = localCopy;
431431
}
432432
}
@@ -435,15 +435,15 @@ private <T, X> T withCacheableSqmInterpretation(DomainQueryExecutionContext exec
435435
else {
436436
// If the translation depends on parameter bindings or it isn't compatible with the current query options,
437437
// we have to rebuild the JdbcSelect, which is still better than having to translate from SQM to SQL AST again
438-
if ( localCopy.jdbcSelect.dependsOnParameterBindings() ) {
438+
if ( localCopy.jdbcOperation().dependsOnParameterBindings() ) {
439439
jdbcParameterBindings = createJdbcParameterBindings( localCopy, executionContext );
440440
}
441441
// If the translation depends on the limit or lock options, we have to rebuild the JdbcSelect
442442
// We could avoid this by putting the lock options into the cache key
443-
if ( !localCopy.jdbcSelect.isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) {
444-
localCopy = buildCacheableSqmInterpretation( sqm, domainParameterXref, executionContext );
445-
jdbcParameterBindings = localCopy.firstParameterBindings;
446-
localCopy.firstParameterBindings = null;
443+
if ( !localCopy.jdbcOperation().isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) {
444+
final MutableObject<JdbcParameterBindings> mutableValue = new MutableObject<>();
445+
localCopy = buildInterpretation( sqm, domainParameterXref, executionContext, mutableValue );
446+
jdbcParameterBindings = mutableValue.get();
447447
cacheableSqmInterpretation = localCopy;
448448
}
449449
}
@@ -455,26 +455,27 @@ private <T, X> T withCacheableSqmInterpretation(DomainQueryExecutionContext exec
455455
return interpreter.interpret( context, executionContext, localCopy, jdbcParameterBindings );
456456
}
457457

458-
private JdbcParameterBindings createJdbcParameterBindings(CacheableSqmInterpretation sqmInterpretation, DomainQueryExecutionContext executionContext) {
458+
private JdbcParameterBindings createJdbcParameterBindings(CacheableSqmInterpretation<SelectStatement, JdbcOperationQuerySelect> sqmInterpretation, DomainQueryExecutionContext executionContext) {
459459
return SqmUtil.createJdbcParameterBindings(
460460
executionContext.getQueryParameterBindings(),
461461
domainParameterXref,
462-
sqmInterpretation.getJdbcParamsXref(),
462+
sqmInterpretation.jdbcParamsXref(),
463463
new SqmParameterMappingModelResolutionAccess() {
464464
//this is pretty ugly!
465465
@Override @SuppressWarnings("unchecked")
466466
public <T> MappingModelExpressible<T> getResolvedMappingModelType(SqmParameter<T> parameter) {
467-
return (MappingModelExpressible<T>) sqmInterpretation.getSqmParameterMappingModelTypes().get(parameter);
467+
return (MappingModelExpressible<T>) sqmInterpretation.sqmParameterMappingModelTypes().get(parameter);
468468
}
469469
},
470470
executionContext.getSession()
471471
);
472472
}
473473

474-
private static CacheableSqmInterpretation buildCacheableSqmInterpretation(
474+
private static CacheableSqmInterpretation<SelectStatement, JdbcOperationQuerySelect> buildInterpretation(
475475
SqmSelectStatement<?> sqm,
476476
DomainParameterXref domainParameterXref,
477-
DomainQueryExecutionContext executionContext) {
477+
DomainQueryExecutionContext executionContext,
478+
MutableObject<JdbcParameterBindings> firstJdbcParameterBindingsConsumer) {
478479
final SharedSessionContractImplementor session = executionContext.getSession();
479480
final SessionFactoryImplementor sessionFactory = session.getFactory();
480481

@@ -511,57 +512,23 @@ public <T> MappingModelExpressible<T> getResolvedMappingModelType(SqmParameter<T
511512
},
512513
session
513514
);
514-
515-
return new CacheableSqmInterpretation(
515+
firstJdbcParameterBindingsConsumer.set( jdbcParameterBindings );
516+
return new CacheableSqmInterpretation<>(
516517
sqmInterpretation.getSqlAst(),
517518
selectTranslator.translate( jdbcParameterBindings, executionContext.getQueryOptions() ),
518519
jdbcParamsXref,
519-
sqmInterpretation.getSqmParameterMappingModelTypeResolutions(),
520-
jdbcParameterBindings
520+
sqmInterpretation.getSqmParameterMappingModelTypeResolutions()
521521
);
522522
}
523523

524524
private interface SqmInterpreter<T, X> {
525525
T interpret(
526526
X context,
527527
DomainQueryExecutionContext executionContext,
528-
CacheableSqmInterpretation sqmInterpretation,
528+
CacheableSqmInterpretation<SelectStatement, JdbcOperationQuerySelect> sqmInterpretation,
529529
JdbcParameterBindings jdbcParameterBindings);
530530
}
531531

532-
private static class CacheableSqmInterpretation {
533-
private final SelectStatement selectStatement;
534-
private final JdbcOperationQuerySelect jdbcSelect;
535-
private final Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> jdbcParamsXref;
536-
private final Map<SqmParameter<?>, MappingModelExpressible<?>> sqmParameterMappingModelTypes;
537-
private transient JdbcParameterBindings firstParameterBindings;
538-
539-
CacheableSqmInterpretation(
540-
SelectStatement selectStatement,
541-
JdbcOperationQuerySelect jdbcSelect,
542-
Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> jdbcParamsXref,
543-
Map<SqmParameter<?>, MappingModelExpressible<?>> sqmParameterMappingModelTypes,
544-
JdbcParameterBindings firstParameterBindings) {
545-
this.selectStatement = selectStatement;
546-
this.jdbcSelect = jdbcSelect;
547-
this.jdbcParamsXref = jdbcParamsXref;
548-
this.sqmParameterMappingModelTypes = sqmParameterMappingModelTypes;
549-
this.firstParameterBindings = firstParameterBindings;
550-
}
551-
552-
JdbcOperationQuerySelect getJdbcSelect() {
553-
return jdbcSelect;
554-
}
555-
556-
Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> getJdbcParamsXref() {
557-
return jdbcParamsXref;
558-
}
559-
560-
public Map<SqmParameter<?>, MappingModelExpressible<?>> getSqmParameterMappingModelTypes() {
561-
return sqmParameterMappingModelTypes;
562-
}
563-
}
564-
565532
private static class MySqmJdbcExecutionContextAdapter extends SqmJdbcExecutionContextAdapter {
566533
private final SubselectFetch.RegistrationHandler subSelectFetchKeyHandler;
567534
private final String hql;

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

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,29 @@
44
*/
55
package org.hibernate.query.sqm.internal;
66

7-
import org.hibernate.action.internal.BulkOperationCleanupAction;
87
import org.hibernate.query.spi.DomainQueryExecutionContext;
9-
import org.hibernate.query.spi.NonSelectQueryPlan;
8+
import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult;
109
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
1110
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
1211

1312
/**
1413
* @author Steve Ebersole
1514
*/
16-
public class MultiTableDeleteQueryPlan implements NonSelectQueryPlan {
17-
private final SqmDeleteStatement sqmDelete;
18-
private final DomainParameterXref domainParameterXref;
19-
private final SqmMultiTableMutationStrategy deleteStrategy;
15+
public class MultiTableDeleteQueryPlan extends AbstractMultiTableMutationQueryPlan<SqmDeleteStatement<?>, SqmMultiTableMutationStrategy> {
2016

2117
public MultiTableDeleteQueryPlan(
22-
SqmDeleteStatement sqmDelete,
18+
SqmDeleteStatement<?> sqmDelete,
2319
DomainParameterXref domainParameterXref,
2420
SqmMultiTableMutationStrategy deleteStrategy) {
25-
this.sqmDelete = sqmDelete;
26-
this.domainParameterXref = domainParameterXref;
27-
this.deleteStrategy = deleteStrategy;
21+
super( sqmDelete, domainParameterXref, deleteStrategy );
2822
}
2923

3024
@Override
31-
public int executeUpdate(DomainQueryExecutionContext executionContext) {
32-
BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmDelete );
33-
return deleteStrategy.executeDelete( sqmDelete, domainParameterXref, executionContext );
25+
protected MultiTableHandlerBuildResult buildHandler(
26+
SqmDeleteStatement<?> statement,
27+
DomainParameterXref domainParameterXref,
28+
SqmMultiTableMutationStrategy strategy,
29+
DomainQueryExecutionContext context) {
30+
return strategy.buildHandler( statement, domainParameterXref, context );
3431
}
3532
}

0 commit comments

Comments
 (0)