Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* @author Milan Milanov
* @author Chirag Tailor
* @author Diego Krupitza
* @author Myeonghyeon Lee
*/
public interface JdbcAggregateOperations {

Expand Down Expand Up @@ -71,6 +72,19 @@ public interface JdbcAggregateOperations {
*/
<T> T insert(T instance);

/**
* Inserts all aggregate instances, including all the members of each aggregate instance.
* <p>
* This is useful if the client provides an id for new aggregate roots.
* </p>
*
* @param instances the aggregate roots to be inserted. Must not be {@code null}.
* @param <T> the type of the aggregate root.
* @return the saved instances.
* @since 3.1
*/
<T> Iterable<T> insertAll(Iterable<T> instances);

/**
* Dedicated update function. This skips the test if the aggregate root is new or not and always performs an update
* operation.
Expand All @@ -81,6 +95,16 @@ public interface JdbcAggregateOperations {
*/
<T> T update(T instance);

/**
* Updates all aggregate instances, including all the members of each aggregate instance.
*
* @param instances the aggregate roots to be inserted. Must not be {@code null}.
* @param <T> the type of the aggregate root.
* @return the saved instances.
* @since 3.1
*/
<T> Iterable<T> updateAll(Iterable<T> instances);

/**
* Counts the number of aggregates of a given type.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,19 @@ public <T> T save(T instance) {

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

return performSave(instance, changeCreatorSelectorForSave(instance));
return performSave(new EntityAndChangeCreator<>(instance, changeCreatorSelectorForSave(instance)));
}

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

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

return performSaveAll(instances);
List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
for (T instance : instances) {
entityAndChangeCreators.add(new EntityAndChangeCreator<>(instance, changeCreatorSelectorForSave(instance)));
}
return performSaveAll(entityAndChangeCreators);
}

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

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

return performSave(instance, entity -> createInsertChange(prepareVersionForInsert(entity)));
return performSave(new EntityAndChangeCreator<>(
instance, entity -> createInsertChange(prepareVersionForInsert(entity))));
}

@Override
public <T> Iterable<T> insertAll(Iterable<T> instances) {

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

List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
for (T instance : instances) {
entityAndChangeCreators.add(new EntityAndChangeCreator<>(
instance, entity -> createInsertChange(prepareVersionForInsert(entity))));
}
return performSaveAll(entityAndChangeCreators);
}

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

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

return performSave(instance, entity -> createUpdateChange(prepareVersionForUpdate(entity)));
return performSave(new EntityAndChangeCreator<>(
instance, entity -> createUpdateChange(prepareVersionForUpdate(entity))));
}

@Override
public <T> Iterable<T> updateAll(Iterable<T> instances) {

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

List<EntityAndChangeCreator<T>> entityAndChangeCreators = new ArrayList<>();
for (T instance : instances) {
entityAndChangeCreators.add(new EntityAndChangeCreator<>(
instance, entity -> createUpdateChange(prepareVersionForUpdate(entity))));
}
return performSaveAll(entityAndChangeCreators);
}

@Override
Expand Down Expand Up @@ -401,13 +433,13 @@ private <T> T afterExecute(AggregateChange<T> change, T entityAfterExecution) {
return triggerAfterSave(entityAfterExecution, change);
}

private <T> RootAggregateChange<T> beforeExecute(T aggregateRoot, Function<T, RootAggregateChange<T>> changeCreator) {
private <T> RootAggregateChange<T> beforeExecute(EntityAndChangeCreator<T> instance) {

Assert.notNull(aggregateRoot, "Aggregate instance must not be null");
Assert.notNull(instance.entity, "Aggregate instance must not be null");

aggregateRoot = triggerBeforeConvert(aggregateRoot);
T aggregateRoot = triggerBeforeConvert(instance.entity);

RootAggregateChange<T> change = changeCreator.apply(aggregateRoot);
RootAggregateChange<T> change = instance.changeCreator.apply(aggregateRoot);

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

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

private <T> T performSave(T instance, Function<T, RootAggregateChange<T>> changeCreator) {
private <T> T performSave(EntityAndChangeCreator<T> instance) {

// noinspection unchecked
BatchingAggregateChange<T, RootAggregateChange<T>> batchingAggregateChange = //
BatchingAggregateChange.forSave((Class<T>) ClassUtils.getUserClass(instance));
batchingAggregateChange.add(beforeExecute(instance, changeCreator));
BatchingAggregateChange.forSave((Class<T>) ClassUtils.getUserClass(instance.entity));
batchingAggregateChange.add(beforeExecute(instance));

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

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

private <T> List<T> performSaveAll(Iterable<T> instances) {

private <T> List<T> performSaveAll(Iterable<EntityAndChangeCreator<T>> instances) {
BatchingAggregateChange<T, RootAggregateChange<T>> batchingAggregateChange = null;

for (T instance : instances) {
for (EntityAndChangeCreator<T> instance : instances) {
if (batchingAggregateChange == null) {
// noinspection unchecked
batchingAggregateChange = BatchingAggregateChange.forSave((Class<T>) ClassUtils.getUserClass(instance));
batchingAggregateChange = BatchingAggregateChange.forSave(
(Class<T>) ClassUtils.getUserClass(instance.entity));
}
batchingAggregateChange.add(beforeExecute(instance, changeCreatorSelectorForSave(instance)));
batchingAggregateChange.add(beforeExecute(instance));
}

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

private record EntityAndPreviousVersion<T> (T entity, @Nullable Number version) {
}

private record EntityAndChangeCreator<T> (T entity, Function<T, RootAggregateChange<T>> changeCreator) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,30 @@ void saveAndDeleteAllByAggregateRootsWithVersion() {
assertThat(template.count(AggregateWithImmutableVersion.class)).isEqualTo(0);
}

@Test // GH-1395
void insertAndUpdateAllByAggregateRootsWithVersion() {

AggregateWithImmutableVersion aggregate1 = new AggregateWithImmutableVersion(null, null);
AggregateWithImmutableVersion aggregate2 = new AggregateWithImmutableVersion(null, null);
AggregateWithImmutableVersion aggregate3 = new AggregateWithImmutableVersion(null, null);
Iterator<AggregateWithImmutableVersion> savedAggregatesIterator = template
.insertAll(List.of(aggregate1, aggregate2, aggregate3)).iterator();
assertThat(template.count(AggregateWithImmutableVersion.class)).isEqualTo(3);

AggregateWithImmutableVersion savedAggregate1 = savedAggregatesIterator.next();
AggregateWithImmutableVersion twiceSavedAggregate2 = template.save(savedAggregatesIterator.next());
AggregateWithImmutableVersion twiceSavedAggregate3 = template.save(savedAggregatesIterator.next());

savedAggregatesIterator = template.updateAll(
List.of(savedAggregate1, twiceSavedAggregate2, twiceSavedAggregate3)).iterator();

assertThat(savedAggregatesIterator.next().version).isEqualTo(1);
assertThat(savedAggregatesIterator.next().version).isEqualTo(2);
assertThat(savedAggregatesIterator.next().version).isEqualTo(2);

AggregateWithImmutableVersion.clearConstructorInvocationData();
}

@Test // DATAJDBC-112
@EnabledOnFeature({ SUPPORTS_QUOTED_IDS, SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES })
void updateReferencedEntityFromNull() {
Expand Down