Skip to content

Commit d10be27

Browse files
mhyeon-leeschauder
authored andcommitted
Add insertAll and updateAll for JdbcAggregateOperations.
Original pull request #1396 Closes #1395
1 parent 9d5c11e commit d10be27

File tree

3 files changed

+99
-16
lines changed

3 files changed

+99
-16
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* @author Milan Milanov
3434
* @author Chirag Tailor
3535
* @author Diego Krupitza
36+
* @author Myeonghyeon Lee
3637
*/
3738
public interface JdbcAggregateOperations {
3839

@@ -71,6 +72,19 @@ public interface JdbcAggregateOperations {
7172
*/
7273
<T> T insert(T instance);
7374

75+
/**
76+
* Inserts all aggregate instances, including all the members of each aggregate instance.
77+
* <p>
78+
* This is useful if the client provides an id for new aggregate roots.
79+
* </p>
80+
*
81+
* @param instances the aggregate roots to be inserted. Must not be {@code null}.
82+
* @param <T> the type of the aggregate root.
83+
* @return the saved instances.
84+
* @since 3.1
85+
*/
86+
<T> Iterable<T> insertAll(Iterable<T> instances);
87+
7488
/**
7589
* Dedicated update function. This skips the test if the aggregate root is new or not and always performs an update
7690
* operation.
@@ -81,6 +95,16 @@ public interface JdbcAggregateOperations {
8195
*/
8296
<T> T update(T instance);
8397

98+
/**
99+
* Updates all aggregate instances, including all the members of each aggregate instance.
100+
*
101+
* @param instances the aggregate roots to be inserted. Must not be {@code null}.
102+
* @param <T> the type of the aggregate root.
103+
* @return the saved instances.
104+
* @since 3.1
105+
*/
106+
<T> Iterable<T> updateAll(Iterable<T> instances);
107+
84108
/**
85109
* Counts the number of aggregates of a given type.
86110
*

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

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -164,15 +164,19 @@ public <T> T save(T instance) {
164164

165165
Assert.notNull(instance, "Aggregate instance must not be null");
166166

167-
return performSave(instance, changeCreatorSelectorForSave(instance));
167+
return performSave(new EntityAndChangeCreator<>(instance, changeCreatorSelectorForSave(instance)));
168168
}
169169

170170
@Override
171171
public <T> Iterable<T> saveAll(Iterable<T> instances) {
172172

173173
Assert.isTrue(instances.iterator().hasNext(), "Aggregate instances must not be empty");
174174

175-
return performSaveAll(instances);
175+
List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
176+
for (T instance : instances) {
177+
entityAndChangeCreators.add(new EntityAndChangeCreator<>(instance, changeCreatorSelectorForSave(instance)));
178+
}
179+
return performSaveAll(entityAndChangeCreators);
176180
}
177181

178182
/**
@@ -187,7 +191,21 @@ public <T> T insert(T instance) {
187191

188192
Assert.notNull(instance, "Aggregate instance must not be null");
189193

190-
return performSave(instance, entity -> createInsertChange(prepareVersionForInsert(entity)));
194+
return performSave(new EntityAndChangeCreator<>(
195+
instance, entity -> createInsertChange(prepareVersionForInsert(entity))));
196+
}
197+
198+
@Override
199+
public <T> Iterable<T> insertAll(Iterable<T> instances) {
200+
201+
Assert.isTrue(instances.iterator().hasNext(), "Aggregate instances must not be empty");
202+
203+
List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
204+
for (T instance : instances) {
205+
entityAndChangeCreators.add(new EntityAndChangeCreator<>(
206+
instance, entity -> createInsertChange(prepareVersionForInsert(entity))));
207+
}
208+
return performSaveAll(entityAndChangeCreators);
191209
}
192210

193211
/**
@@ -202,7 +220,21 @@ public <T> T update(T instance) {
202220

203221
Assert.notNull(instance, "Aggregate instance must not be null");
204222

205-
return performSave(instance, entity -> createUpdateChange(prepareVersionForUpdate(entity)));
223+
return performSave(new EntityAndChangeCreator<>(
224+
instance, entity -> createUpdateChange(prepareVersionForUpdate(entity))));
225+
}
226+
227+
@Override
228+
public <T> Iterable<T> updateAll(Iterable<T> instances) {
229+
230+
Assert.isTrue(instances.iterator().hasNext(), "Aggregate instances must not be empty");
231+
232+
List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
233+
for (T instance : instances) {
234+
entityAndChangeCreators.add(new EntityAndChangeCreator<>(
235+
instance, entity -> createUpdateChange(prepareVersionForUpdate(entity))));
236+
}
237+
return performSaveAll(entityAndChangeCreators);
206238
}
207239

208240
@Override
@@ -401,13 +433,13 @@ private <T> T afterExecute(AggregateChange<T> change, T entityAfterExecution) {
401433
return triggerAfterSave(entityAfterExecution, change);
402434
}
403435

404-
private <T> RootAggregateChange<T> beforeExecute(T aggregateRoot, Function<T, RootAggregateChange<T>> changeCreator) {
436+
private <T> RootAggregateChange<T> beforeExecute(EntityAndChangeCreator<T> instance) {
405437

406-
Assert.notNull(aggregateRoot, "Aggregate instance must not be null");
438+
Assert.notNull(instance.entity, "Aggregate instance must not be null");
407439

408-
aggregateRoot = triggerBeforeConvert(aggregateRoot);
440+
T aggregateRoot = triggerBeforeConvert(instance.entity);
409441

410-
RootAggregateChange<T> change = changeCreator.apply(aggregateRoot);
442+
RootAggregateChange<T> change = instance.changeCreator.apply(aggregateRoot);
411443

412444
aggregateRoot = triggerBeforeSave(change.getRoot(), change);
413445

@@ -427,12 +459,12 @@ private <T> void deleteTree(Object id, @Nullable T entity, Class<T> domainType)
427459
triggerAfterDelete(entity, id, change);
428460
}
429461

430-
private <T> T performSave(T instance, Function<T, RootAggregateChange<T>> changeCreator) {
462+
private <T> T performSave(EntityAndChangeCreator<T> instance) {
431463

432464
// noinspection unchecked
433465
BatchingAggregateChange<T, RootAggregateChange<T>> batchingAggregateChange = //
434-
BatchingAggregateChange.forSave((Class<T>) ClassUtils.getUserClass(instance));
435-
batchingAggregateChange.add(beforeExecute(instance, changeCreator));
466+
BatchingAggregateChange.forSave((Class<T>) ClassUtils.getUserClass(instance.entity));
467+
batchingAggregateChange.add(beforeExecute(instance));
436468

437469
Iterator<T> afterExecutionIterator = executor.executeSave(batchingAggregateChange).iterator();
438470

@@ -441,16 +473,16 @@ private <T> T performSave(T instance, Function<T, RootAggregateChange<T>> change
441473
return afterExecute(batchingAggregateChange, afterExecutionIterator.next());
442474
}
443475

444-
private <T> List<T> performSaveAll(Iterable<T> instances) {
445-
476+
private <T> List<T> performSaveAll(Iterable<EntityAndChangeCreator<T>> instances) {
446477
BatchingAggregateChange<T, RootAggregateChange<T>> batchingAggregateChange = null;
447478

448-
for (T instance : instances) {
479+
for (EntityAndChangeCreator<T> instance : instances) {
449480
if (batchingAggregateChange == null) {
450481
// noinspection unchecked
451-
batchingAggregateChange = BatchingAggregateChange.forSave((Class<T>) ClassUtils.getUserClass(instance));
482+
batchingAggregateChange = BatchingAggregateChange.forSave(
483+
(Class<T>) ClassUtils.getUserClass(instance.entity));
452484
}
453-
batchingAggregateChange.add(beforeExecute(instance, changeCreatorSelectorForSave(instance)));
485+
batchingAggregateChange.add(beforeExecute(instance));
454486
}
455487

456488
Assert.notNull(batchingAggregateChange, "Iterable in saveAll must not be empty");
@@ -604,4 +636,7 @@ private <T> T triggerBeforeDelete(@Nullable T aggregateRoot, Object id, MutableA
604636

605637
private record EntityAndPreviousVersion<T> (T entity, @Nullable Number version) {
606638
}
639+
640+
private record EntityAndChangeCreator<T> (T entity, Function<T, RootAggregateChange<T>> changeCreator) {
641+
}
607642
}

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,30 @@ void saveAndDeleteAllByAggregateRootsWithVersion() {
384384
assertThat(template.count(AggregateWithImmutableVersion.class)).isEqualTo(0);
385385
}
386386

387+
@Test // GH-1395
388+
void insertAndUpdateAllByAggregateRootsWithVersion() {
389+
390+
AggregateWithImmutableVersion aggregate1 = new AggregateWithImmutableVersion(null, null);
391+
AggregateWithImmutableVersion aggregate2 = new AggregateWithImmutableVersion(null, null);
392+
AggregateWithImmutableVersion aggregate3 = new AggregateWithImmutableVersion(null, null);
393+
Iterator<AggregateWithImmutableVersion> savedAggregatesIterator = template
394+
.insertAll(List.of(aggregate1, aggregate2, aggregate3)).iterator();
395+
assertThat(template.count(AggregateWithImmutableVersion.class)).isEqualTo(3);
396+
397+
AggregateWithImmutableVersion savedAggregate1 = savedAggregatesIterator.next();
398+
AggregateWithImmutableVersion twiceSavedAggregate2 = template.save(savedAggregatesIterator.next());
399+
AggregateWithImmutableVersion twiceSavedAggregate3 = template.save(savedAggregatesIterator.next());
400+
401+
savedAggregatesIterator = template.updateAll(
402+
List.of(savedAggregate1, twiceSavedAggregate2, twiceSavedAggregate3)).iterator();
403+
404+
assertThat(savedAggregatesIterator.next().version).isEqualTo(1);
405+
assertThat(savedAggregatesIterator.next().version).isEqualTo(2);
406+
assertThat(savedAggregatesIterator.next().version).isEqualTo(2);
407+
408+
AggregateWithImmutableVersion.clearConstructorInvocationData();
409+
}
410+
387411
@Test // DATAJDBC-112
388412
@EnabledOnFeature({ SUPPORTS_QUOTED_IDS, SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES })
389413
void updateReferencedEntityFromNull() {

0 commit comments

Comments
 (0)