Skip to content

HHH-19685 Rework multi-table handling code to allow caching and help Hibernate Reactive #10680

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1558,9 +1558,9 @@ else if ( modelPart instanceof VirtualModelPart ) {
}

public static Expression buildColumnReferenceExpression(
TableGroup tableGroup,
@Nullable TableGroup tableGroup,
ModelPart modelPart,
SqlExpressionResolver sqlExpressionResolver,
@Nullable SqlExpressionResolver sqlExpressionResolver,
SessionFactoryImplementor sessionFactory) {
final int jdbcTypeCount = modelPart.getJdbcTypeCount();
if ( modelPart instanceof EmbeddableValuedModelPart ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.query.sqm.internal;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.action.internal.BulkOperationCleanupAction;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.sqm.mutation.spi.MultiTableHandler;
import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult;
import org.hibernate.query.sqm.tree.SqmDmlStatement;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;


/**
* @since 7.1
*/
public abstract class AbstractMultiTableMutationQueryPlan<S extends SqmDmlStatement<?>, F> implements NonSelectQueryPlan {
private final S statement;
private final DomainParameterXref domainParameterXref;
private final F strategy;

private volatile MultiTableHandler handler;

public AbstractMultiTableMutationQueryPlan(S statement, DomainParameterXref domainParameterXref, F strategy) {
this.statement = statement;
this.domainParameterXref = domainParameterXref;
this.strategy = strategy;
}

protected abstract MultiTableHandlerBuildResult buildHandler(
S statement,
DomainParameterXref domainParameterXref,
F strategy,
DomainQueryExecutionContext context);

@Override
public int executeUpdate(DomainQueryExecutionContext context) {
BulkOperationCleanupAction.schedule( context.getSession(), statement );
final Interpretation interpretation = getInterpretation( context );
return interpretation.handler().execute( interpretation.jdbcParameterBindings(), context );
}

// For Hibernate Reactive
protected S getStatement() {
return statement;
}

// For Hibernate Reactive
protected Interpretation getInterpretation(DomainQueryExecutionContext context) {
Interpretation builtInterpretation = null;
MultiTableHandler localCopy = handler;

if ( localCopy == null ) {
synchronized (this) {
localCopy = handler;
if ( localCopy == null ) {
final MultiTableHandlerBuildResult buildResult = buildHandler(
statement,
domainParameterXref,
strategy,
context
);
builtInterpretation = new Interpretation(
buildResult.multiTableHandler(),
buildResult.firstJdbcParameterBindings()
);
localCopy = buildResult.multiTableHandler();
handler = localCopy;
}
else {
builtInterpretation = updateInterpretation( localCopy, context );
}
}
}
else {
builtInterpretation = updateInterpretation( localCopy, context );
}
return builtInterpretation != null ? builtInterpretation
: new Interpretation( localCopy, localCopy.createJdbcParameterBindings( context ) );
}

private @Nullable Interpretation updateInterpretation(
MultiTableHandler localCopy,
DomainQueryExecutionContext context) {
Interpretation builtInterpretation = null;

// If the translation depends on parameter bindings or it isn't compatible with the current query options,
// we have to rebuild the JdbcSelect, which is still better than having to translate from SQM to SQL AST again
if ( localCopy.dependsOnParameterBindings() ) {
final JdbcParameterBindings jdbcParameterBindings = localCopy.createJdbcParameterBindings( context );
// If the translation depends on the limit or lock options, we have to rebuild the JdbcSelect
// We could avoid this by putting the lock options into the cache key
if ( !localCopy.isCompatibleWith( jdbcParameterBindings, context.getQueryOptions() ) ) {
final MultiTableHandlerBuildResult buildResult = buildHandler(
statement,
domainParameterXref,
strategy,
context
);
localCopy = buildResult.multiTableHandler();
builtInterpretation = new Interpretation(
buildResult.multiTableHandler(),
buildResult.firstJdbcParameterBindings()
);
handler = localCopy;
}
else {
builtInterpretation = new Interpretation( localCopy, jdbcParameterBindings );
}
}
return builtInterpretation;
}

// For Hibernate Reactive
protected record Interpretation(
MultiTableHandler handler,
JdbcParameterBindings jdbcParameterBindings
) {}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.query.sqm.internal;

import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperationQuery;
import org.hibernate.sql.exec.spi.JdbcParametersList;

import java.util.List;
import java.util.Map;

/**
* @since 7.1
*/
public record CacheableSqmInterpretation<S extends Statement, J extends JdbcOperationQuery>(
S statement,
J jdbcOperation,
Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> jdbcParamsXref,
Map<SqmParameter<?>, MappingModelExpressible<?>> sqmParameterMappingModelTypes) {

}
Loading
Loading