Skip to content

Commit 6911bfb

Browse files
committed
Polishing.
Introduces the BatchedActions abstraction to encapsulate the different ways singular actions get combined into batched actions. Original pull request #1230 Original pull request #1229 Original pull request #1228 Original pull request #1211
1 parent 64a07e6 commit 6911bfb

File tree

34 files changed

+540
-285
lines changed

34 files changed

+540
-285
lines changed

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

+5-6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
* @author Myeonghyeon Lee
5151
* @author Chirag Tailor
5252
*/
53+
@SuppressWarnings("rawtypes")
5354
class JdbcAggregateChangeExecutionContext {
5455

5556
private static final String UPDATE_FAILED = "Failed to update entity [%s]. Id [%s] not found in database.";
@@ -192,16 +193,14 @@ private Object getParentId(DbAction.WithDependingOn<?> action) {
192193
private DbAction.WithEntity<?> getIdOwningAction(DbAction.WithEntity<?> action,
193194
PersistentPropertyPathExtension idPath) {
194195

195-
if (!(action instanceof DbAction.WithDependingOn)) {
196+
if (!(action instanceof DbAction.WithDependingOn<?> withDependingOn)) {
196197

197198
Assert.state(idPath.getLength() == 0,
198199
"When the id path is not empty the id providing action should be of type WithDependingOn");
199200

200201
return action;
201202
}
202203

203-
DbAction.WithDependingOn<?> withDependingOn = (DbAction.WithDependingOn<?>) action;
204-
205204
if (idPath.matches(withDependingOn.getPropertyPath())) {
206205
return action;
207206
}
@@ -257,9 +256,8 @@ <T> List<T> populateIdsIfNecessary() {
257256
roots.add((T) newEntity);
258257
}
259258

260-
// the id property was immutable so we have to propagate changes up the tree
261-
if (newEntity != action.getEntity() && action instanceof DbAction.Insert) {
262-
DbAction.Insert<?> insert = (DbAction.Insert<?>) action;
259+
// the id property was immutable, so we have to propagate changes up the tree
260+
if (newEntity != action.getEntity() && action instanceof DbAction.Insert<?> insert) {
263261

264262
Pair<?, ?> qualifier = insert.getQualifier();
265263

@@ -463,6 +461,7 @@ public List createEmptyInstance() {
463461
public List add(@Nullable List list, @Nullable Object qualifier, Object value) {
464462

465463
Assert.notNull(list, "List must not be null.");
464+
Assert.notNull(qualifier, "ListAggregator can't handle a null qualifier.");
466465

467466
int index = (int) qualifier;
468467
if (index >= list.size()) {

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import org.springframework.lang.Nullable;
2222

2323
/**
24-
* Specifies a operations one can perform on a database, based on an <em>Domain Type</em>.
24+
* Specifies operations one can perform on a database, based on an <em>Domain Type</em>.
2525
*
2626
* @author Jens Schauder
2727
* @author Thomas Lang
@@ -90,7 +90,7 @@ public interface JdbcAggregateOperations {
9090
<T> void deleteAllById(Iterable<?> ids, Class<T> domainType);
9191

9292
/**
93-
* Delete an aggregate identified by it's aggregate root.
93+
* Delete an aggregate identified by its aggregate root.
9494
*
9595
* @param aggregateRoot to delete. Must not be {@code null}.
9696
* @param domainType the type of the aggregate root. Must not be {@code null}.

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

+4-10
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ public <T> void deleteAllById(Iterable<?> ids, Class<T> domainType) {
287287
.forDelete(domainType);
288288

289289
ids.forEach(id -> {
290+
290291
DeleteAggregateChange<T> change = createDeletingChange(id, null, domainType);
291292
triggerBeforeDelete(null, id, change);
292293
batchingAggregateChange.add(change);
@@ -316,6 +317,7 @@ public <T> void deleteAll(Iterable<? extends T> instances, Class<T> domainType)
316317
Map<Object, T> instancesBeforeExecute = new LinkedHashMap<>();
317318

318319
instances.forEach(instance -> {
320+
319321
Object id = context.getRequiredPersistentEntity(domainType).getIdentifierAccessor(instance)
320322
.getRequiredIdentifier();
321323
DeleteAggregateChange<T> change = createDeletingChange(id, instance, domainType);
@@ -384,6 +386,7 @@ private <T> List<T> performSaveAll(Iterable<T> instances) {
384386

385387
for (T instance : instances) {
386388
if (batchingAggregateChange == null) {
389+
// noinspection unchecked
387390
batchingAggregateChange = BatchingAggregateChange.forSave((Class<T>) ClassUtils.getUserClass(instance));
388391
}
389392
batchingAggregateChange.add(beforeExecute(instance, changeCreatorSelectorForSave(instance)));
@@ -540,15 +543,6 @@ private <T> T triggerBeforeDelete(@Nullable T aggregateRoot, Object id, MutableA
540543
return null;
541544
}
542545

543-
private static class EntityAndPreviousVersion<T> {
544-
545-
private final T entity;
546-
private final Number version;
547-
548-
EntityAndPreviousVersion(T entity, @Nullable Number version) {
549-
550-
this.entity = entity;
551-
this.version = version;
552-
}
546+
private record EntityAndPreviousVersion<T> (T entity, @Nullable Number version) {
553547
}
554548
}

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
2828
import org.springframework.data.relational.core.sql.LockMode;
2929

30+
import static java.lang.Boolean.*;
31+
3032
/**
3133
* Delegates each methods to the {@link DataAccessStrategy}s passed to the constructor in turn until the first that does
3234
* not throw an exception.
@@ -155,15 +157,14 @@ public <T> Iterable<T> findAll(Class<T> domainType, Pageable pageable) {
155157

156158
private <T> T collect(Function<DataAccessStrategy, T> function) {
157159

158-
// Keep <T> as Eclipse fails to compile if <> is used.
159160
return strategies.stream().collect(new FunctionCollector<>(function));
160161
}
161162

162163
private void collectVoid(Consumer<DataAccessStrategy> consumer) {
163164

164165
collect(das -> {
165166
consumer.accept(das);
166-
return null;
167+
return TRUE;
167168
});
168169
}
169170
}

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

+12-6
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,7 @@ public <T> void deleteWithVersion(Object id, Class<T> domainType, Number previou
183183
@Override
184184
public void delete(Object rootId, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
185185

186-
RelationalPersistentEntity<?> rootEntity = context
187-
.getRequiredPersistentEntity(propertyPath.getBaseProperty().getOwner().getType());
186+
RelationalPersistentEntity<?> rootEntity = context.getRequiredPersistentEntity(getBaseType(propertyPath));
188187

189188
RelationalPersistentProperty referencingProperty = propertyPath.getLeafProperty();
190189
Assert.notNull(referencingProperty, "No property found matching the PropertyPath " + propertyPath);
@@ -199,8 +198,7 @@ public void delete(Object rootId, PersistentPropertyPath<RelationalPersistentPro
199198
@Override
200199
public void delete(Iterable<Object> rootIds, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
201200

202-
RelationalPersistentEntity<?> rootEntity = context
203-
.getRequiredPersistentEntity(propertyPath.getBaseProperty().getOwner().getType());
201+
RelationalPersistentEntity<?> rootEntity = context.getRequiredPersistentEntity(getBaseType(propertyPath));
204202

205203
RelationalPersistentProperty referencingProperty = propertyPath.getLeafProperty();
206204

@@ -220,8 +218,7 @@ public <T> void deleteAll(Class<T> domainType) {
220218
@Override
221219
public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
222220

223-
operations.getJdbcOperations()
224-
.update(sql(propertyPath.getBaseProperty().getOwner().getType()).createDeleteAllSql(propertyPath));
221+
operations.getJdbcOperations().update(sql(getBaseType(propertyPath)).createDeleteAllSql(propertyPath));
225222
}
226223

227224
@Override
@@ -365,4 +362,13 @@ private <T> SqlIdentifier getIdColumn(Class<T> domainType) {
365362
return Optional.ofNullable(context.getRequiredPersistentEntity(domainType).getIdProperty())
366363
.map(RelationalPersistentProperty::getColumnName).orElse(null);
367364
}
365+
366+
private Class<?> getBaseType(PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
367+
368+
RelationalPersistentProperty baseProperty = propertyPath.getBaseProperty();
369+
370+
Assert.notNull(baseProperty, "The base property must not be null");
371+
372+
return baseProperty.getOwner().getType();
373+
}
368374
}

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

+12-12
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class SqlGenerator {
103103
}
104104

105105
/**
106-
* Construct a IN-condition based on a {@link Select Sub-Select} which selects the ids (or stand ins for ids) of the
106+
* Construct an IN-condition based on a {@link Select Sub-Select} which selects the ids (or stand-ins for ids) of the
107107
* given {@literal path} to those that reference the root entities specified by the {@literal rootCondition}.
108108
*
109109
* @param path specifies the table and id to select
@@ -135,7 +135,7 @@ private Condition getSubselectCondition(PersistentPropertyPathExtension path,
135135
innerCondition = rootCondition.apply(selectFilterColumn);
136136
} else {
137137

138-
// otherwise we need another layer of subselect
138+
// otherwise, we need another layer of subselect
139139
innerCondition = getSubselectCondition(parentPath, rootCondition, selectFilterColumn);
140140
}
141141

@@ -502,7 +502,7 @@ private SelectBuilder.SelectOrdered applyPagination(Pageable pageable, SelectBui
502502
@Nullable
503503
Column getColumn(PersistentPropertyPathExtension path) {
504504

505-
// an embedded itself doesn't give an column, its members will though.
505+
// an embedded itself doesn't give a column, its members will though.
506506
// if there is a collection or map on the path it won't get selected at all, but it will get loaded with a separate
507507
// select
508508
// only the parent path is considered in order to handle arrays that get stored as BINARY properly
@@ -512,7 +512,7 @@ Column getColumn(PersistentPropertyPathExtension path) {
512512

513513
if (path.isEntity()) {
514514

515-
// Simple entities without id include there backreference as an synthetic id in order to distinguish null entities
515+
// Simple entities without id include there backreference as a synthetic id in order to distinguish null entities
516516
// from entities with only null values.
517517

518518
if (path.isQualified() //
@@ -622,7 +622,7 @@ private UpdateBuilder.UpdateWhereAndOr createBaseUpdate() {
622622

623623
Table table = getTable();
624624

625-
List<AssignValue> assignments = columns.getUpdateableColumns() //
625+
List<AssignValue> assignments = columns.getUpdatableColumns() //
626626
.stream() //
627627
.map(columnName -> Assignments.value( //
628628
table.column(columnName), //
@@ -807,7 +807,7 @@ static class Columns {
807807
private final List<SqlIdentifier> nonIdColumnNames = new ArrayList<>();
808808
private final Set<SqlIdentifier> readOnlyColumnNames = new HashSet<>();
809809
private final Set<SqlIdentifier> insertableColumns;
810-
private final Set<SqlIdentifier> updateableColumns;
810+
private final Set<SqlIdentifier> updatableColumns;
811811

812812
Columns(RelationalPersistentEntity<?> entity,
813813
MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty> mappingContext,
@@ -823,12 +823,12 @@ static class Columns {
823823

824824
this.insertableColumns = Collections.unmodifiableSet(insertable);
825825

826-
Set<SqlIdentifier> updateable = new LinkedHashSet<>(columnNames);
826+
Set<SqlIdentifier> updatable = new LinkedHashSet<>(columnNames);
827827

828-
updateable.removeAll(idColumnNames);
829-
updateable.removeAll(readOnlyColumnNames);
828+
updatable.removeAll(idColumnNames);
829+
updatable.removeAll(readOnlyColumnNames);
830830

831-
this.updateableColumns = Collections.unmodifiableSet(updateable);
831+
this.updatableColumns = Collections.unmodifiableSet(updatable);
832832
}
833833

834834
private void populateColumnNameCache(RelationalPersistentEntity<?> entity, String prefix) {
@@ -881,8 +881,8 @@ Set<SqlIdentifier> getInsertableColumns() {
881881
/**
882882
* @return Column names that can be used for {@code UPDATE}.
883883
*/
884-
Set<SqlIdentifier> getUpdateableColumns() {
885-
return updateableColumns;
884+
Set<SqlIdentifier> getUpdatableColumns() {
885+
return updatableColumns;
886886
}
887887
}
888888
}

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

+18-18
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@
2121
import java.util.HashMap;
2222
import java.util.List;
2323
import java.util.Map;
24-
import java.util.stream.Collectors;
2524

26-
import org.apache.commons.logging.Log;
27-
import org.apache.commons.logging.LogFactory;
2825
import org.apache.ibatis.session.SqlSession;
2926
import org.mybatis.spring.SqlSessionTemplate;
3027
import org.springframework.dao.EmptyResultDataAccessException;
@@ -63,11 +60,9 @@
6360
*/
6461
public class MyBatisDataAccessStrategy implements DataAccessStrategy {
6562

66-
private static final Log LOG = LogFactory.getLog(MyBatisDataAccessStrategy.class);
6763
private static final String VERSION_SQL_PARAMETER_NAME_OLD = "___oldOptimisticLockingVersion";
6864

6965
private final SqlSession sqlSession;
70-
private final IdentifierProcessing identifierProcessing;
7166
private NamespaceStrategy namespaceStrategy = NamespaceStrategy.DEFAULT_INSTANCE;
7267

7368
/**
@@ -133,7 +128,6 @@ public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingC
133128
public MyBatisDataAccessStrategy(SqlSession sqlSession, IdentifierProcessing identifierProcessing) {
134129

135130
this.sqlSession = sqlSession;
136-
this.identifierProcessing = identifierProcessing;
137131
}
138132

139133
/**
@@ -210,7 +204,7 @@ public <T> void deleteWithVersion(Object id, Class<T> domainType, Number previou
210204
@Override
211205
public void delete(Object rootId, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
212206

213-
Class<?> ownerType = propertyPath.getBaseProperty().getOwner().getType();
207+
Class<?> ownerType = getOwnerTyp(propertyPath);
214208
String statement = namespace(ownerType) + ".delete-" + toDashPath(propertyPath);
215209
Class<?> leafType = propertyPath.getRequiredLeafProperty().getTypeInformation().getType();
216210
MyBatisContext parameter = new MyBatisContext(rootId, null, leafType, Collections.emptyMap());
@@ -234,10 +228,9 @@ public <T> void deleteAll(Class<T> domainType) {
234228
@Override
235229
public void deleteAll(PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
236230

237-
Class<?> baseType = propertyPath.getBaseProperty().getOwner().getType();
238231
Class<?> leafType = propertyPath.getRequiredLeafProperty().getTypeInformation().getType();
239232

240-
String statement = namespace(baseType) + ".deleteAll-" + toDashPath(propertyPath);
233+
String statement = namespace(getOwnerTyp(propertyPath)) + ".deleteAll-" + toDashPath(propertyPath);
241234
MyBatisContext parameter = new MyBatisContext(null, null, leafType, Collections.emptyMap());
242235
sqlSession().delete(statement, parameter);
243236
}
@@ -291,8 +284,7 @@ public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
291284
public Iterable<Object> findAllByPath(Identifier identifier,
292285
PersistentPropertyPath<? extends RelationalPersistentProperty> path) {
293286

294-
String statementName = namespace(path.getBaseProperty().getOwner().getType()) + ".findAllByPath-"
295-
+ path.toDotPath();
287+
String statementName = namespace(getOwnerTyp(path)) + ".findAllByPath-" + path.toDotPath();
296288

297289
return sqlSession().selectList(statementName,
298290
new MyBatisContext(identifier, null, path.getRequiredLeafProperty().getType()));
@@ -333,12 +325,6 @@ public long count(Class<?> domainType) {
333325
return sqlSession().selectOne(statement, parameter);
334326
}
335327

336-
private Map<String, Object> convertToParameterMap(Map<SqlIdentifier, Object> additionalParameters) {
337-
338-
return additionalParameters.entrySet().stream() //
339-
.collect(Collectors.toMap(e -> e.getKey().toSql(identifierProcessing), Map.Entry::getValue));
340-
}
341-
342328
private String namespace(Class<?> domainType) {
343329
return this.namespaceStrategy.getNamespace(domainType);
344330
}
@@ -348,6 +334,20 @@ private SqlSession sqlSession() {
348334
}
349335

350336
private static String toDashPath(PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
351-
return propertyPath.toDotPath().replaceAll("\\.", "-");
337+
338+
String dotPath = propertyPath.toDotPath();
339+
if (dotPath == null) {
340+
return "";
341+
}
342+
return dotPath.replaceAll("\\.", "-");
343+
}
344+
345+
private Class<?> getOwnerTyp(PersistentPropertyPath<? extends RelationalPersistentProperty> propertyPath) {
346+
347+
RelationalPersistentProperty baseProperty = propertyPath.getBaseProperty();
348+
349+
Assert.notNull(baseProperty, "BaseProperty must not be null.");
350+
351+
return baseProperty.getOwner().getType();
352352
}
353353
}

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java

+3-6
Original file line numberDiff line numberDiff line change
@@ -406,22 +406,19 @@ private static Map<String, Tag> createTagMap(Object... keysAndValues) {
406406

407407
DbAction.Insert<?> createInsert(String propertyName, Object value, @Nullable Object key) {
408408

409-
DbAction.Insert<Object> insert = new DbAction.Insert<>(value,
409+
return new DbAction.Insert<>(value,
410410
context.getPersistentPropertyPath(propertyName, DummyEntity.class), rootInsert,
411411
singletonMap(toPath(propertyName), key), IdValueSource.GENERATED);
412-
413-
return insert;
414412
}
415413

416414
DbAction.Insert<?> createDeepInsert(String propertyName, Object value, Object key,
417415
@Nullable DbAction.Insert<?> parentInsert) {
418416

419417
PersistentPropertyPath<RelationalPersistentProperty> propertyPath = toPath(
420418
parentInsert.getPropertyPath().toDotPath() + "." + propertyName);
421-
DbAction.Insert<Object> insert = new DbAction.Insert<>(value, propertyPath, parentInsert,
422-
singletonMap(propertyPath, key), IdValueSource.GENERATED);
423419

424-
return insert;
420+
return new DbAction.Insert<>(value, propertyPath, parentInsert,
421+
singletonMap(propertyPath, key), IdValueSource.GENERATED);
425422
}
426423

427424
PersistentPropertyPath<RelationalPersistentProperty> toPath(String path) {

0 commit comments

Comments
 (0)