Update SaveBatchingAggregateChange to batch InsertRoot actions.
Original pull request #1228
This commit is contained in:
committed by
Jens Schauder
parent
c1164383d6
commit
ec3cec3be3
@@ -82,6 +82,8 @@ class AggregateChangeExecutor {
|
||||
try {
|
||||
if (action instanceof DbAction.InsertRoot) {
|
||||
executionContext.executeInsertRoot((DbAction.InsertRoot<?>) action);
|
||||
} else if (action instanceof DbAction.BatchInsertRoot<?>) {
|
||||
executionContext.executeBatchInsertRoot((DbAction.BatchInsertRoot<?>) action);
|
||||
} else if (action instanceof DbAction.Insert) {
|
||||
executionContext.executeInsert((DbAction.Insert<?>) action);
|
||||
} else if (action instanceof DbAction.BatchInsert) {
|
||||
|
||||
@@ -75,6 +75,20 @@ class JdbcAggregateChangeExecutionContext {
|
||||
add(new DbActionExecutionResult(insert, id));
|
||||
}
|
||||
|
||||
<T> void executeBatchInsertRoot(DbAction.BatchInsertRoot<T> batchInsertRoot) {
|
||||
|
||||
List<DbAction.InsertRoot<T>> inserts = batchInsertRoot.getActions();
|
||||
List<InsertSubject<T>> insertSubjects = inserts.stream()
|
||||
.map(insert -> InsertSubject.describedBy(insert.getEntity(), Identifier.empty())).collect(Collectors.toList());
|
||||
|
||||
Object[] ids = accessStrategy.insert(insertSubjects, batchInsertRoot.getEntityType(),
|
||||
batchInsertRoot.getBatchValue());
|
||||
|
||||
for (int i = 0; i < inserts.size(); i++) {
|
||||
add(new DbActionExecutionResult(inserts.get(i), ids.length > 0 ? ids[i] : null));
|
||||
}
|
||||
}
|
||||
|
||||
<T> void executeInsert(DbAction.Insert<T> insert) {
|
||||
|
||||
Identifier parentKeys = getParentKeys(insert, converter);
|
||||
|
||||
@@ -164,6 +164,32 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
assertThat(content.id).isNull();
|
||||
}
|
||||
|
||||
@Test // GH-537
|
||||
void batchInsertRootOperation_withGeneratedIds() {
|
||||
|
||||
when(accessStrategy.insert(singletonList(InsertSubject.describedBy(root, Identifier.empty())), DummyEntity.class, IdValueSource.GENERATED))
|
||||
.thenReturn(new Object[] { 123L });
|
||||
executionContext.executeBatchInsertRoot(new DbAction.BatchInsertRoot<>(singletonList(new DbAction.InsertRoot<>(root, IdValueSource.GENERATED))));
|
||||
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoots).containsExactly(root);
|
||||
assertThat(root.id).isEqualTo(123L);
|
||||
}
|
||||
|
||||
@Test // GH-537
|
||||
void batchInsertRootOperation_withoutGeneratedIds() {
|
||||
|
||||
when(accessStrategy.insert(singletonList(InsertSubject.describedBy(root, Identifier.empty())), DummyEntity.class, IdValueSource.PROVIDED))
|
||||
.thenReturn(new Object[] { null });
|
||||
executionContext.executeBatchInsertRoot(new DbAction.BatchInsertRoot<>(singletonList(new DbAction.InsertRoot<>(root, IdValueSource.PROVIDED))));
|
||||
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoots).containsExactly(root);
|
||||
assertThat(root.id).isNull();
|
||||
}
|
||||
|
||||
@Test // GH-1201
|
||||
void updates_whenReferencesWithImmutableIdAreInserted() {
|
||||
|
||||
@@ -177,7 +203,8 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
Identifier identifier = Identifier.empty().withPart(SqlIdentifier.quoted("DUMMY_ENTITY"), 123L, Long.class);
|
||||
when(accessStrategy.insert(contentImmutableId, ContentImmutableId.class, identifier, IdValueSource.GENERATED))
|
||||
.thenReturn(456L);
|
||||
executionContext.executeInsert(createInsert(rootUpdate, "contentImmutableId", contentImmutableId, null, IdValueSource.GENERATED));
|
||||
executionContext.executeInsert(
|
||||
createInsert(rootUpdate, "contentImmutableId", contentImmutableId, null, IdValueSource.GENERATED));
|
||||
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
assertThat(newRoots).containsExactly(root);
|
||||
@@ -197,7 +224,6 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
when(accessStrategy.insert(content1, Content.class, createBackRef(123L), IdValueSource.GENERATED)).thenReturn(11L);
|
||||
executionContext.executeInsert(createInsert(rootUpdate1, "content", content1, null, IdValueSource.GENERATED));
|
||||
|
||||
|
||||
DummyEntity root2 = new DummyEntity();
|
||||
DbAction.InsertRoot<DummyEntity> rootInsert2 = new DbAction.InsertRoot<>(root2, IdValueSource.GENERATED);
|
||||
when(accessStrategy.insert(root2, DummyEntity.class, Identifier.empty(), IdValueSource.GENERATED)).thenReturn(456L);
|
||||
|
||||
@@ -410,6 +410,18 @@ public interface DbAction<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a batch insert statement for a multiple entities that are aggregate roots.
|
||||
*
|
||||
* @param <T> type of the entity for which this represents a database interaction.
|
||||
* @since 3.0
|
||||
*/
|
||||
final class BatchInsertRoot<T> extends BatchWithValue<T, InsertRoot<T>, IdValueSource> {
|
||||
public BatchInsertRoot(List<InsertRoot<T>> actions) {
|
||||
super(actions, InsertRoot::getIdValueSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An action depending on another action for providing additional information like the id of a parent entity.
|
||||
*
|
||||
|
||||
@@ -43,7 +43,8 @@ public class SaveBatchingAggregateChange<T> implements BatchingAggregateChange<T
|
||||
Comparator.comparing(PersistentPropertyPath::getLength);
|
||||
|
||||
private final Class<T> entityType;
|
||||
private final List<DbAction.WithRoot<?>> rootActions = new ArrayList<>();
|
||||
private final List<DbAction<?>> rootActions = new ArrayList<>();
|
||||
private final List<DbAction.InsertRoot<T>> insertRootBatchCandidates = new ArrayList<>();
|
||||
private final Map<PersistentPropertyPath<RelationalPersistentProperty>, Map<IdValueSource, List<DbAction.Insert<Object>>>> insertActions = //
|
||||
new HashMap<>();
|
||||
private final Map<PersistentPropertyPath<RelationalPersistentProperty>, List<DbAction.Delete<?>>> deleteActions = //
|
||||
@@ -69,39 +70,59 @@ public class SaveBatchingAggregateChange<T> implements BatchingAggregateChange<T
|
||||
Assert.notNull(consumer, "Consumer must not be null.");
|
||||
|
||||
rootActions.forEach(consumer);
|
||||
if (insertRootBatchCandidates.size() > 1) {
|
||||
consumer.accept(new DbAction.BatchInsertRoot<>(insertRootBatchCandidates));
|
||||
} else {
|
||||
insertRootBatchCandidates.forEach(consumer);
|
||||
}
|
||||
deleteActions.entrySet().stream().sorted(Map.Entry.comparingByKey(pathLengthComparator.reversed()))
|
||||
.forEach((entry) -> entry.getValue().forEach(consumer));
|
||||
insertActions.entrySet().stream().sorted(Map.Entry.comparingByKey(pathLengthComparator))
|
||||
.forEach((entry) -> entry.getValue()
|
||||
.forEach((idValueSource, inserts) -> consumer.accept(new DbAction.BatchInsert<>(inserts))));
|
||||
insertActions.entrySet().stream().sorted(Map.Entry.comparingByKey(pathLengthComparator)).forEach((entry) -> entry
|
||||
.getValue().forEach((idValueSource, inserts) -> consumer.accept(new DbAction.BatchInsert<>(inserts))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(RootAggregateChange<T> aggregateChange) {
|
||||
|
||||
aggregateChange.forEachAction(action -> {
|
||||
if (action instanceof DbAction.WithRoot<?> rootAction) {
|
||||
if (action instanceof DbAction.UpdateRoot<?> rootAction) {
|
||||
commitBatchCandidates();
|
||||
rootActions.add(rootAction);
|
||||
} else if (action instanceof DbAction.Insert<?>) {
|
||||
} else if (action instanceof DbAction.InsertRoot<?> rootAction) {
|
||||
if (!insertRootBatchCandidates.isEmpty() && !insertRootBatchCandidates.get(0).getIdValueSource().equals(rootAction.getIdValueSource())) {
|
||||
commitBatchCandidates();
|
||||
}
|
||||
//noinspection unchecked
|
||||
insertRootBatchCandidates.add((DbAction.InsertRoot<T>) rootAction);
|
||||
} else if (action instanceof DbAction.Insert<?> insertAction) {
|
||||
// noinspection unchecked
|
||||
addInsert((DbAction.Insert<Object>) action);
|
||||
addInsert((DbAction.Insert<Object>) insertAction);
|
||||
} else if (action instanceof DbAction.Delete<?> deleteAction) {
|
||||
addDelete(deleteAction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void commitBatchCandidates() {
|
||||
|
||||
if (insertRootBatchCandidates.size() > 1) {
|
||||
rootActions.add(new DbAction.BatchInsertRoot<>(List.copyOf(insertRootBatchCandidates)));
|
||||
} else {
|
||||
rootActions.addAll(insertRootBatchCandidates);
|
||||
}
|
||||
insertRootBatchCandidates.clear();
|
||||
}
|
||||
|
||||
private void addInsert(DbAction.Insert<Object> action) {
|
||||
|
||||
PersistentPropertyPath<RelationalPersistentProperty> propertyPath = action.getPropertyPath();
|
||||
insertActions.merge(propertyPath,
|
||||
new HashMap<>(singletonMap(action.getIdValueSource(), new ArrayList<>(singletonList(action)))),
|
||||
(map, mapDefaultValue) -> {
|
||||
map.merge(action.getIdValueSource(), new ArrayList<>(singletonList(action)),
|
||||
(actions, listDefaultValue) -> {
|
||||
actions.add(action);
|
||||
return actions;
|
||||
});
|
||||
map.merge(action.getIdValueSource(), new ArrayList<>(singletonList(action)), (actions, listDefaultValue) -> {
|
||||
actions.add(action);
|
||||
return actions;
|
||||
});
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,12 +52,12 @@ class DbActionTestSupport {
|
||||
@Nullable
|
||||
static IdValueSource insertIdValueSource(DbAction<?> action) {
|
||||
|
||||
if (action instanceof DbAction.InsertRoot) {
|
||||
return ((DbAction.InsertRoot<?>) action).getIdValueSource();
|
||||
} else if (action instanceof DbAction.Insert) {
|
||||
return ((DbAction.Insert<?>) action).getIdValueSource();
|
||||
if (action instanceof DbAction.WithEntity<?>) {
|
||||
return ((DbAction.WithEntity<?>) action).getIdValueSource();
|
||||
} else if (action instanceof DbAction.BatchInsert) {
|
||||
return ((DbAction.BatchInsert<?>) action).getBatchValue();
|
||||
} else if (action instanceof DbAction.BatchInsertRoot<?>) {
|
||||
return ((DbAction.BatchInsertRoot<?>) action).getBatchValue();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ public class RelationalEntityWriterUnitTests {
|
||||
DbActionTestSupport::isWithDependsOn, //
|
||||
DbActionTestSupport::insertIdValueSource) //
|
||||
.containsExactly( //
|
||||
tuple(UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false, null), //
|
||||
tuple(UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false, IdValueSource.PROVIDED), //
|
||||
tuple(Delete.class, Element.class, "other", null, false, null), //
|
||||
tuple(Insert.class, Element.class, "other", Element.class, true, IdValueSource.GENERATED) //
|
||||
);
|
||||
@@ -372,7 +372,7 @@ public class RelationalEntityWriterUnitTests {
|
||||
DbActionTestSupport::isWithDependsOn, //
|
||||
DbActionTestSupport::insertIdValueSource) //
|
||||
.containsExactly( //
|
||||
tuple(UpdateRoot.class, CascadingReferenceEntity.class, "", CascadingReferenceEntity.class, false, null), //
|
||||
tuple(UpdateRoot.class, CascadingReferenceEntity.class, "", CascadingReferenceEntity.class, false, IdValueSource.PROVIDED), //
|
||||
tuple(Delete.class, Element.class, "other.element", null, false, null),
|
||||
tuple(Delete.class, CascadingReferenceMiddleElement.class, "other", null, false, null),
|
||||
tuple(Insert.class, CascadingReferenceMiddleElement.class, "other", CascadingReferenceMiddleElement.class,
|
||||
@@ -531,7 +531,7 @@ public class RelationalEntityWriterUnitTests {
|
||||
DbActionTestSupport::extractPath, //
|
||||
DbActionTestSupport::insertIdValueSource) //
|
||||
.containsExactly( //
|
||||
tuple(UpdateRoot.class, MapContainer.class, null, "", null), //
|
||||
tuple(UpdateRoot.class, MapContainer.class, null, "", IdValueSource.PROVIDED), //
|
||||
tuple(Delete.class, Element.class, null, "elements", null), //
|
||||
tuple(Insert.class, Element.class, "one", "elements", IdValueSource.GENERATED) //
|
||||
);
|
||||
@@ -554,7 +554,7 @@ public class RelationalEntityWriterUnitTests {
|
||||
DbActionTestSupport::extractPath, //
|
||||
DbActionTestSupport::insertIdValueSource) //
|
||||
.containsExactly( //
|
||||
tuple(UpdateRoot.class, ListContainer.class, null, "", null), //
|
||||
tuple(UpdateRoot.class, ListContainer.class, null, "", IdValueSource.PROVIDED), //
|
||||
tuple(Delete.class, Element.class, null, "elements", null), //
|
||||
tuple(Insert.class, Element.class, 0, "elements", IdValueSource.GENERATED) //
|
||||
);
|
||||
@@ -579,7 +579,7 @@ public class RelationalEntityWriterUnitTests {
|
||||
DbActionTestSupport::extractPath, //
|
||||
DbActionTestSupport::insertIdValueSource) //
|
||||
.containsExactly( //
|
||||
tuple(UpdateRoot.class, ListMapContainer.class, null, null, "", null), //
|
||||
tuple(UpdateRoot.class, ListMapContainer.class, null, null, "", IdValueSource.PROVIDED), //
|
||||
tuple(Delete.class, Element.class, null, null, "maps.elements", null), //
|
||||
tuple(Delete.class, MapContainer.class, null, null, "maps", null), //
|
||||
tuple(Insert.class, MapContainer.class, 0, null, "maps", IdValueSource.PROVIDED), //
|
||||
@@ -607,7 +607,7 @@ public class RelationalEntityWriterUnitTests {
|
||||
DbActionTestSupport::extractPath, //
|
||||
DbActionTestSupport::insertIdValueSource) //
|
||||
.containsExactly( //
|
||||
tuple(UpdateRoot.class, NoIdListMapContainer.class, null, null, "", null), //
|
||||
tuple(UpdateRoot.class, NoIdListMapContainer.class, null, null, "", IdValueSource.PROVIDED), //
|
||||
tuple(Delete.class, NoIdElement.class, null, null, "maps.elements", null), //
|
||||
tuple(Delete.class, NoIdMapContainer.class, null, null, "maps", null), //
|
||||
tuple(Insert.class, NoIdMapContainer.class, 0, null, "maps", IdValueSource.NONE), //
|
||||
|
||||
@@ -18,17 +18,18 @@ package org.springframework.data.relational.core.conversion;
|
||||
import static java.util.Collections.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.assertj.core.groups.Tuple;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SaveBatchingAggregateChange}.
|
||||
*
|
||||
@@ -46,23 +47,192 @@ class SaveBatchingAggregateChangeTest {
|
||||
assertThat(extractActions(change)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void yieldsRootActions() {
|
||||
@Nested
|
||||
class RootActionsTests {
|
||||
@Test
|
||||
void yieldsUpdateRoot() {
|
||||
|
||||
Root root1 = new Root(null, null);
|
||||
DbAction.InsertRoot<Root> root1Insert = new DbAction.InsertRoot<>(root1, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange1 = MutableAggregateChange.forSave(root1);
|
||||
aggregateChange1.setRootAction(root1Insert);
|
||||
Root root2 = new Root(null, null);
|
||||
DbAction.InsertRoot<Root> root2Insert = new DbAction.InsertRoot<>(root2, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange2 = MutableAggregateChange.forSave(root2);
|
||||
aggregateChange2.setRootAction(root2Insert);
|
||||
Root root = new Root(1L, null);
|
||||
DbAction.UpdateRoot<Root> rootUpdate = new DbAction.UpdateRoot<>(root, null);
|
||||
RootAggregateChange<Root> aggregateChange = MutableAggregateChange.forSave(root);
|
||||
aggregateChange.setRootAction(rootUpdate);
|
||||
|
||||
BatchingAggregateChange<Root, RootAggregateChange<Root>> change = BatchingAggregateChange.forSave(Root.class);
|
||||
change.add(aggregateChange1);
|
||||
change.add(aggregateChange2);
|
||||
BatchingAggregateChange<Root, RootAggregateChange<Root>> change = BatchingAggregateChange.forSave(Root.class);
|
||||
change.add(aggregateChange);
|
||||
|
||||
assertThat(extractActions(change)).containsExactly(root1Insert, root2Insert);
|
||||
assertThat(extractActions(change)).containsExactly(rootUpdate);
|
||||
}
|
||||
|
||||
@Test
|
||||
void yieldsSingleInsertRoot_followedByUpdateRoot_asIndividualActions() {
|
||||
|
||||
Root root1 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root1Insert = new DbAction.InsertRoot<>(root1, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange1 = MutableAggregateChange.forSave(root1);
|
||||
aggregateChange1.setRootAction(root1Insert);
|
||||
Root root2 = new Root(1L, null);
|
||||
DbAction.UpdateRoot<Root> root2Update = new DbAction.UpdateRoot<>(root2, null);
|
||||
RootAggregateChange<Root> aggregateChange2 = MutableAggregateChange.forSave(root2);
|
||||
aggregateChange2.setRootAction(root2Update);
|
||||
|
||||
BatchingAggregateChange<Root, RootAggregateChange<Root>> change = BatchingAggregateChange.forSave(Root.class);
|
||||
change.add(aggregateChange1);
|
||||
change.add(aggregateChange2);
|
||||
|
||||
assertThat(extractActions(change)) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
|
||||
.containsExactly( //
|
||||
Tuple.tuple(DbAction.InsertRoot.class, Root.class, IdValueSource.GENERATED), //
|
||||
Tuple.tuple(DbAction.UpdateRoot.class, Root.class, IdValueSource.PROVIDED));
|
||||
}
|
||||
|
||||
@Test
|
||||
void yieldsMultipleMatchingInsertRoot_followedByUpdateRoot_asBatchInsertRootAction() {
|
||||
|
||||
Root root1 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root1Insert = new DbAction.InsertRoot<>(root1, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange1 = MutableAggregateChange.forSave(root1);
|
||||
aggregateChange1.setRootAction(root1Insert);
|
||||
Root root2 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root2Insert = new DbAction.InsertRoot<>(root2, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange2 = MutableAggregateChange.forSave(root2);
|
||||
aggregateChange2.setRootAction(root2Insert);
|
||||
Root root3 = new Root(1L, null);
|
||||
DbAction.UpdateRoot<Root> root3Update = new DbAction.UpdateRoot<>(root3, null);
|
||||
RootAggregateChange<Root> aggregateChange3 = MutableAggregateChange.forSave(root3);
|
||||
aggregateChange3.setRootAction(root3Update);
|
||||
|
||||
BatchingAggregateChange<Root, RootAggregateChange<Root>> change = BatchingAggregateChange.forSave(Root.class);
|
||||
change.add(aggregateChange1);
|
||||
change.add(aggregateChange2);
|
||||
change.add(aggregateChange3);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(change);
|
||||
assertThat(actions) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
|
||||
.containsExactly( //
|
||||
Tuple.tuple(DbAction.BatchInsertRoot.class, Root.class, IdValueSource.GENERATED), //
|
||||
Tuple.tuple(DbAction.UpdateRoot.class, Root.class, IdValueSource.PROVIDED));
|
||||
assertThat(getBatchWithValueAction(actions, Root.class, DbAction.BatchInsertRoot.class).getActions())
|
||||
.containsExactly(root1Insert, root2Insert);
|
||||
}
|
||||
|
||||
@Test
|
||||
void yieldsInsertRoot() {
|
||||
|
||||
Root root = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> rootInsert = new DbAction.InsertRoot<>(root, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange = MutableAggregateChange.forSave(root);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
|
||||
BatchingAggregateChange<Root, RootAggregateChange<Root>> change = BatchingAggregateChange.forSave(Root.class);
|
||||
change.add(aggregateChange);
|
||||
|
||||
assertThat(extractActions(change)).containsExactly(rootInsert);
|
||||
}
|
||||
|
||||
@Test
|
||||
void yieldsSingleInsertRoot_followedByNonMatchingInsertRoot_asIndividualActions() {
|
||||
|
||||
Root root1 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root1Insert = new DbAction.InsertRoot<>(root1, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange1 = MutableAggregateChange.forSave(root1);
|
||||
aggregateChange1.setRootAction(root1Insert);
|
||||
Root root2 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root2Insert = new DbAction.InsertRoot<>(root2, IdValueSource.PROVIDED);
|
||||
RootAggregateChange<Root> aggregateChange2 = MutableAggregateChange.forSave(root2);
|
||||
aggregateChange2.setRootAction(root2Insert);
|
||||
|
||||
BatchingAggregateChange<Root, RootAggregateChange<Root>> change = BatchingAggregateChange.forSave(Root.class);
|
||||
change.add(aggregateChange1);
|
||||
change.add(aggregateChange2);
|
||||
|
||||
assertThat(extractActions(change)).containsExactly(root1Insert, root2Insert);
|
||||
}
|
||||
|
||||
@Test
|
||||
void yieldsMultipleMatchingInsertRoot_followedByNonMatchingInsertRoot_asBatchInsertRootAction() {
|
||||
|
||||
Root root1 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root1Insert = new DbAction.InsertRoot<>(root1, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange1 = MutableAggregateChange.forSave(root1);
|
||||
aggregateChange1.setRootAction(root1Insert);
|
||||
Root root2 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root2Insert = new DbAction.InsertRoot<>(root2, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange2 = MutableAggregateChange.forSave(root2);
|
||||
aggregateChange2.setRootAction(root2Insert);
|
||||
Root root3 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root3Insert = new DbAction.InsertRoot<>(root3, IdValueSource.PROVIDED);
|
||||
RootAggregateChange<Root> aggregateChange3 = MutableAggregateChange.forSave(root3);
|
||||
aggregateChange3.setRootAction(root3Insert);
|
||||
|
||||
BatchingAggregateChange<Root, RootAggregateChange<Root>> change = BatchingAggregateChange.forSave(Root.class);
|
||||
change.add(aggregateChange1);
|
||||
change.add(aggregateChange2);
|
||||
change.add(aggregateChange3);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(change);
|
||||
assertThat(actions) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
|
||||
.containsExactly( //
|
||||
Tuple.tuple(DbAction.BatchInsertRoot.class, Root.class, IdValueSource.GENERATED), //
|
||||
Tuple.tuple(DbAction.InsertRoot.class, Root.class, IdValueSource.PROVIDED));
|
||||
assertThat(getBatchWithValueAction(actions, Root.class, DbAction.BatchInsertRoot.class).getActions())
|
||||
.containsExactly(root1Insert, root2Insert);
|
||||
}
|
||||
|
||||
@Test
|
||||
void yieldsMultipleMatchingInsertRoot_asBatchInsertRootAction() {
|
||||
|
||||
Root root1 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root1Insert = new DbAction.InsertRoot<>(root1, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange1 = MutableAggregateChange.forSave(root1);
|
||||
aggregateChange1.setRootAction(root1Insert);
|
||||
Root root2 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root2Insert = new DbAction.InsertRoot<>(root2, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange2 = MutableAggregateChange.forSave(root2);
|
||||
aggregateChange2.setRootAction(root2Insert);
|
||||
|
||||
BatchingAggregateChange<Root, RootAggregateChange<Root>> change = BatchingAggregateChange.forSave(Root.class);
|
||||
change.add(aggregateChange1);
|
||||
change.add(aggregateChange2);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(change);
|
||||
assertThat(actions) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
|
||||
.containsExactly(Tuple.tuple(DbAction.BatchInsertRoot.class, Root.class, IdValueSource.GENERATED));
|
||||
assertThat(getBatchWithValueAction(actions, Root.class, DbAction.BatchInsertRoot.class).getActions())
|
||||
.containsExactly(root1Insert, root2Insert);
|
||||
}
|
||||
|
||||
@Test
|
||||
void yieldsPreviouslyYieldedInsertRoot_asBatchInsertRootAction_whenAdditionalMatchingInsertRootIsAdded() {
|
||||
|
||||
Root root1 = new Root(1L, null);
|
||||
DbAction.InsertRoot<Root> root1Insert = new DbAction.InsertRoot<>(root1, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange1 = MutableAggregateChange.forSave(root1);
|
||||
aggregateChange1.setRootAction(root1Insert);
|
||||
Root root2 = new Root(2L, null);
|
||||
DbAction.InsertRoot<Root> root2Insert = new DbAction.InsertRoot<>(root2, IdValueSource.GENERATED);
|
||||
RootAggregateChange<Root> aggregateChange2 = MutableAggregateChange.forSave(root2);
|
||||
aggregateChange2.setRootAction(root2Insert);
|
||||
BatchingAggregateChange<Root, RootAggregateChange<Root>> change = BatchingAggregateChange.forSave(Root.class);
|
||||
|
||||
change.add(aggregateChange1);
|
||||
|
||||
assertThat(extractActions(change)) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
|
||||
.containsExactly(Tuple.tuple(DbAction.InsertRoot.class, Root.class, IdValueSource.GENERATED));
|
||||
|
||||
change.add(aggregateChange2);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(change);
|
||||
assertThat(actions) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
|
||||
.containsExactly(Tuple.tuple(DbAction.BatchInsertRoot.class, Root.class, IdValueSource.GENERATED));
|
||||
assertThat(getBatchWithValueAction(actions, Root.class, DbAction.BatchInsertRoot.class).getActions())
|
||||
.containsExactly(root1Insert, root2Insert);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -177,10 +347,10 @@ class SaveBatchingAggregateChangeTest {
|
||||
Tuple.tuple(DbAction.InsertRoot.class, Root.class, IdValueSource.GENERATED), //
|
||||
Tuple.tuple(DbAction.BatchInsert.class, Intermediate.class, IdValueSource.GENERATED)) //
|
||||
.doesNotContain(Tuple.tuple(DbAction.Insert.class, Intermediate.class));
|
||||
assertThat(getBatchInsertAction(actions, Intermediate.class, IdValueSource.GENERATED).getActions())
|
||||
.containsExactly(intermediateInsertGeneratedId);
|
||||
assertThat(getBatchInsertAction(actions, Intermediate.class, IdValueSource.PROVIDED).getActions())
|
||||
.containsExactly(intermediateInsertProvidedId);
|
||||
assertThat(getBatchWithValueAction(actions, Intermediate.class, DbAction.BatchInsert.class, IdValueSource.GENERATED)
|
||||
.getActions()).containsExactly(intermediateInsertGeneratedId);
|
||||
assertThat(getBatchWithValueAction(actions, Intermediate.class, DbAction.BatchInsert.class, IdValueSource.PROVIDED)
|
||||
.getActions()).containsExactly(intermediateInsertProvidedId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -221,9 +391,9 @@ class SaveBatchingAggregateChangeTest {
|
||||
.containsSubsequence( //
|
||||
Tuple.tuple(DbAction.BatchInsert.class, Intermediate.class, IdValueSource.GENERATED),
|
||||
Tuple.tuple(DbAction.BatchInsert.class, Leaf.class, IdValueSource.GENERATED));
|
||||
assertThat(getBatchInsertAction(actions, Intermediate.class).getActions()) //
|
||||
assertThat(getBatchWithValueAction(actions, Intermediate.class, DbAction.BatchInsert.class).getActions()) //
|
||||
.containsExactly(root1IntermediateInsert, root2IntermediateInsert);
|
||||
assertThat(getBatchInsertAction(actions, Leaf.class).getActions()) //
|
||||
assertThat(getBatchWithValueAction(actions, Leaf.class, DbAction.BatchInsert.class).getActions()) //
|
||||
.containsExactly(root1LeafInsert);
|
||||
}
|
||||
|
||||
@@ -256,33 +426,13 @@ class SaveBatchingAggregateChangeTest {
|
||||
.containsSubsequence( //
|
||||
Tuple.tuple(DbAction.BatchInsert.class, Intermediate.class, IdValueSource.GENERATED),
|
||||
Tuple.tuple(DbAction.BatchInsert.class, Intermediate.class, IdValueSource.GENERATED));
|
||||
List<DbAction.BatchInsert<Intermediate>> batchInsertActions = getBatchInsertActions(actions, Intermediate.class);
|
||||
List<DbAction.BatchWithValue<Intermediate, DbAction<Intermediate>, Object>> batchInsertActions = getBatchWithValueActions(actions, Intermediate.class,
|
||||
DbAction.BatchInsert.class);
|
||||
assertThat(batchInsertActions).hasSize(2);
|
||||
assertThat(batchInsertActions.get(0).getActions()).containsExactly(oneInsert);
|
||||
assertThat(batchInsertActions.get(1).getActions()).containsExactly(twoInsert);
|
||||
}
|
||||
|
||||
private <T> DbAction.BatchInsert<T> getBatchInsertAction(List<DbAction<?>> actions, Class<T> entityType,
|
||||
IdValueSource idValueSource) {
|
||||
return getBatchInsertActions(actions, entityType).stream()
|
||||
.filter(batchInsert -> batchInsert.getBatchValue() == idValueSource).findFirst().orElseThrow(
|
||||
() -> new RuntimeException(String.format("No BatchInsert with batch value '%s' found!", idValueSource)));
|
||||
}
|
||||
|
||||
private <T> DbAction.BatchInsert<T> getBatchInsertAction(List<DbAction<?>> actions, Class<T> entityType) {
|
||||
return getBatchInsertActions(actions, entityType).stream().findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("No BatchInsert action found!"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> List<DbAction.BatchInsert<T>> getBatchInsertActions(List<DbAction<?>> actions, Class<T> entityType) {
|
||||
|
||||
return actions.stream() //
|
||||
.filter(dbAction -> dbAction instanceof DbAction.BatchInsert) //
|
||||
.filter(dbAction -> dbAction.getEntityType().equals(entityType)) //
|
||||
.map(dbAction -> (DbAction.BatchInsert<T>) dbAction).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private <T> List<DbAction<?>> extractActions(BatchingAggregateChange<T, RootAggregateChange<T>> change) {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
@@ -290,6 +440,31 @@ class SaveBatchingAggregateChangeTest {
|
||||
return actions;
|
||||
}
|
||||
|
||||
private <T, A> DbAction.BatchWithValue<T, DbAction<T>, Object> getBatchWithValueAction(List<DbAction<?>> actions,
|
||||
Class<T> entityType, Class<A> batchActionType) {
|
||||
|
||||
return getBatchWithValueActions(actions, entityType, batchActionType).stream().findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("No BatchWithValue action found!"));
|
||||
}
|
||||
|
||||
private <T, A> DbAction.BatchWithValue<T, DbAction<T>, Object> getBatchWithValueAction(List<DbAction<?>> actions,
|
||||
Class<T> entityType, Class<A> batchActionType, Object batchValue) {
|
||||
|
||||
return getBatchWithValueActions(actions, entityType, batchActionType).stream()
|
||||
.filter(batchWithValue -> batchWithValue.getBatchValue() == batchValue).findFirst().orElseThrow(
|
||||
() -> new RuntimeException(String.format("No BatchWithValue with batch value '%s' found!", batchValue)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T, A> List<DbAction.BatchWithValue<T, DbAction<T>, Object>> getBatchWithValueActions(
|
||||
List<DbAction<?>> actions, Class<T> entityType, Class<A> batchActionType) {
|
||||
|
||||
return actions.stream() //
|
||||
.filter(dbAction -> dbAction.getClass().equals(batchActionType)) //
|
||||
.filter(dbAction -> dbAction.getEntityType().equals(entityType)) //
|
||||
.map(dbAction -> (DbAction.BatchWithValue<T, DbAction<T>, Object>) dbAction).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Value
|
||||
static class RootWithSameLengthReferences {
|
||||
@Id Long id;
|
||||
|
||||
Reference in New Issue
Block a user