Skip to content

Commit 48637ed

Browse files
committed
JdbcAggregateOperations delete by query
Issue link: #1978 Add deleteAllByQuery method to JdbcAggregateOperations This method enables deleting aggregates based on a query by performing the following steps: 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 query conditions. But if the query has no criteria, deletion is performed in the same way as deleteAll method of JdbcAggregateOperations Signed-off-by: JaeYeon Kim <[email protected]>
1 parent 273fddf commit 48637ed

File tree

15 files changed

+505
-0
lines changed

15 files changed

+505
-0
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* @author Myeonghyeon Lee
3131
* @author Chirag Tailor
3232
* @author Mikhail Polivakha
33+
* @author Jaeyeon Kim
3334
* @since 2.0
3435
*/
3536
class AggregateChangeExecutor {
@@ -101,10 +102,16 @@ private void execute(DbAction<?> action, JdbcAggregateChangeExecutionContext exe
101102
executionContext.executeBatchDeleteRoot(batchDeleteRoot);
102103
} else if (action instanceof DbAction.DeleteAllRoot<?> deleteAllRoot) {
103104
executionContext.executeDeleteAllRoot(deleteAllRoot);
105+
} else if (action instanceof DbAction.DeleteRootByQuery<?> deleteRootByQuery) {
106+
executionContext.excuteDeleteRootByQuery(deleteRootByQuery);
107+
} else if (action instanceof DbAction.DeleteByQuery<?> deleteByQuery) {
108+
executionContext.excuteDeleteByQuery(deleteByQuery);
104109
} else if (action instanceof DbAction.AcquireLockRoot<?> acquireLockRoot) {
105110
executionContext.executeAcquireLock(acquireLockRoot);
106111
} else if (action instanceof DbAction.AcquireLockAllRoot<?> acquireLockAllRoot) {
107112
executionContext.executeAcquireLockAllRoot(acquireLockAllRoot);
113+
} else if (action instanceof DbAction.AcquireLockAllRootByQuery<?> acquireLockAllRootByQuery) {
114+
executionContext.executeAcquireLockRootByQuery(acquireLockAllRootByQuery);
108115
} else {
109116
throw new RuntimeException("unexpected action");
110117
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
* @author Myeonghyeon Lee
5252
* @author Chirag Tailor
5353
* @author Mark Paluch
54+
* @author Jaeyeon Kim
5455
*/
5556
@SuppressWarnings("rawtypes")
5657
class JdbcAggregateChangeExecutionContext {
@@ -160,6 +161,16 @@ <T> void executeDeleteAll(DbAction.DeleteAll<T> delete) {
160161
accessStrategy.deleteAll(delete.propertyPath());
161162
}
162163

164+
<T> void excuteDeleteRootByQuery(DbAction.DeleteRootByQuery<T> deleteRootByQuery) {
165+
166+
accessStrategy.deleteByQuery(deleteRootByQuery.getQuery(), deleteRootByQuery.getEntityType());
167+
}
168+
169+
<T> void excuteDeleteByQuery(DbAction.DeleteByQuery<T> deleteByQuery) {
170+
171+
accessStrategy.deleteByQuery(deleteByQuery.getQuery(), deleteByQuery.propertyPath());
172+
}
173+
163174
<T> void executeAcquireLock(DbAction.AcquireLockRoot<T> acquireLock) {
164175
accessStrategy.acquireLockById(acquireLock.getId(), LockMode.PESSIMISTIC_WRITE, acquireLock.getEntityType());
165176
}
@@ -168,6 +179,10 @@ <T> void executeAcquireLockAllRoot(DbAction.AcquireLockAllRoot<T> acquireLock) {
168179
accessStrategy.acquireLockAll(LockMode.PESSIMISTIC_WRITE, acquireLock.getEntityType());
169180
}
170181

182+
<T> void executeAcquireLockRootByQuery(DbAction.AcquireLockAllRootByQuery<T> acquireLock) {
183+
accessStrategy.acquireLockByQuery(acquireLock.getQuery(), LockMode.PESSIMISTIC_WRITE, acquireLock.getEntityType());
184+
}
185+
171186
private void add(DbActionExecutionResult result) {
172187
results.put(result.getAction(), result);
173188
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* @author Myeonghyeon Lee
4141
* @author Sergey Korotaev
4242
* @author Tomohiko Ozawa
43+
* @author Jaeyeon Kim
4344
*/
4445
public interface JdbcAggregateOperations {
4546

@@ -328,6 +329,15 @@ public interface JdbcAggregateOperations {
328329
*/
329330
<T> void deleteAll(Iterable<? extends T> aggregateRoots);
330331

332+
/**
333+
* Deletes all aggregates of the given type that match the provided query.
334+
*
335+
* @param query Must not be {@code null}.
336+
* @param domainType the type of the aggregate root. Must not be {@code null}.
337+
* @param <T> the type of the aggregate root.
338+
*/
339+
<T> void deleteAllByQuery(Query query, Class<T> domainType);
340+
331341
/**
332342
* Returns the {@link JdbcConverter}.
333343
*

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
* @author Diego Krupitza
7373
* @author Sergey Korotaev
7474
* @author Mikhail Polivakha
75+
* @author Jaeyeon Kim
7576
*/
7677
public class JdbcAggregateTemplate implements JdbcAggregateOperations, ApplicationContextAware {
7778

@@ -484,6 +485,17 @@ public <T> void deleteAll(Iterable<? extends T> instances) {
484485
}
485486
}
486487

488+
@Override
489+
public <T> void deleteAllByQuery(Query query, Class<T> domainType) {
490+
491+
Assert.notNull(query, "Query must not be null");
492+
Assert.notNull(domainType, "Domain type must not be null");
493+
494+
MutableAggregateChange<?> change = createDeletingChange(query, domainType);
495+
496+
executor.executeDelete(change);
497+
}
498+
487499
@Override
488500
public DataAccessStrategy getDataAccessStrategy() {
489501
return accessStrategy;
@@ -672,6 +684,13 @@ private MutableAggregateChange<?> createDeletingChange(Class<?> domainType) {
672684
return aggregateChange;
673685
}
674686

687+
private MutableAggregateChange<?> createDeletingChange(Query query, Class<?> domainType) {
688+
689+
MutableAggregateChange<?> aggregateChange = MutableAggregateChange.forDelete(domainType);
690+
jdbcEntityDeleteWriter.writeForQuery(query, aggregateChange);
691+
return aggregateChange;
692+
}
693+
675694
private <T> List<T> triggerAfterConvert(Iterable<T> all) {
676695

677696
List<T> result = new ArrayList<>();

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
* @author Chirag Tailor
5050
* @author Diego Krupitza
5151
* @author Sergey Korotaev
52+
* @author Jaeyeon Kim
5253
* @since 1.1
5354
*/
5455
public class CascadingDataAccessStrategy implements DataAccessStrategy {
@@ -132,6 +133,16 @@ public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> prope
132133
collectVoid(das -> das.deleteAll(propertyPath));
133134
}
134135

136+
@Override
137+
public void deleteByQuery(Query query, Class<?> domainType) {
138+
collectVoid(das -> das.deleteByQuery(query, domainType));
139+
}
140+
141+
@Override
142+
public void deleteByQuery(Query query, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
143+
collectVoid(das -> das.deleteByQuery(query, propertyPath));
144+
}
145+
135146
@Override
136147
public <T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType) {
137148
collectVoid(das -> das.acquireLockById(id, lockMode, domainType));
@@ -142,6 +153,11 @@ public <T> void acquireLockAll(LockMode lockMode, Class<T> domainType) {
142153
collectVoid(das -> das.acquireLockAll(lockMode, domainType));
143154
}
144155

156+
@Override
157+
public <T> void acquireLockByQuery(Query query, LockMode lockMode, Class<T> domainType) {
158+
collectVoid(das -> das.acquireLockByQuery(query, lockMode, domainType));
159+
}
160+
145161
@Override
146162
public long count(Class<?> domainType) {
147163
return collect(das -> das.count(domainType));

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
* @author Chirag Tailor
4646
* @author Diego Krupitza
4747
* @author Sergey Korotaev
48+
* @author Jaeyeon Kim
4849
*/
4950
public interface DataAccessStrategy extends ReadingDataAccessStrategy, RelationResolver {
5051

@@ -191,6 +192,22 @@ public interface DataAccessStrategy extends ReadingDataAccessStrategy, RelationR
191192
*/
192193
void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> propertyPath);
193194

195+
/**
196+
* Deletes all root entities of the given domain type that match the given {@link Query}.
197+
*
198+
* @param query the query specifying which rows to delete. Must not be {@code null}.
199+
* @param domainType the domain type of the entity. Must not be {@code null}.
200+
*/
201+
void deleteByQuery(Query query, Class<?> domainType);
202+
203+
/**
204+
* Deletes entities reachable via the given {@link PersistentPropertyPath} from root entities that match the given {@link Query}.
205+
*
206+
* @param query the query specifying which root entities to consider for deleting related entities. Must not be {@code null}.
207+
* @param propertyPath Leading from the root object to the entities to be deleted. Must not be {@code null}.
208+
*/
209+
void deleteByQuery(Query query, PersistentPropertyPath<RelationalPersistentProperty> propertyPath);
210+
194211
/**
195212
* Acquire a lock on the aggregate specified by id.
196213
*
@@ -208,6 +225,16 @@ public interface DataAccessStrategy extends ReadingDataAccessStrategy, RelationR
208225
*/
209226
<T> void acquireLockAll(LockMode lockMode, Class<T> domainType);
210227

228+
/**
229+
* Acquire a lock on all aggregates that match the given {@link Query}.
230+
*
231+
* @param query the query specifying which entities to lock. Must not be {@code null}.
232+
* @param lockMode the lock mode to apply to the query (e.g. {@code FOR UPDATE}). Must not be {@code null}.
233+
* @param domainType the domain type of the entities to be locked. Must not be {@code null}.
234+
* @param <T> the type of the domain entity.
235+
*/
236+
<T> void acquireLockByQuery(Query query, LockMode lockMode, Class<T> domainType);
237+
211238
/**
212239
* Counts the rows in the table representing the given domain type.
213240
*

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
* @author Diego Krupitza
6565
* @author Sergey Korotaev
6666
* @author Mikhail Polivakha
67+
* @author Jaeyeon Kim
6768
* @since 1.1
6869
*/
6970
public class DefaultDataAccessStrategy implements DataAccessStrategy {
@@ -256,6 +257,29 @@ public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> prope
256257
operations.getJdbcOperations().update(sql(getBaseType(propertyPath)).createDeleteAllSql(propertyPath));
257258
}
258259

260+
@Override
261+
public void deleteByQuery(Query query, Class<?> domainType) {
262+
263+
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
264+
String deleteSql = sql(domainType).createDeleteByQuery(query, parameterSource);
265+
266+
operations.update(deleteSql, parameterSource);
267+
}
268+
269+
@Override
270+
public void deleteByQuery(Query query, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
271+
272+
RelationalPersistentEntity<?> rootEntity = context.getRequiredPersistentEntity(getBaseType(propertyPath));
273+
274+
RelationalPersistentProperty referencingProperty = propertyPath.getLeafProperty();
275+
Assert.notNull(referencingProperty, "No property found matching the PropertyPath " + propertyPath);
276+
277+
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
278+
String deleteSql = sql(rootEntity.getType()).createDeleteInSubselectByPath(query, parameterSource, propertyPath);
279+
280+
operations.update(deleteSql, parameterSource);
281+
}
282+
259283
@Override
260284
public <T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType) {
261285

@@ -272,6 +296,15 @@ public <T> void acquireLockAll(LockMode lockMode, Class<T> domainType) {
272296
operations.getJdbcOperations().query(acquireLockAllSql, ResultSet::next);
273297
}
274298

299+
@Override
300+
public <T> void acquireLockByQuery(Query query, LockMode lockMode, Class<T> domainType) {
301+
302+
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
303+
String acquireLockByQuerySql = sql(domainType).getAcquireLockByQuery(query, parameterSource, lockMode);
304+
305+
operations.query(acquireLockByQuerySql, parameterSource, ResultSet::next);
306+
}
307+
275308
@Override
276309
public long count(Class<?> domainType) {
277310

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
* @author Chirag Tailor
4343
* @author Diego Krupitza
4444
* @author Sergey Korotaev
45+
* @author Jaeyeon Kim
4546
* @since 1.1
4647
*/
4748
public class DelegatingDataAccessStrategy implements DataAccessStrategy {
@@ -126,6 +127,16 @@ public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> prope
126127
delegate.deleteAll(propertyPath);
127128
}
128129

130+
@Override
131+
public void deleteByQuery(Query query, Class<?> domainType) {
132+
delegate.deleteByQuery(query, domainType);
133+
}
134+
135+
@Override
136+
public void deleteByQuery(Query query, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
137+
delegate.deleteByQuery(query, propertyPath);
138+
}
139+
129140
@Override
130141
public <T> void acquireLockById(Object id, LockMode lockMode, Class<T> domainType) {
131142
delegate.acquireLockById(id, lockMode, domainType);
@@ -136,6 +147,11 @@ public <T> void acquireLockAll(LockMode lockMode, Class<T> domainType) {
136147
delegate.acquireLockAll(lockMode, domainType);
137148
}
138149

150+
@Override
151+
public <T> void acquireLockByQuery(Query query, LockMode lockMode, Class<T> domainType) {
152+
delegate.acquireLockByQuery(query, lockMode, domainType);
153+
}
154+
139155
@Override
140156
public long count(Class<?> domainType) {
141157
return delegate.count(domainType);

0 commit comments

Comments
 (0)