HHH-16392 Fix where clause in collection cleanup subqueries

This commit is contained in:
Marco Belladelli 2023-04-06 14:18:21 +02:00
parent c16cedf327
commit a9080f5f7d
5 changed files with 100 additions and 7 deletions

View File

@ -1427,9 +1427,9 @@ See the <<chapters/domain/basic_types.adoc#mapping-database-generated-value-exam
[[annotations-hibernate-where]] [[annotations-hibernate-where]]
==== `@SQLRestriction` ==== `@SQLRestriction`
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/SQLRestriction.html[`@SQLRestriction`] annotation is used to specify a custom SQL `WHERE` clause used when fetching an entity or a collection. The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/SQLRestriction.html[`@SQLRestriction`] annotation is used to specify a custom SQL `WHERE` clause used to filter out entities or collections.
See the <<chapters/pc/PersistenceContext.adoc#pc-where-example,`@SQLRestriction` mapping>> section for more info. See the <<chapters/pc/PersistenceContext.adoc#pc-where,`@SQLRestriction` mapping>> section for more info.
[[annotations-hibernate-wherejointable]] [[annotations-hibernate-wherejointable]]
==== `@SQLJoinTableRestriction` ==== `@SQLJoinTableRestriction`

View File

@ -16,7 +16,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Specifies a restriction written in native SQL to add to the generated * Specifies a restriction written in native SQL to add to the generated
* SQL when querying an entity or collection. * SQL for entities or collections.
* <p> * <p>
* For example, {@code @SQLRestriction} could be used to hide entity * For example, {@code @SQLRestriction} could be used to hide entity
* instances which have been soft-deleted, either for the entity class * instances which have been soft-deleted, either for the entity class

View File

@ -16,7 +16,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Specifies a restriction written in native SQL to add to the generated * Specifies a restriction written in native SQL to add to the generated
* SQL when querying an entity or collection. * SQL for entities or collections.
* <p> * <p>
* For example, {@code @Where} could be used to hide entity instances which * For example, {@code @Where} could be used to hide entity instances which
* have been soft-deleted, either for the entity class itself: * have been soft-deleted, either for the entity class itself:

View File

@ -124,8 +124,7 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan {
jdbcDelete = deleteTranslator.translate( jdbcParameterBindings, executionContext.getQueryOptions() ); jdbcDelete = deleteTranslator.translate( jdbcParameterBindings, executionContext.getQueryOptions() );
} }
final boolean missingRestriction = sqmDelete.getWhereClause() == null final boolean missingRestriction = sqmInterpretation.getSqlAst().getRestriction() == null;
|| sqmDelete.getWhereClause().getPredicate() == null;
if ( missingRestriction ) { if ( missingRestriction ) {
assert domainParameterXref.getSqmParameterCount() == 0; assert domainParameterXref.getSqmParameterCount() == 0;
assert jdbcParamsXref.isEmpty(); assert jdbcParamsXref.isEmpty();
@ -171,7 +170,12 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan {
tableGroup tableGroup
); );
matchingIdSubQuery.applyPredicate( sqmInterpretation.getSqlAst().getRestriction() ); matchingIdSubQuery.applyPredicate( SqmMutationStrategyHelper.getIdSubqueryPredicate(
sqmInterpretation.getSqlAst().getRestriction(),
entityDescriptor,
tableGroup,
session
) );
return new InSubQueryPredicate( fkColumnExpression, matchingIdSubQuery, false ); return new InSubQueryPredicate( fkColumnExpression, matchingIdSubQuery, false );
}, },

View File

@ -6,17 +6,24 @@
*/ */
package org.hibernate.query.sqm.mutation.internal; package org.hibernate.query.sqm.mutation.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.persister.internal.SqlFragmentPredicate;
import org.hibernate.sql.ast.tree.delete.DeleteStatement; import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParameterBindings;
@ -179,4 +186,86 @@ public class SqmMutationStrategyHelper {
); );
} }
} }
/**
* Translates the original delete predicate to be used in the id subquery
* forcing the use of the table alias qualifier
*/
public static Predicate getIdSubqueryPredicate(
Predicate predicate,
EntityMappingType entityDescriptor,
TableGroup tableGroup,
SharedSessionContractImplementor session) {
if ( predicate instanceof FilterPredicate || predicate instanceof SqlFragmentPredicate ) {
return getBaseRestrictions( entityDescriptor, tableGroup, session ).get( 0 );
}
else if ( predicate instanceof Junction ) {
final Junction original = (Junction) predicate;
if ( original.getPredicates().size() > 1 ) {
final Junction junction = new Junction(
original.getNature(),
original.getExpressionType()
);
junction.getPredicates().addAll( original.getPredicates() );
final Predicate secondToLastPredicate = junction.getPredicates().get( junction.getPredicates().size() - 2 );
final Predicate lastPredicate = junction.getPredicates().get( junction.getPredicates().size() - 1 );
int filterPredicateIndex = -1;
int fragmentPredicateIndex = -1;
if ( lastPredicate instanceof Junction ) {
// If the mutation query specified an explicit where condition and there are multiple base
// restrictions they will be in a nested Junction predicate, so we need to process that one
final Predicate baseRestrictions = getIdSubqueryPredicate(
lastPredicate,
entityDescriptor,
tableGroup,
session
);
junction.getPredicates().set( junction.getPredicates().size() - 1, baseRestrictions );
predicate = junction;
}
else if ( secondToLastPredicate instanceof FilterPredicate ) {
filterPredicateIndex = junction.getPredicates().size() - 2;
fragmentPredicateIndex = filterPredicateIndex + 1;
}
else if ( lastPredicate instanceof FilterPredicate ) {
filterPredicateIndex = junction.getPredicates().size() - 1;
}
else if ( lastPredicate instanceof SqlFragmentPredicate ) {
fragmentPredicateIndex = junction.getPredicates().size() - 1;
}
if ( filterPredicateIndex != -1 || fragmentPredicateIndex != -1 ) {
final List<Predicate> baseRestrictions = getBaseRestrictions(
entityDescriptor,
tableGroup,
session
);
int index = 0;
if ( filterPredicateIndex != -1 ) {
junction.getPredicates().set( filterPredicateIndex, baseRestrictions.get( index++ ) );
}
if ( fragmentPredicateIndex != -1 ) {
junction.getPredicates().set( fragmentPredicateIndex, baseRestrictions.get( index ) );
}
predicate = junction;
}
}
}
return predicate;
}
private static List<Predicate> getBaseRestrictions(
EntityMappingType entityDescriptor,
TableGroup tableGroup,
SharedSessionContractImplementor session) {
final List<Predicate> baseRestrictions = new ArrayList<>( 2 );
entityDescriptor.applyBaseRestrictions(
baseRestrictions::add,
tableGroup,
true,
session.getLoadQueryInfluencers().getEnabledFilters(),
null,
null
);
return baseRestrictions;
}
} }