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]]
==== `@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]]
==== `@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
* SQL when querying an entity or collection.
* SQL for entities or collections.
* <p>
* For example, {@code @SQLRestriction} could be used to hide entity
* 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
* SQL when querying an entity or collection.
* SQL for entities or collections.
* <p>
* For example, {@code @Where} could be used to hide entity instances which
* 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() );
}
final boolean missingRestriction = sqmDelete.getWhereClause() == null
|| sqmDelete.getWhereClause().getPredicate() == null;
final boolean missingRestriction = sqmInterpretation.getSqlAst().getRestriction() == null;
if ( missingRestriction ) {
assert domainParameterXref.getSqmParameterCount() == 0;
assert jdbcParamsXref.isEmpty();
@ -171,7 +170,12 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan {
tableGroup
);
matchingIdSubQuery.applyPredicate( sqmInterpretation.getSqlAst().getRestriction() );
matchingIdSubQuery.applyPredicate( SqmMutationStrategyHelper.getIdSubqueryPredicate(
sqmInterpretation.getSqlAst().getRestriction(),
entityDescriptor,
tableGroup,
session
) );
return new InSubQueryPredicate( fkColumnExpression, matchingIdSubQuery, false );
},

View File

@ -6,17 +6,24 @@
*/
package org.hibernate.query.sqm.mutation.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
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.from.NamedTableReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
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.exec.spi.ExecutionContext;
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;
}
}