Skip to content

Commit ec9e1e1

Browse files
committed
fix
1 parent f227466 commit ec9e1e1

File tree

2 files changed

+316
-2
lines changed

2 files changed

+316
-2
lines changed

hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveRestrictedDeleteExecutionDelegate.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ public ReactiveRestrictedDeleteExecutionDelegate(
8585
DomainParameterXref domainParameterXref, QueryOptions queryOptions, LoadQueryInfluencers loadQueryInfluencers, QueryParameterBindings queryParameterBindings,
8686
Function<SharedSessionContractImplementor, String> sessionUidAccess, SessionFactoryImplementor sessionFactory
8787
) {
88-
super( entityDescriptor, idTable, temporaryTableStrategy, forceDropAfterUse, sqmDelete, domainParameterXref, queryOptions, loadQueryInfluencers, queryParameterBindings, sessionUidAccess, sessionFactory );
88+
super(
89+
entityDescriptor, idTable, temporaryTableStrategy, forceDropAfterUse, sqmDelete, domainParameterXref, queryOptions, loadQueryInfluencers, queryParameterBindings, sessionUidAccess,
90+
sessionFactory
91+
);
8992
}
9093

9194

Lines changed: 312 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,315 @@
11
package org.hibernate.reactive.query.sqm.mutation.internal.temptable;
22

3-
public class ReactiveSoftDeleteExecutionDelegate {
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.concurrent.CompletionStage;
6+
import java.util.function.Function;
7+
8+
import org.hibernate.dialect.temptable.TemporaryTable;
9+
import org.hibernate.dialect.temptable.TemporaryTableStrategy;
10+
import org.hibernate.engine.jdbc.spi.JdbcServices;
11+
import org.hibernate.engine.spi.LoadQueryInfluencers;
12+
import org.hibernate.engine.spi.SessionFactoryImplementor;
13+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
14+
import org.hibernate.metamodel.mapping.EntityMappingType;
15+
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
16+
import org.hibernate.metamodel.mapping.MappingModelExpressible;
17+
import org.hibernate.metamodel.mapping.TableDetails;
18+
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
19+
import org.hibernate.persister.entity.EntityPersister;
20+
import org.hibernate.query.spi.DomainQueryExecutionContext;
21+
import org.hibernate.query.spi.QueryOptions;
22+
import org.hibernate.query.spi.QueryParameterBindings;
23+
import org.hibernate.query.sqm.internal.DomainParameterXref;
24+
import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter;
25+
import org.hibernate.query.sqm.internal.SqmUtil;
26+
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
27+
import org.hibernate.query.sqm.mutation.internal.temptable.AbstractDeleteExecutionDelegate;
28+
import org.hibernate.query.sqm.mutation.internal.temptable.ColumnReferenceCheckingSqlAstWalker;
29+
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
30+
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
31+
import org.hibernate.query.sqm.tree.expression.SqmParameter;
32+
import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveSqmMutationStrategyHelper;
33+
import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor;
34+
import org.hibernate.spi.NavigablePath;
35+
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
36+
import org.hibernate.sql.ast.tree.expression.ColumnReference;
37+
import org.hibernate.sql.ast.tree.expression.Expression;
38+
import org.hibernate.sql.ast.tree.expression.SqlTuple;
39+
import org.hibernate.sql.ast.tree.from.MutatingTableReferenceGroupWrapper;
40+
import org.hibernate.sql.ast.tree.from.NamedTableReference;
41+
import org.hibernate.sql.ast.tree.from.TableGroup;
42+
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
43+
import org.hibernate.sql.ast.tree.predicate.Predicate;
44+
import org.hibernate.sql.ast.tree.predicate.PredicateCollector;
45+
import org.hibernate.sql.ast.tree.select.QuerySpec;
46+
import org.hibernate.sql.ast.tree.update.Assignment;
47+
import org.hibernate.sql.ast.tree.update.UpdateStatement;
48+
import org.hibernate.sql.exec.spi.ExecutionContext;
49+
import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation;
50+
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
51+
import org.hibernate.sql.results.internal.SqlSelectionImpl;
52+
53+
import static java.util.Collections.singletonList;
54+
import static org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter.omittingLockingAndPaging;
55+
import static org.hibernate.query.sqm.mutation.internal.temptable.ExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec;
56+
57+
public class ReactiveSoftDeleteExecutionDelegate extends AbstractDeleteExecutionDelegate
58+
implements ReactiveTableBasedDeleteHandler.ReactiveExecutionDelegate {
59+
60+
public ReactiveSoftDeleteExecutionDelegate(
61+
EntityMappingType entityDescriptor, TemporaryTable idTable, TemporaryTableStrategy temporaryTableStrategy,
62+
boolean forceDropAfterUse, SqmDeleteStatement<?> sqmDelete, DomainParameterXref domainParameterXref,
63+
QueryOptions queryOptions, LoadQueryInfluencers loadQueryInfluencers, QueryParameterBindings queryParameterBindings,
64+
Function<SharedSessionContractImplementor, String> sessionUidAccess, SessionFactoryImplementor sessionFactory
65+
) {
66+
super(
67+
entityDescriptor, idTable, temporaryTableStrategy, forceDropAfterUse, sqmDelete, domainParameterXref, queryOptions, loadQueryInfluencers, queryParameterBindings, sessionUidAccess,
68+
sessionFactory
69+
);
70+
}
71+
72+
@Override
73+
public CompletionStage<Integer> reactiveExecute(DomainQueryExecutionContext domainQueryExecutionContext) {
74+
final String targetEntityName = getSqmDelete().getTarget().getEntityName();
75+
final EntityPersister targetEntityDescriptor = getSessionFactory().getMappingMetamodel().getEntityDescriptor( targetEntityName );
76+
77+
final EntityMappingType rootEntityDescriptor = targetEntityDescriptor.getRootEntityDescriptor();
78+
79+
// determine if we need to use a sub-query for matching ids -
80+
// 1. if the target is not the root we will
81+
// 2. if the supplied predicate (if any) refers to columns from a table
82+
// other than the identifier table we will
83+
final SqmJdbcExecutionContextAdapter executionContext = omittingLockingAndPaging( domainQueryExecutionContext );
84+
85+
final TableGroup deletingTableGroup = getConverter().getMutatingTableGroup();
86+
final TableDetails softDeleteTable = rootEntityDescriptor.getSoftDeleteTableDetails();
87+
final NamedTableReference rootTableReference = (NamedTableReference) deletingTableGroup.resolveTableReference(
88+
deletingTableGroup.getNavigablePath(),
89+
softDeleteTable.getTableName()
90+
);
91+
assert rootTableReference != null;
92+
93+
// NOTE : `converter.visitWhereClause` already applies the soft-delete restriction
94+
final Predicate specifiedRestriction = getConverter().visitWhereClause( getSqmDelete().getWhereClause() );
95+
96+
final PredicateCollector predicateCollector = new PredicateCollector( specifiedRestriction );
97+
targetEntityDescriptor.applyBaseRestrictions(
98+
predicateCollector,
99+
deletingTableGroup,
100+
true,
101+
executionContext.getSession().getLoadQueryInfluencers().getEnabledFilters(),
102+
false,
103+
null,
104+
getConverter()
105+
);
106+
107+
getConverter().pruneTableGroupJoins();
108+
final ColumnReferenceCheckingSqlAstWalker walker = new ColumnReferenceCheckingSqlAstWalker(
109+
rootTableReference.getIdentificationVariable()
110+
);
111+
if ( predicateCollector.getPredicate() != null ) {
112+
predicateCollector.getPredicate().accept( walker );
113+
}
114+
115+
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
116+
executionContext.getQueryParameterBindings(),
117+
getDomainParameterXref(),
118+
SqmUtil.generateJdbcParamsXref( getDomainParameterXref(), getConverter() ),
119+
new SqmParameterMappingModelResolutionAccess() {
120+
@Override
121+
@SuppressWarnings("unchecked")
122+
public <T> MappingModelExpressible<T> getResolvedMappingModelType(SqmParameter<T> parameter) {
123+
return (MappingModelExpressible<T>) getConverter().getSqmParameterMappingModelExpressibleResolutions().get( parameter );
124+
}
125+
},
126+
executionContext.getSession()
127+
);
128+
129+
final boolean needsSubQuery = !walker.isAllColumnReferencesFromIdentificationVariable()
130+
|| targetEntityDescriptor != rootEntityDescriptor;
131+
132+
if ( needsSubQuery ) {
133+
return getSessionFactory().getJdbcServices().getDialect().supportsSubqueryOnMutatingTable()
134+
? performDeleteWithSubQuery( rootEntityDescriptor, deletingTableGroup, rootTableReference, predicateCollector, jdbcParameterBindings, getConverter(), executionContext )
135+
: performDeleteWithIdTable( rootEntityDescriptor, rootTableReference, predicateCollector, jdbcParameterBindings, executionContext );
136+
}
137+
return performDirectDelete( rootEntityDescriptor, rootTableReference, predicateCollector, jdbcParameterBindings, executionContext );
138+
}
139+
140+
private CompletionStage<Integer> performDeleteWithIdTable(
141+
EntityMappingType rootEntityDescriptor,
142+
NamedTableReference targetTableReference,
143+
PredicateCollector predicateCollector,
144+
JdbcParameterBindings jdbcParameterBindings,
145+
SqmJdbcExecutionContextAdapter executionContext
146+
) {
147+
ReactiveExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions( getIdTable(), getTemporaryTableStrategy(), executionContext );
148+
try {
149+
return deleteUsingIdTable( rootEntityDescriptor, targetTableReference, predicateCollector, jdbcParameterBindings, executionContext );
150+
}
151+
finally {
152+
ReactiveExecuteWithTemporaryTableHelper.performAfterTemporaryTableUseActions( getIdTable(), getSessionUidAccess(), getAfterUseAction(), executionContext );
153+
}
154+
}
155+
156+
private CompletionStage<Integer> deleteUsingIdTable(
157+
EntityMappingType rootEntityDescriptor,
158+
NamedTableReference targetTableReference,
159+
PredicateCollector predicateCollector,
160+
JdbcParameterBindings jdbcParameterBindings,
161+
SqmJdbcExecutionContextAdapter executionContext
162+
) {
163+
return ReactiveExecuteWithTemporaryTableHelper.saveMatchingIdsIntoIdTable( getConverter(),
164+
predicateCollector.getPredicate(),
165+
getIdTable(),
166+
getSessionUidAccess(),
167+
jdbcParameterBindings,
168+
executionContext
169+
)
170+
.thenCompose( rows -> {
171+
final QuerySpec idTableIdentifierSubQuery = createIdTableSelectQuerySpec(
172+
getIdTable(),
173+
getSessionUidAccess(),
174+
getEntityDescriptor(),
175+
executionContext
176+
);
177+
178+
return ReactiveSqmMutationStrategyHelper.cleanUpCollectionTables(
179+
getEntityDescriptor(),
180+
(tableReference, attributeMapping) -> {
181+
final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor();
182+
final QuerySpec idTableFkSubQuery = fkDescriptor.getTargetPart().isEntityIdentifierMapping()
183+
? idTableIdentifierSubQuery
184+
: createIdTableSelectQuerySpec( getIdTable(), fkDescriptor.getTargetPart(), getSessionUidAccess(), getEntityDescriptor(), executionContext );
185+
return new InSubQueryPredicate(
186+
MappingModelCreationHelper.buildColumnReferenceExpression(
187+
new MutatingTableReferenceGroupWrapper(
188+
new NavigablePath( attributeMapping.getRootPathName() ),
189+
attributeMapping,
190+
(NamedTableReference) tableReference
191+
),
192+
fkDescriptor,
193+
null,
194+
getSessionFactory()
195+
),
196+
idTableFkSubQuery,
197+
false
198+
);
199+
200+
},
201+
JdbcParameterBindings.NO_BINDINGS,
202+
executionContext
203+
).thenCompose( unused -> {
204+
final Assignment softDeleteAssignment = rootEntityDescriptor
205+
.getSoftDeleteMapping()
206+
.createSoftDeleteAssignment( targetTableReference );
207+
final Expression idExpression = createIdExpression( rootEntityDescriptor, targetTableReference );
208+
final UpdateStatement updateStatement = new UpdateStatement(
209+
targetTableReference,
210+
singletonList( softDeleteAssignment ),
211+
new InSubQueryPredicate( idExpression, idTableIdentifierSubQuery, false )
212+
);
213+
214+
return executeUpdate( updateStatement, jdbcParameterBindings, executionContext );
215+
} )
216+
.thenApply( v -> rows );
217+
} );
218+
}
219+
220+
private static Expression createIdExpression(EntityMappingType rootEntityDescriptor, NamedTableReference targetTableReference) {
221+
final TableDetails softDeleteTable = rootEntityDescriptor.getSoftDeleteTableDetails();
222+
final TableDetails.KeyDetails keyDetails = softDeleteTable.getKeyDetails();
223+
final List<Expression> idExpressions = new ArrayList<>( keyDetails.getColumnCount() );
224+
keyDetails.forEachKeyColumn( (position, column) -> idExpressions.add( new ColumnReference( targetTableReference, column ) ) );
225+
return idExpressions.size() == 1
226+
? idExpressions.get( 0 )
227+
: new SqlTuple( idExpressions, rootEntityDescriptor.getIdentifierMapping() );
228+
}
229+
230+
private CompletionStage<Integer> performDeleteWithSubQuery(
231+
EntityMappingType rootEntityDescriptor,
232+
TableGroup deletingTableGroup,
233+
NamedTableReference rootTableReference,
234+
PredicateCollector predicateCollector,
235+
JdbcParameterBindings jdbcParameterBindings,
236+
MultiTableSqmMutationConverter converter,
237+
SqmJdbcExecutionContextAdapter executionContext
238+
) {
239+
final QuerySpec matchingIdSubQuery = new QuerySpec( false, 1 );
240+
matchingIdSubQuery.getFromClause().addRoot( deletingTableGroup );
241+
242+
final TableDetails identifierTableDetails = rootEntityDescriptor.getIdentifierTableDetails();
243+
final TableDetails.KeyDetails keyDetails = identifierTableDetails.getKeyDetails();
244+
245+
final NamedTableReference targetTable = new NamedTableReference(
246+
identifierTableDetails.getTableName(),
247+
DeleteStatement.DEFAULT_ALIAS,
248+
false
249+
);
250+
251+
final List<Expression> idExpressions = new ArrayList<>( keyDetails.getColumnCount() );
252+
keyDetails.forEachKeyColumn( (position, column) -> {
253+
final Expression columnReference = converter.getSqlExpressionResolver().resolveSqlExpression(
254+
rootTableReference,
255+
column
256+
);
257+
matchingIdSubQuery.getSelectClause().addSqlSelection(
258+
new SqlSelectionImpl( position, columnReference )
259+
);
260+
idExpressions.add( new ColumnReference( targetTable, column ) );
261+
} );
262+
263+
matchingIdSubQuery.applyPredicate( predicateCollector.getPredicate() );
264+
final Expression idExpression = idExpressions.size() == 1
265+
? idExpressions.get( 0 )
266+
: new SqlTuple( idExpressions, rootEntityDescriptor.getIdentifierMapping() );
267+
268+
final Assignment softDeleteAssignment = rootEntityDescriptor
269+
.getSoftDeleteMapping()
270+
.createSoftDeleteAssignment( targetTable );
271+
272+
final UpdateStatement updateStatement = new UpdateStatement(
273+
targetTable,
274+
singletonList( softDeleteAssignment ),
275+
new InSubQueryPredicate( idExpression, matchingIdSubQuery, false )
276+
);
277+
278+
return executeUpdate( updateStatement, jdbcParameterBindings, executionContext );
279+
}
280+
281+
private CompletionStage<Integer> performDirectDelete(
282+
EntityMappingType rootEntityDescriptor,
283+
NamedTableReference rootTableReference,
284+
PredicateCollector predicateCollector,
285+
JdbcParameterBindings jdbcParameterBindings,
286+
SqmJdbcExecutionContextAdapter executionContext
287+
) {
288+
289+
final Assignment softDeleteAssignment = rootEntityDescriptor.getSoftDeleteMapping().createSoftDeleteAssignment( rootTableReference );
290+
final UpdateStatement updateStatement = new UpdateStatement( rootTableReference, singletonList( softDeleteAssignment ), predicateCollector.getPredicate() );
291+
return executeUpdate( updateStatement, jdbcParameterBindings, executionContext );
292+
}
293+
294+
private CompletionStage<Integer> executeUpdate(UpdateStatement updateStatement, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) {
295+
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
296+
final JdbcServices jdbcServices = factory.getJdbcServices();
297+
298+
final JdbcOperationQueryMutation jdbcUpdate = jdbcServices.getJdbcEnvironment()
299+
.getSqlAstTranslatorFactory()
300+
.buildMutationTranslator( factory, updateStatement )
301+
.translate( jdbcParameterBindings, executionContext.getQueryOptions() );
302+
303+
return StandardReactiveJdbcMutationExecutor.INSTANCE
304+
.executeReactive(
305+
jdbcUpdate,
306+
jdbcParameterBindings,
307+
sql -> executionContext.getSession()
308+
.getJdbcCoordinator()
309+
.getStatementPreparer()
310+
.prepareStatement( sql ),
311+
(integer, preparedStatement) -> {},
312+
executionContext
313+
);
314+
}
4315
}

0 commit comments

Comments
 (0)