|
1 | 1 | package org.hibernate.reactive.query.sqm.mutation.internal.temptable;
|
2 | 2 |
|
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 | + } |
4 | 315 | }
|
0 commit comments