Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5420ba2

Browse files
JaeYeon KimJaeYeon Kim
JaeYeon Kim
authored and
JaeYeon Kim
committed
GH-1978 JdbcAggregateOperations delete by query
Changed the steps as follows: 1. Lock the target rows using SELECT ... FOR UPDATE based on the query conditions. 2. Delete sub-entities by leveraging a subquery that selects the matching root rows. 3. Delete the root entities using the same subquery criteria. Signed-off-by: JaeYeon Kim <JaeYeon.Kim@ibm.com>
1 parent 2f97ff2 commit 5420ba2

File tree

13 files changed

+264
-175
lines changed

13 files changed

+264
-175
lines changed

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/AggregateChangeExecutor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ private void execute(DbAction<?> action, JdbcAggregateChangeExecutionContext exe
102102
executionContext.executeBatchDeleteRoot(batchDeleteRoot);
103103
} else if (action instanceof DbAction.DeleteAllRoot<?> deleteAllRoot) {
104104
executionContext.executeDeleteAllRoot(deleteAllRoot);
105-
} else if (action instanceof DbAction.DeleteRootByIdIn<?> deleteRootByIdIn) {
106-
executionContext.executeDeleteRootByIdIn(deleteRootByIdIn);
107-
} else if (action instanceof DbAction.DeleteByRootIdIn<?> deleteByRootIdIn) {
108-
executionContext.executeDeleteByRootIdIn(deleteByRootIdIn);
105+
} else if (action instanceof DbAction.DeleteRootByQuery<?> deleteRootByQuery) {
106+
executionContext.excuteDeleteRootByQuery(deleteRootByQuery);
107+
} else if (action instanceof DbAction.DeleteByQuery<?> deleteByQuery) {
108+
executionContext.excuteDeleteByQuery(deleteByQuery);
109109
} else if (action instanceof DbAction.AcquireLockRoot<?> acquireLockRoot) {
110110
executionContext.executeAcquireLock(acquireLockRoot);
111111
} else if (action instanceof DbAction.AcquireLockAllRoot<?> acquireLockAllRoot) {

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import org.springframework.data.mapping.PersistentPropertyPath;
4141
import org.springframework.data.mapping.PersistentPropertyPathAccessor;
4242
import org.springframework.data.relational.core.conversion.DbAction;
43-
import org.springframework.data.relational.core.conversion.SelectIdsDbActionExecutionResult;
4443
import org.springframework.data.relational.core.conversion.DbActionExecutionResult;
4544
import org.springframework.data.relational.core.conversion.IdValueSource;
4645
import org.springframework.data.relational.core.mapping.AggregatePath;
@@ -74,7 +73,6 @@ class JdbcAggregateChangeExecutionContext {
7473
private final DataAccessStrategy accessStrategy;
7574

7675
private final Map<DbAction<?>, DbActionExecutionResult> results = new LinkedHashMap<>();
77-
private final Map<DbAction.SelectIds<?>, SelectIdsDbActionExecutionResult> selectIdsDbActionExecutionResult = new LinkedHashMap<>();
7876

7977
JdbcAggregateChangeExecutionContext(JdbcConverter converter, DataAccessStrategy accessStrategy) {
8078

@@ -172,32 +170,14 @@ <T> void executeDeleteAll(DbAction.DeleteAll<T> delete) {
172170
accessStrategy.deleteAll(delete.getPropertyPath());
173171
}
174172

175-
<T> void executeDeleteRootByIdIn(DbAction.DeleteRootByIdIn<T> deleteRootByIdIn) {
176-
SelectIdsDbActionExecutionResult result = getRequiredSelectIdsResult(deleteRootByIdIn.getSelectIdsAction());
173+
<T> void excuteDeleteRootByQuery(DbAction.DeleteRootByQuery<T> deleteRootByQuery) {
177174

178-
List<Object> rootIds = new ArrayList<>(result.getSelectedIds());
179-
if (rootIds.isEmpty()) {
180-
return;
181-
}
182-
accessStrategy.delete(rootIds, deleteRootByIdIn.getEntityType());
175+
accessStrategy.deleteRootByQuery(deleteRootByQuery.getQuery(), deleteRootByQuery.getEntityType());
183176
}
184177

185-
<T> void executeDeleteByRootIdIn(DbAction.DeleteByRootIdIn<T> deleteByRootIdIn) {
186-
SelectIdsDbActionExecutionResult result = getRequiredSelectIdsResult(deleteByRootIdIn.getSelectIdsAction());
178+
<T> void excuteDeleteByQuery(DbAction.DeleteByQuery<T> deleteByQuery) {
187179

188-
List<Object> rootIds = new ArrayList<>(result.getSelectedIds());
189-
if (rootIds.isEmpty()) {
190-
return;
191-
}
192-
accessStrategy.delete(rootIds, deleteByRootIdIn.getPropertyPath());
193-
}
194-
195-
private SelectIdsDbActionExecutionResult getRequiredSelectIdsResult(DbAction.SelectIds selectIdsAction) {
196-
SelectIdsDbActionExecutionResult result = selectIdsDbActionExecutionResult.get(selectIdsAction);
197-
if (result == null) {
198-
throw new IllegalArgumentException("Expected SelectIdsDbActionExecutionResult for given selectIdsAction but found none");
199-
}
200-
return result;
180+
accessStrategy.deleteByQuery(deleteByQuery.getQuery(), deleteByQuery.getPropertyPath());
201181
}
202182

203183
<T> void executeAcquireLock(DbAction.AcquireLockRoot<T> acquireLock) {
@@ -209,10 +189,7 @@ <T> void executeAcquireLockAllRoot(DbAction.AcquireLockAllRoot<T> acquireLock) {
209189
}
210190

211191
<T> void executeAcquireLockRootByQuery(DbAction.AcquireLockAllRootByQuery<T> acquireLock) {
212-
213-
List<?> rootIds = accessStrategy.acquireLockAndFindIdsByQuery(acquireLock.getQuery(), LockMode.PESSIMISTIC_WRITE, acquireLock.getEntityType());
214-
215-
selectIdsDbActionExecutionResult.put(acquireLock, new SelectIdsDbActionExecutionResult(rootIds, acquireLock));
192+
accessStrategy.acquireLockByQuery(acquireLock.getQuery(), LockMode.PESSIMISTIC_WRITE, acquireLock.getEntityType());
216193
}
217194

218195
private void add(DbActionExecutionResult result) {

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CascadingDataAccessStrategy.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> prope
110110
collectVoid(das -> das.deleteAll(propertyPath));
111111
}
112112

113+
@Override
114+
public void deleteRootByQuery(Query query, Class<?> domainType) {
115+
collectVoid(das -> das.deleteRootByQuery(query, domainType));
116+
}
117+
118+
@Override
119+
public void deleteByQuery(Query query, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
120+
collectVoid(das -> das.deleteByQuery(query, propertyPath));
121+
}
122+
113123
@Override
114124
public <T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType) {
115125
collectVoid(das -> das.acquireLockById(id, lockMode, domainType));
@@ -121,8 +131,8 @@ public <T> void acquireLockAll(LockMode lockMode, Class<T> domainType) {
121131
}
122132

123133
@Override
124-
public <T> List<?> acquireLockAndFindIdsByQuery(Query query, LockMode lockMode, Class<T> domainType) {
125-
returncollect(das -> das.acquireLockAndFindIdsByQuery(query, lockMode, domainType));
134+
public <T> voidacquireLockByQuery(Query query, LockMode lockMode, Class<T> domainType) {
135+
collectVoid(das -> das.acquireLockByQuery(query, lockMode, domainType));
126136
}
127137

128138
@Override

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,22 @@ public interface DataAccessStrategy extends ReadingDataAccessStrategy, RelationR
178178
*/
179179
void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> propertyPath);
180180

181+
/**
182+
* Deletes all root entities of the given domain type that match the given {@link Query}.
183+
*
184+
* @param query the query specifying which rows to delete. Must not be {@code null}.
185+
* @param domainType the domain type of the entity. Must not be {@code null}.
186+
*/
187+
void deleteRootByQuery(Query query, Class<?> domainType);
188+
189+
/**
190+
* Deletes entities reachable via the given {@link PersistentPropertyPath} from root entities that match the given {@link Query}.
191+
*
192+
* @param query the query specifying which root entities to consider for deleting related entities. Must not be {@code null}.
193+
* @param propertyPath Leading from the root object to the entities to be deleted. Must not be {@code null}.
194+
*/
195+
void deleteByQuery(Query query, PersistentPropertyPath<RelationalPersistentProperty> propertyPath);
196+
181197
/**
182198
* Acquire a lock on the aggregate specified by id.
183199
*
@@ -196,16 +212,14 @@ public interface DataAccessStrategy extends ReadingDataAccessStrategy, RelationR
196212
<T> void acquireLockAll(LockMode lockMode, Class<T> domainType);
197213

198214
/**
199-
* Acquire a lock on all aggregates that match the given {@link Query} and return their identifiers.
200-
* The resulting SQL will include a {@code SELECT id FROM ... WHERE ... (LOCK CLAUSE)} to retrieve and lock the matching rows.
215+
* Acquire a lock on all aggregates that match the given {@link Query}.
201216
*
202217
* @param query the query specifying which entities to lock. Must not be {@code null}.
203218
* @param lockMode the lock mode to apply to the query (e.g. {@code FOR UPDATE}). Must not be {@code null}.
204219
* @param domainType the domain type of the entities to be locked. Must not be {@code null}.
205220
* @param <T> the type of the domain entity.
206-
* @return a {@link List} of ids corresponding to the rows locked by the query.
207221
*/
208-
<T> List<?> acquireLockAndFindIdsByQuery(Query query, LockMode lockMode, Class<T> domainType);
222+
<T> voidacquireLockByQuery(Query query, LockMode lockMode, Class<T> domainType);
209223

210224
/**
211225
* Counts the rows in the table representing the given domain type.

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import org.springframework.data.relational.core.sql.LockMode;
4040
import org.springframework.data.relational.core.sql.SqlIdentifier;
4141
import org.springframework.jdbc.core.RowMapper;
42-
import org.springframework.jdbc.core.SingleColumnRowMapper;
4342
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
4443
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
4544
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
@@ -245,6 +244,30 @@ public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> prope
245244
operations.getJdbcOperations().update(sql(getBaseType(propertyPath)).createDeleteAllSql(propertyPath));
246245
}
247246

247+
@Override
248+
public void deleteRootByQuery(Query query, Class<?> domainType) {
249+
250+
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
251+
String deleteSql = sql(domainType).createDeleteByQuery(query, parameterSource);
252+
253+
operations.update(deleteSql, parameterSource);
254+
}
255+
256+
@Override
257+
public void deleteByQuery(Query query, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
258+
259+
RelationalPersistentEntity<?> rootEntity = context.getRequiredPersistentEntity(getBaseType(propertyPath));
260+
261+
RelationalPersistentProperty referencingProperty = propertyPath.getLeafProperty();
262+
263+
Assert.notNull(referencingProperty, "No property found matching the PropertyPath " + propertyPath);
264+
265+
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
266+
String deleteSql = sql(rootEntity.getType()).createDeleteInSubselectByPath(query, parameterSource, propertyPath);
267+
268+
operations.update(deleteSql, parameterSource);
269+
}
270+
248271
@Override
249272
public <T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType) {
250273

@@ -262,25 +285,12 @@ public <T> void acquireLockAll(LockMode lockMode, Class<T> domainType) {
262285
}
263286

264287
@Override
265-
public <T> List<?> acquireLockAndFindIdsByQuery(Query query, LockMode lockMode, Class<T> domainType) {
288+
public <T> voidacquireLockByQuery(Query query, LockMode lockMode, Class<T> domainType) {
266289

267290
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
268-
String acquireLockByQuerySql = sql(domainType).getAcquireLockAndFindIdsByQuery(query, parameterSource, lockMode);
291+
String acquireLockByQuerySql = sql(domainType).getAcquireLockByQuery(query, parameterSource, lockMode);
269292

270-
RelationalPersistentEntity<?> entity = context.getRequiredPersistentEntity(domainType);
271-
RelationalPersistentProperty idProperty = entity.getRequiredIdProperty();
272-
273-
return operations.query(acquireLockByQuerySql, parameterSource, getIdRowMapper(idProperty));
274-
}
275-
276-
private RowMapper<?> getIdRowMapper(RelationalPersistentProperty idProperty) {
277-
RelationalPersistentEntity<?> complexId = context.getPersistentEntity(idProperty.getType());
278-
279-
if (complexId == null) {
280-
return SingleColumnRowMapper.newInstance(idProperty.getType(), converter.getConversionService());
281-
} else {
282-
return new EntityRowMapper<>(context.getRequiredPersistentEntity(idProperty.getType()), converter);
283-
}
293+
operations.query(acquireLockByQuerySql, parameterSource, ResultSet::next);
284294
}
285295

286296
@Override

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> prope
110110
delegate.deleteAll(propertyPath);
111111
}
112112

113+
@Override
114+
public void deleteRootByQuery(Query query, Class<?> domainType) {
115+
delegate.deleteRootByQuery(query, domainType);
116+
}
117+
118+
@Override
119+
public void deleteByQuery(Query query, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
120+
delegate.deleteByQuery(query, propertyPath);
121+
}
122+
113123
@Override
114124
public <T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType) {
115125
delegate.acquireLockById(id, lockMode, domainType);
@@ -121,8 +131,8 @@ public <T> void acquireLockAll(LockMode lockMode, Class<T> domainType) {
121131
}
122132

123133
@Override
124-
public <T> List<?> acquireLockAndFindIdsByQuery(Query query, LockMode lockMode, Class<T> domainType) {
125-
returndelegate.acquireLockAndFindIdsByQuery(query, lockMode, domainType);
134+
public <T> voidacquireLockByQuery(Query query, LockMode lockMode, Class<T> domainType) {
135+
delegate.acquireLockByQuery(query, lockMode, domainType);
126136
}
127137

128138
@Override

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ String getAcquireLockAll(LockMode lockMode) {
386386
* @param lockMode Lock clause mode.
387387
* @return the SQL statement as a {@link String}. Guaranteed to be not {@literal null}.
388388
*/
389-
String getAcquireLockAndFindIdsByQuery(Query query, MapSqlParameterSource parameterSource, LockMode lockMode) {
389+
String getAcquireLockByQuery(Query query, MapSqlParameterSource parameterSource, LockMode lockMode) {
390390
return this.createAcquireLockByQuery(query, parameterSource, lockMode);
391391
}
392392

@@ -505,6 +505,76 @@ String createDeleteInByPath(PersistentPropertyPath<RelationalPersistentProperty>
505505
return createDeleteByPathAndCriteria(mappingContext.getAggregatePath(path), this::inCondition);
506506
}
507507

508+
/**
509+
* Create a {@code DELETE FROM ... WHERE ...} SQL statement based on the given {@link Query}.
510+
*
511+
* @param query the query object defining filter criteria; must not be {@literal null}.
512+
* @param parameterSource the parameter bindings for the query; must not be {@literal null}.
513+
* @return the SQL DELETE statement as a {@link String}; guaranteed to be not {@literal null}.
514+
*/
515+
public String createDeleteByQuery(Query query, MapSqlParameterSource parameterSource) {
516+
Assert.notNull(parameterSource, "parameterSource must not be null");
517+
518+
Table table = this.getTable();
519+
520+
DeleteBuilder.DeleteWhere builder = Delete.builder()
521+
.from(table);
522+
523+
query.getCriteria()
524+
.filter(criteria -> !criteria.isEmpty())
525+
.ifPresent(criteria ->
526+
builder.where(queryMapper.getMappedObject(parameterSource, criteria, table, entity))
527+
);
528+
529+
return render(builder.build());
530+
}
531+
532+
/**
533+
* Creates a {@code DELETE} SQL query that targets a specific table defined by the given {@link PersistentPropertyPath},
534+
* and applies filtering using a subselect based on the provided {@link Query}.
535+
*
536+
* @param query the query object containing the filtering criteria; must not be {@literal null}.
537+
* @param parameterSource the source for parameter bindings used in the query; must not be {@literal null}.
538+
* @param propertyPath must not be {@literal null}.
539+
* @return the DELETE SQL statement as a {@link String}. Guaranteed to be not {@literal null}.
540+
*/
541+
public String createDeleteInSubselectByPath(Query query, MapSqlParameterSource parameterSource,
542+
PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
543+
544+
Assert.notNull(parameterSource, "parameterSource must not be null");
545+
546+
AggregatePath path = mappingContext.getAggregatePath(propertyPath);
547+
548+
return createDeleteByPathAndCriteria(path, columnMap -> {
549+
Select subSelect = createRootIdSubSelectByQuery(query, parameterSource);
550+
Collection<Column> columns = columnMap.values();
551+
Expression expression = columns.size() == 1 ? columns.iterator().next() : TupleExpression.create(columns);
552+
return Conditions.in(expression, subSelect);
553+
});
554+
}
555+
556+
/**
557+
* Creates a subselect that retrieves root entity IDs filtered by the given query.
558+
*/
559+
private Select createRootIdSubSelectByQuery(Query query, MapSqlParameterSource parameterSource) {
560+
561+
Table table = this.getTable();
562+
563+
SelectBuilder.SelectWhere selectBuilder = StatementBuilder
564+
.select(getIdColumns())
565+
.from(table);
566+
567+
query.getCriteria()
568+
.filter(criteria -> !criteria.isEmpty())
569+
.ifPresent(criteria ->
570+
selectBuilder.where(
571+
queryMapper.getMappedObject(parameterSource, criteria, table, entity)
572+
)
573+
);
574+
575+
return selectBuilder.build();
576+
}
577+
508578
/**
509579
* Constructs a where condition. The where condition will be of the form {@literal <columns> IN :bind-marker}
510580
*/
@@ -614,10 +684,18 @@ private String createAcquireLockByQuery(Query query, MapSqlParameterSource param
614684
Table table = this.getTable();
615685

616686
SelectBuilder.SelectWhere selectBuilder = StatementBuilder
617-
.select(getIdColumns())
687+
.select(getSingleNonNullColumn())
618688
.from(table);
619689

620-
Select select = applyQueryOnSelect(query, parameterSource, selectBuilder)
690+
query.getCriteria()
691+
.filter(criteria -> !criteria.isEmpty())
692+
.ifPresent(criteria ->
693+
selectBuilder.where(
694+
queryMapper.getMappedObject(parameterSource, criteria, table, entity)
695+
)
696+
);
697+
698+
Select select = selectBuilder
621699
.lock(lockMode)
622700
.build();
623701

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,16 @@ public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> prope
231231
sqlSession().delete(statement, parameter);
232232
}
233233

234+
@Override
235+
public void deleteRootByQuery(Query query, Class<?> domainType) {
236+
throw new UnsupportedOperationException("Not implemented");
237+
}
238+
239+
@Override
240+
public void deleteByQuery(Query query, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
241+
throw new UnsupportedOperationException("Not implemented");
242+
}
243+
234244
@Override
235245
public <T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType) {
236246

@@ -255,7 +265,7 @@ public <T> void acquireLockAll(LockMode lockMode, Class<T> domainType) {
255265
}
256266

257267
@Override
258-
public <T> List<?> acquireLockAndFindIdsByQuery(Query query, LockMode lockMode, Class<T> domainType) {
268+
public <T> voidacquireLockByQuery(Query query, LockMode lockMode, Class<T> domainType) {
259269
throw new UnsupportedOperationException("Not implemented");
260270
}
261271

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /