Add SaveMergedAggregateChange which merges AggregateChangeWithRoot changes into one.
Remove behavior from WritingContext for creating InsertBatch in favor of SaveMergedAggregateChange. Update all save paths to use SaveMergedAggregateChange. + Update #populateIdsIfNecessary return type from T to List<T> Pull out an abstract BatchWithValue class from InsertBatch to use it for batching root inserts as well. Rename InsertBatch to BatchInsert Rename AggregateChangeWithRoot to RootAggregateChange. Original pull request #1211
This commit is contained in:
committed by
Jens Schauder
parent
d3d05039da
commit
6b02a4e627
@@ -18,11 +18,12 @@ package org.springframework.data.jdbc.core;
|
||||
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.convert.JdbcConverter;
|
||||
import org.springframework.data.relational.core.conversion.AggregateChange;
|
||||
import org.springframework.data.relational.core.conversion.AggregateChangeWithRoot;
|
||||
import org.springframework.data.relational.core.conversion.DbAction;
|
||||
import org.springframework.data.relational.core.conversion.DbActionExecutionException;
|
||||
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Executes an {@link MutableAggregateChange}.
|
||||
*
|
||||
@@ -43,15 +44,15 @@ class AggregateChangeExecutor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an aggregate change which has a root entity. It returns the root entity, with all changes that might apply.
|
||||
* This might be the original instance or a new instance, depending on its mutability.
|
||||
* Execute a save aggregate change. It returns the resulting root entities, with all changes that might apply. This
|
||||
* might be the original instances or new instances, depending on their mutability.
|
||||
*
|
||||
* @param aggregateChange the aggregate change to be executed. Must not be {@literal null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the potentially modified aggregate root. Guaranteed to be not {@literal null}.
|
||||
* @return the aggregate roots resulting from the change, if there are any. May be empty.
|
||||
* @since 3.0
|
||||
*/
|
||||
<T> T execute(AggregateChangeWithRoot<T> aggregateChange) {
|
||||
<T> List<T> executeSave(AggregateChange<T> aggregateChange) {
|
||||
|
||||
JdbcAggregateChangeExecutionContext executionContext = new JdbcAggregateChangeExecutionContext(converter,
|
||||
accessStrategy);
|
||||
@@ -62,13 +63,13 @@ class AggregateChangeExecutor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an aggregate change without a root entity.
|
||||
* Execute a delete aggregate change.
|
||||
*
|
||||
* @param aggregateChange the aggregate change to be executed. Must not be {@literal null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @since 3.0
|
||||
*/
|
||||
<T> void execute(AggregateChange<T> aggregateChange) {
|
||||
<T> void executeDelete(AggregateChange<T> aggregateChange) {
|
||||
|
||||
JdbcAggregateChangeExecutionContext executionContext = new JdbcAggregateChangeExecutionContext(converter,
|
||||
accessStrategy);
|
||||
@@ -83,8 +84,8 @@ class AggregateChangeExecutor {
|
||||
executionContext.executeInsertRoot((DbAction.InsertRoot<?>) action);
|
||||
} else if (action instanceof DbAction.Insert) {
|
||||
executionContext.executeInsert((DbAction.Insert<?>) action);
|
||||
} else if (action instanceof DbAction.InsertBatch) {
|
||||
executionContext.executeInsertBatch((DbAction.InsertBatch<?>) action);
|
||||
} else if (action instanceof DbAction.BatchInsert) {
|
||||
executionContext.executeBatchInsert((DbAction.BatchInsert<?>) action);
|
||||
} else if (action instanceof DbAction.UpdateRoot) {
|
||||
executionContext.executeUpdateRoot((DbAction.UpdateRoot<?>) action);
|
||||
} else if (action instanceof DbAction.Delete) {
|
||||
|
||||
@@ -83,14 +83,14 @@ class JdbcAggregateChangeExecutionContext {
|
||||
add(new DbActionExecutionResult(insert, id));
|
||||
}
|
||||
|
||||
<T> void executeInsertBatch(DbAction.InsertBatch<T> insertBatch) {
|
||||
<T> void executeBatchInsert(DbAction.BatchInsert<T> batchInsert) {
|
||||
|
||||
List<DbAction.Insert<T>> inserts = insertBatch.getInserts();
|
||||
List<DbAction.Insert<T>> inserts = batchInsert.getActions();
|
||||
List<InsertSubject<T>> insertSubjects = inserts.stream()
|
||||
.map(insert -> InsertSubject.describedBy(insert.getEntity(), getParentKeys(insert, converter)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Object[] ids = accessStrategy.insert(insertSubjects, insertBatch.getEntityType(), insertBatch.getIdValueSource());
|
||||
Object[] ids = accessStrategy.insert(insertSubjects, batchInsert.getEntityType(), batchInsert.getBatchValue());
|
||||
|
||||
for (int i = 0; i < inserts.size(); i++) {
|
||||
add(new DbActionExecutionResult(inserts.get(i), ids.length > 0 ? ids[i] : null));
|
||||
@@ -216,7 +216,7 @@ class JdbcAggregateChangeExecutionContext {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
<T> T populateIdsIfNecessary() {
|
||||
<T> List<T> populateIdsIfNecessary() {
|
||||
|
||||
// have the results so that the inserts on the leaves come first.
|
||||
List<DbActionExecutionResult> reverseResults = new ArrayList<>(results.values());
|
||||
@@ -224,6 +224,8 @@ class JdbcAggregateChangeExecutionContext {
|
||||
|
||||
StagedValues cascadingValues = new StagedValues();
|
||||
|
||||
List<T> roots = new ArrayList<>(reverseResults.size());
|
||||
|
||||
for (DbActionExecutionResult result : reverseResults) {
|
||||
|
||||
DbAction.WithEntity<?> action = result.getAction();
|
||||
@@ -232,7 +234,7 @@ class JdbcAggregateChangeExecutionContext {
|
||||
|
||||
if (action instanceof DbAction.InsertRoot || action instanceof DbAction.UpdateRoot) {
|
||||
// noinspection unchecked
|
||||
return (T) newEntity;
|
||||
roots.add((T) newEntity);
|
||||
}
|
||||
|
||||
// the id property was immutable so we have to propagate changes up the tree
|
||||
@@ -246,9 +248,15 @@ class JdbcAggregateChangeExecutionContext {
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
String.format("Cannot retrieve the resulting instance unless a %s or %s action was successfully executed.",
|
||||
DbAction.InsertRoot.class.getName(), DbAction.UpdateRoot.class.getName()));
|
||||
if (roots.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
String.format("Cannot retrieve the resulting instance(s) unless a %s or %s action was successfully executed.",
|
||||
DbAction.InsertRoot.class.getName(), DbAction.UpdateRoot.class.getName()));
|
||||
}
|
||||
|
||||
Collections.reverse(roots);
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
private <S> Object setIdAndCascadingProperties(DbAction.WithEntity<S> action, @Nullable Object generatedId,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
* Copyright 2017-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import org.springframework.lang.Nullable;
|
||||
* @author Jens Schauder
|
||||
* @author Thomas Lang
|
||||
* @author Milan Milanov
|
||||
* @author Chirag Tailor
|
||||
*/
|
||||
public interface JdbcAggregateOperations {
|
||||
|
||||
@@ -38,6 +39,16 @@ public interface JdbcAggregateOperations {
|
||||
*/
|
||||
<T> T save(T instance);
|
||||
|
||||
/**
|
||||
* Saves all aggregate instances, including all the members of each aggregate instance.
|
||||
*
|
||||
* @param instances the aggregate roots to be saved. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the saved instances.
|
||||
* @since 3.0
|
||||
*/
|
||||
<T> Iterable<T> saveAll(Iterable<T> instances);
|
||||
|
||||
/**
|
||||
* Dedicated insert function. This skips the test if the aggregate root is new and makes an insert.
|
||||
* <p>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -31,7 +32,8 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter;
|
||||
import org.springframework.data.mapping.IdentifierAccessor;
|
||||
import org.springframework.data.mapping.callback.EntityCallbacks;
|
||||
import org.springframework.data.relational.core.conversion.AggregateChange;
|
||||
import org.springframework.data.relational.core.conversion.AggregateChangeWithRoot;
|
||||
import org.springframework.data.relational.core.conversion.RootAggregateChange;
|
||||
import org.springframework.data.relational.core.conversion.BatchingAggregateChange;
|
||||
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
|
||||
import org.springframework.data.relational.core.conversion.RelationalEntityDeleteWriter;
|
||||
import org.springframework.data.relational.core.conversion.RelationalEntityInsertWriter;
|
||||
@@ -44,6 +46,7 @@ import org.springframework.data.relational.core.mapping.event.*;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link JdbcAggregateOperations} implementation, storing aggregates in and obtaining them from a JDBC data store.
|
||||
@@ -141,13 +144,15 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
|
||||
Assert.notNull(instance, "Aggregate instance must not be null!");
|
||||
|
||||
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(instance.getClass());
|
||||
return performSave(instance, changeCreatorSelectorForSave(instance));
|
||||
}
|
||||
|
||||
Function<T, AggregateChangeWithRoot<T>> changeCreator = persistentEntity.isNew(instance)
|
||||
? entity -> createInsertChange(prepareVersionForInsert(entity))
|
||||
: entity -> createUpdateChange(prepareVersionForUpdate(entity));
|
||||
@Override
|
||||
public <T> Iterable<T> saveAll(Iterable<T> instances) {
|
||||
|
||||
return store(instance, changeCreator, persistentEntity);
|
||||
Assert.isTrue(instances.iterator().hasNext(), "Aggregate instances must not be empty!");
|
||||
|
||||
return performSaveAll(instances);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,9 +167,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
|
||||
Assert.notNull(instance, "Aggregate instance must not be null!");
|
||||
|
||||
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(instance.getClass());
|
||||
|
||||
return store(instance, entity -> createInsertChange(prepareVersionForInsert(entity)), persistentEntity);
|
||||
return performSave(instance, entity -> createInsertChange(prepareVersionForInsert(entity)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,9 +182,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
|
||||
Assert.notNull(instance, "Aggregate instance must not be null!");
|
||||
|
||||
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(instance.getClass());
|
||||
|
||||
return store(instance, entity -> createUpdateChange(prepareVersionForUpdate(entity)), persistentEntity);
|
||||
return performSave(instance, entity -> createUpdateChange(prepareVersionForUpdate(entity)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -280,29 +281,33 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
Assert.notNull(domainType, "Domain type must not be null!");
|
||||
|
||||
MutableAggregateChange<?> change = createDeletingChange(domainType);
|
||||
executor.execute(change);
|
||||
executor.executeDelete(change);
|
||||
}
|
||||
|
||||
private <T> T store(T aggregateRoot, Function<T, AggregateChangeWithRoot<T>> changeCreator,
|
||||
RelationalPersistentEntity<?> persistentEntity) {
|
||||
private <T> T afterExecute(AggregateChange<T> change, T entityAfterExecution) {
|
||||
|
||||
Object identifier = context.getRequiredPersistentEntity(change.getEntityType())
|
||||
.getIdentifierAccessor(entityAfterExecution).getIdentifier();
|
||||
|
||||
Assert.notNull(identifier, "After saving the identifier must not be null!");
|
||||
|
||||
return triggerAfterSave(entityAfterExecution, change);
|
||||
}
|
||||
|
||||
private <T> RootAggregateChange<T> beforeExecute(T aggregateRoot,
|
||||
Function<T, RootAggregateChange<T>> changeCreator) {
|
||||
|
||||
Assert.notNull(aggregateRoot, "Aggregate instance must not be null!");
|
||||
|
||||
aggregateRoot = triggerBeforeConvert(aggregateRoot);
|
||||
|
||||
AggregateChangeWithRoot<T> change = changeCreator.apply(aggregateRoot);
|
||||
RootAggregateChange<T> change = changeCreator.apply(aggregateRoot);
|
||||
|
||||
aggregateRoot = triggerBeforeSave(change.getRoot(), change);
|
||||
|
||||
change.setRoot(aggregateRoot);
|
||||
|
||||
T entityAfterExecution = executor.execute(change);
|
||||
|
||||
Object identifier = persistentEntity.getIdentifierAccessor(entityAfterExecution).getIdentifier();
|
||||
|
||||
Assert.notNull(identifier, "After saving the identifier must not be null!");
|
||||
|
||||
return triggerAfterSave(entityAfterExecution, change);
|
||||
return change;
|
||||
}
|
||||
|
||||
private <T> void deleteTree(Object id, @Nullable T entity, Class<T> domainType) {
|
||||
@@ -311,23 +316,70 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
|
||||
entity = triggerBeforeDelete(entity, id, change);
|
||||
|
||||
executor.execute(change);
|
||||
executor.executeDelete(change);
|
||||
|
||||
triggerAfterDelete(entity, id, change);
|
||||
}
|
||||
|
||||
private <T> AggregateChangeWithRoot<T> createInsertChange(T instance) {
|
||||
private <T> T performSave(T instance, Function<T, RootAggregateChange<T>> changeCreator) {
|
||||
|
||||
AggregateChangeWithRoot<T> aggregateChange = MutableAggregateChange.forSave(instance);
|
||||
// noinspection unchecked
|
||||
BatchingAggregateChange<T, RootAggregateChange<T>> batchingAggregateChange = //
|
||||
BatchingAggregateChange.forSave((Class<T>) ClassUtils.getUserClass(instance));
|
||||
batchingAggregateChange.add(beforeExecute(instance, changeCreator));
|
||||
|
||||
Iterator<T> afterExecutionIterator = executor.executeSave(batchingAggregateChange).iterator();
|
||||
|
||||
Assert.isTrue(afterExecutionIterator.hasNext(), "Instances after execution must not be empty!");
|
||||
|
||||
return afterExecute(batchingAggregateChange, afterExecutionIterator.next());
|
||||
}
|
||||
|
||||
private <T> List<T> performSaveAll(Iterable<T> instances) {
|
||||
|
||||
Iterator<T> iterator = instances.iterator();
|
||||
T firstInstance = iterator.next();
|
||||
|
||||
// noinspection unchecked
|
||||
BatchingAggregateChange<T, RootAggregateChange<T>> batchingAggregateChange = //
|
||||
BatchingAggregateChange.forSave((Class<T>) ClassUtils.getUserClass(firstInstance));
|
||||
batchingAggregateChange.add(beforeExecute(firstInstance, changeCreatorSelectorForSave(firstInstance)));
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
T instance = iterator.next();
|
||||
batchingAggregateChange.add(beforeExecute(instance, changeCreatorSelectorForSave(instance)));
|
||||
}
|
||||
|
||||
List<T> instancesAfterExecution = executor.executeSave(batchingAggregateChange);
|
||||
|
||||
ArrayList<T> results = new ArrayList<>(instancesAfterExecution.size());
|
||||
for (T instance : instancesAfterExecution) {
|
||||
results.add(afterExecute(batchingAggregateChange, instance));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private <T> Function<T, RootAggregateChange<T>> changeCreatorSelectorForSave(T instance) {
|
||||
|
||||
return context.getRequiredPersistentEntity(instance.getClass()).isNew(instance)
|
||||
? entity -> createInsertChange(prepareVersionForInsert(entity))
|
||||
: entity -> createUpdateChange(prepareVersionForUpdate(entity));
|
||||
}
|
||||
|
||||
private <T> RootAggregateChange<T> createInsertChange(T instance) {
|
||||
|
||||
RootAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(instance);
|
||||
new RelationalEntityInsertWriter<T>(context).write(instance, aggregateChange);
|
||||
return aggregateChange;
|
||||
}
|
||||
|
||||
private <T> AggregateChangeWithRoot<T> createUpdateChange(EntityAndPreviousVersion<T> entityAndVersion) {
|
||||
private <T> RootAggregateChange<T> createUpdateChange(EntityAndPreviousVersion<T> entityAndVersion) {
|
||||
|
||||
AggregateChangeWithRoot<T> aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity,
|
||||
RootAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity,
|
||||
entityAndVersion.version);
|
||||
new RelationalEntityUpdateWriter<T>(context).write(entityAndVersion.entity, aggregateChange);
|
||||
new RelationalEntityUpdateWriter<T>(context).write(entityAndVersion.entity,
|
||||
aggregateChange);
|
||||
return aggregateChange;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
* Copyright 2017-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -25,7 +24,6 @@ import org.springframework.data.jdbc.core.JdbcAggregateOperations;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -35,6 +33,7 @@ import org.springframework.util.Assert;
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
* @author Milan Milanov
|
||||
* @author Chirag Tailor
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public class SimpleJdbcRepository<T, ID> implements CrudRepository<T,ID>, PagingAndSortingRepository<T, ID> {
|
||||
@@ -60,10 +59,7 @@ public class SimpleJdbcRepository<T, ID> implements CrudRepository<T,ID>, Paging
|
||||
@Transactional
|
||||
@Override
|
||||
public <S extends T> Iterable<S> saveAll(Iterable<S> entities) {
|
||||
|
||||
return Streamable.of(entities).stream() //
|
||||
.map(this::save) //
|
||||
.collect(Collectors.toList());
|
||||
return entityOperations.saveAll(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.convert.JdbcConverter;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PersistentPropertyPaths;
|
||||
import org.springframework.data.relational.core.conversion.AggregateChangeWithRoot;
|
||||
import org.springframework.data.relational.core.conversion.RootAggregateChange;
|
||||
import org.springframework.data.relational.core.conversion.DbAction;
|
||||
import org.springframework.data.relational.core.conversion.IdValueSource;
|
||||
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
|
||||
@@ -80,10 +80,11 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
@Test // DATAJDBC-291
|
||||
public void singleRoot() {
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertThat(entity.rootId).isEqualTo(1);
|
||||
}
|
||||
@@ -93,11 +94,12 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
|
||||
entity = entity.withSingle(content);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("single", content, null));
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -111,12 +113,13 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
|
||||
entity = entity.withContentList(asList(content, content2));
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("contentList", content, 0));
|
||||
aggregateChange.addAction(createInsert("contentList", content2, 1));
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -130,12 +133,13 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
|
||||
entity = entity.withContentMap(createContentMap("a", content, "b", content2));
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("contentMap", content, "a"));
|
||||
aggregateChange.addAction(createInsert("contentMap", content2, "b"));
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertThat(entity.rootId).isEqualTo(1);
|
||||
assertThat(entity.contentMap.values()).extracting(c -> c.id).containsExactly(2, 3);
|
||||
@@ -150,12 +154,13 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> parentInsert = createInsert("single", content, null);
|
||||
DbAction.Insert<?> insert = createDeepInsert("single", tag1, null, parentInsert);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertThat(entity.rootId).isEqualTo(1);
|
||||
assertThat(entity.single.id).isEqualTo(2);
|
||||
@@ -172,13 +177,14 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("tagList", tag1, 0, parentInsert);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagList", tag2, 1, parentInsert);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -198,13 +204,14 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("tagSet", tag1, null, parentInsert);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagSet", tag2, null, parentInsert);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -231,14 +238,15 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -262,7 +270,7 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagList", tag2, 0, parentInsert2);
|
||||
DbAction.Insert<?> insert3 = createDeepInsert("tagList", tag3, 1, parentInsert2);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
@@ -270,7 +278,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
aggregateChange.addAction(insert2);
|
||||
aggregateChange.addAction(insert3);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -297,7 +306,7 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagMap", tag2, "222", parentInsert2);
|
||||
DbAction.Insert<?> insert3 = createDeepInsert("tagMap", tag3, "333", parentInsert2);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
@@ -305,7 +314,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
aggregateChange.addAction(insert2);
|
||||
aggregateChange.addAction(insert3);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -336,14 +346,15 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -362,11 +373,12 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
|
||||
DbAction.Insert<?> parentInsert = createInsert("embedded.single", tag1, null);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
List<DummyEntity> result = executor.executeSave(aggregateChange);
|
||||
entity = result.get(0);
|
||||
|
||||
assertThat(entity.rootId).isEqualTo(1);
|
||||
assertThat(entity.embedded.single.id).isEqualTo(2);
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.convert.JdbcConverter;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PersistentPropertyPaths;
|
||||
import org.springframework.data.relational.core.conversion.AggregateChangeWithRoot;
|
||||
import org.springframework.data.relational.core.conversion.RootAggregateChange;
|
||||
import org.springframework.data.relational.core.conversion.DbAction;
|
||||
import org.springframework.data.relational.core.conversion.IdValueSource;
|
||||
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
|
||||
@@ -71,10 +71,10 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
@Test // DATAJDBC-291
|
||||
public void singleRoot() {
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
executor.executeSave(aggregateChange);
|
||||
|
||||
assertThat(entity.rootId).isEqualTo(1);
|
||||
}
|
||||
@@ -84,11 +84,11 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
|
||||
entity.single = content;
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("single", content, null));
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
executor.executeSave(aggregateChange);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -103,12 +103,12 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
entity.contentList.add(content);
|
||||
entity.contentList.add(content2);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("contentList", content, 0));
|
||||
aggregateChange.addAction(createInsert("contentList", content2, 1));
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
executor.executeSave(aggregateChange);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -123,12 +123,12 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
entity.contentMap.put("a", content);
|
||||
entity.contentMap.put("b", content2);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("contentMap", content, "a"));
|
||||
aggregateChange.addAction(createInsert("contentMap", content2, "b"));
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
executor.executeSave(aggregateChange);
|
||||
|
||||
assertThat(entity.rootId).isEqualTo(1);
|
||||
assertThat(entity.contentMap.values()).extracting(c -> c.id).containsExactly(2, 3);
|
||||
@@ -143,12 +143,12 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> parentInsert = createInsert("single", content, null);
|
||||
DbAction.Insert<?> insert = createDeepInsert("single", tag1, null, parentInsert);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert);
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
executor.executeSave(aggregateChange);
|
||||
|
||||
assertThat(entity.rootId).isEqualTo(1);
|
||||
assertThat(entity.single.id).isEqualTo(2);
|
||||
@@ -166,13 +166,13 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("tagList", tag1, 0, parentInsert);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagList", tag2, 1, parentInsert);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
executor.executeSave(aggregateChange);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -193,13 +193,13 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("tagSet", tag1, null, parentInsert);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagSet", tag2, null, parentInsert);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
executor.executeSave(aggregateChange);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -227,14 +227,14 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
executor.executeSave(aggregateChange);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -260,7 +260,7 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagList", tag2, 0, parentInsert2);
|
||||
DbAction.Insert<?> insert3 = createDeepInsert("tagList", tag3, 1, parentInsert2);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
@@ -268,7 +268,7 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
aggregateChange.addAction(insert2);
|
||||
aggregateChange.addAction(insert3);
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
executor.executeSave(aggregateChange);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
@@ -298,7 +298,7 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagMap", tag2, "222", parentInsert2);
|
||||
DbAction.Insert<?> insert3 = createDeepInsert("tagMap", tag3, "333", parentInsert2);
|
||||
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
RootAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
@@ -306,7 +306,7 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
aggregateChange.addAction(insert2);
|
||||
aggregateChange.addAction(insert3);
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
executor.executeSave(aggregateChange);
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
* Copyright 2017-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,7 +46,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Salim Achouche
|
||||
* @author Chirag Taylor
|
||||
* @author Chirag Tailor
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@Transactional
|
||||
|
||||
@@ -46,7 +46,7 @@ import org.springframework.lang.Nullable;
|
||||
* Test for the {@link JdbcAggregateChangeExecutionContext} when operating on immutable classes.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Chirag Taylor
|
||||
* @author Chirag Tailor
|
||||
*/
|
||||
public class JdbcAggregateChangeExecutorContextImmutableUnitTests {
|
||||
|
||||
@@ -70,9 +70,10 @@ public class JdbcAggregateChangeExecutorContextImmutableUnitTests {
|
||||
|
||||
executionContext.executeInsertRoot(new DbAction.InsertRoot<>(root, IdValueSource.GENERATED));
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isNotNull();
|
||||
assertThat(newRoots).hasSize(1);
|
||||
DummyEntity newRoot = newRoots.get(0);
|
||||
assertThat(newRoot.id).isEqualTo(23L);
|
||||
}
|
||||
|
||||
@@ -83,16 +84,17 @@ public class JdbcAggregateChangeExecutorContextImmutableUnitTests {
|
||||
|
||||
when(accessStrategy.insert(any(DummyEntity.class), eq(DummyEntity.class), eq(Identifier.empty()),
|
||||
eq(IdValueSource.GENERATED))).thenReturn(23L);
|
||||
when(accessStrategy.insert(any(Content.class), eq(Content.class), eq(createBackRef()), eq(IdValueSource.GENERATED)))
|
||||
when(accessStrategy.insert(any(Content.class), eq(Content.class), eq(createBackRef(23L)), eq(IdValueSource.GENERATED)))
|
||||
.thenReturn(24L);
|
||||
|
||||
DbAction.InsertRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(root, IdValueSource.GENERATED);
|
||||
executionContext.executeInsertRoot(rootInsert);
|
||||
executionContext.executeInsert(createInsert(rootInsert, "content", content, null));
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isNotNull();
|
||||
assertThat(newRoots).hasSize(1);
|
||||
DummyEntity newRoot = newRoots.get(0);
|
||||
assertThat(newRoot.id).isEqualTo(23L);
|
||||
|
||||
assertThat(newRoot.content.id).isEqualTo(24L);
|
||||
@@ -112,14 +114,46 @@ public class JdbcAggregateChangeExecutorContextImmutableUnitTests {
|
||||
executionContext.executeInsertRoot(rootInsert);
|
||||
executionContext.executeInsert(createInsert(rootInsert, "list", content, 1));
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isNotNull();
|
||||
assertThat(newRoots).hasSize(1);
|
||||
DummyEntity newRoot = newRoots.get(0);
|
||||
assertThat(newRoot.id).isEqualTo(23L);
|
||||
|
||||
assertThat(newRoot.list.get(0).id).isEqualTo(24L);
|
||||
}
|
||||
|
||||
@Test // GH-537
|
||||
void populatesIdsIfNecessaryForAllRootsThatWereProcessed() {
|
||||
|
||||
DummyEntity root1 = new DummyEntity().withId(123L);
|
||||
when(accessStrategy.update(root1, DummyEntity.class)).thenReturn(true);
|
||||
DbAction.UpdateRoot<DummyEntity> rootUpdate1 = new DbAction.UpdateRoot<>(root1, null);
|
||||
executionContext.executeUpdateRoot(rootUpdate1);
|
||||
Content content1 = new Content();
|
||||
when(accessStrategy.insert(content1, Content.class, createBackRef(123L), IdValueSource.GENERATED)).thenReturn(11L);
|
||||
executionContext.executeInsert(createInsert(rootUpdate1, "content", content1, null));
|
||||
|
||||
|
||||
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);
|
||||
executionContext.executeInsertRoot(rootInsert2);
|
||||
Content content2 = new Content();
|
||||
when(accessStrategy.insert(content2, Content.class, createBackRef(456L), IdValueSource.GENERATED)).thenReturn(12L);
|
||||
executionContext.executeInsert(createInsert(rootInsert2, "content", content2, null));
|
||||
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoots).hasSize(2);
|
||||
DummyEntity newRoot1 = newRoots.get(0);
|
||||
assertThat(newRoot1.id).isEqualTo(123L);
|
||||
assertThat(newRoot1.content.id).isEqualTo(11L);
|
||||
DummyEntity newRoot2 = newRoots.get(1);
|
||||
assertThat(newRoot2.id).isEqualTo(456L);
|
||||
assertThat(newRoot2.content.id).isEqualTo(12L);
|
||||
}
|
||||
|
||||
DbAction.Insert<?> createInsert(DbAction.WithEntity<?> parent, String propertyName, Object value,
|
||||
@Nullable Object key) {
|
||||
|
||||
@@ -135,8 +169,8 @@ public class JdbcAggregateChangeExecutorContextImmutableUnitTests {
|
||||
return context.getPersistentPropertyPath(propertyName, DummyEntity.class);
|
||||
}
|
||||
|
||||
Identifier createBackRef() {
|
||||
return JdbcIdentifierBuilder.forBackReferences(converter, toPathExt("content"), 23L).build();
|
||||
Identifier createBackRef(long value) {
|
||||
return JdbcIdentifierBuilder.forBackReferences(converter, toPathExt("content"), value).build();
|
||||
}
|
||||
|
||||
PersistentPropertyPath<RelationalPersistentProperty> toPath(String path) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.jdbc.core;
|
||||
import static java.util.Collections.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder.*;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@@ -31,7 +32,6 @@ import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.convert.Identifier;
|
||||
import org.springframework.data.jdbc.core.convert.InsertSubject;
|
||||
import org.springframework.data.jdbc.core.convert.JdbcConverter;
|
||||
import org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PersistentPropertyPaths;
|
||||
import org.springframework.data.relational.core.conversion.DbAction;
|
||||
@@ -69,9 +69,9 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
|
||||
executionContext.executeInsertRoot(new DbAction.InsertRoot<>(root, IdValueSource.GENERATED));
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
assertThat(newRoots).containsExactly(root);
|
||||
assertThat(root.id).isEqualTo(23L);
|
||||
}
|
||||
|
||||
@@ -81,15 +81,15 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
Content content = new Content();
|
||||
|
||||
when(accessStrategy.insert(root, DummyEntity.class, Identifier.empty(), IdValueSource.GENERATED)).thenReturn(23L);
|
||||
when(accessStrategy.insert(content, Content.class, createBackRef(), IdValueSource.GENERATED)).thenReturn(24L);
|
||||
when(accessStrategy.insert(content, Content.class, createBackRef(23L), IdValueSource.GENERATED)).thenReturn(24L);
|
||||
|
||||
DbAction.InsertRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(root, IdValueSource.GENERATED);
|
||||
executionContext.executeInsertRoot(rootInsert);
|
||||
executionContext.executeInsert(createInsert(rootInsert, "content", content, null));
|
||||
executionContext.executeInsert(createInsert(rootInsert, "content", content, null, IdValueSource.GENERATED));
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
assertThat(newRoots).containsExactly(root);
|
||||
assertThat(root.id).isEqualTo(23L);
|
||||
|
||||
assertThat(content.id).isEqualTo(24L);
|
||||
@@ -106,11 +106,11 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
|
||||
DbAction.InsertRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(root, IdValueSource.GENERATED);
|
||||
executionContext.executeInsertRoot(rootInsert);
|
||||
executionContext.executeInsert(createInsert(rootInsert, "list", content, 1));
|
||||
executionContext.executeInsert(createInsert(rootInsert, "list", content, 1, IdValueSource.GENERATED));
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
assertThat(newRoots).containsExactly(root);
|
||||
assertThat(root.id).isEqualTo(23L);
|
||||
|
||||
assertThat(content.id).isEqualTo(24L);
|
||||
@@ -129,13 +129,13 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
.withPart(SqlIdentifier.quoted("DUMMY_ENTITY_KEY"), 0, Integer.class);
|
||||
when(accessStrategy.insert(singletonList(InsertSubject.describedBy(content, identifier)), Content.class,
|
||||
IdValueSource.GENERATED)).thenReturn(new Object[] { 456L });
|
||||
DbAction.InsertBatch<?> insertBatch = new DbAction.InsertBatch<>(
|
||||
singletonList(createInsert(rootInsert, "list", content, 0)), IdValueSource.GENERATED);
|
||||
executionContext.executeInsertBatch(insertBatch);
|
||||
DbAction.BatchInsert<?> batchInsert = new DbAction.BatchInsert<>(
|
||||
singletonList(createInsert(rootInsert, "list", content, 0, IdValueSource.GENERATED)));
|
||||
executionContext.executeBatchInsert(batchInsert);
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
assertThat(newRoots).containsExactly(root);
|
||||
assertThat(root.id).isEqualTo(123L);
|
||||
assertThat(content.id).isEqualTo(456L);
|
||||
}
|
||||
@@ -153,42 +153,73 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
.withPart(SqlIdentifier.quoted("DUMMY_ENTITY_KEY"), 0, Integer.class);
|
||||
when(accessStrategy.insert(singletonList(InsertSubject.describedBy(content, identifier)), Content.class,
|
||||
IdValueSource.PROVIDED)).thenReturn(new Object[] { null });
|
||||
DbAction.InsertBatch<?> insertBatch = new DbAction.InsertBatch<>(
|
||||
singletonList(createInsert(rootInsert, "list", content, 0)), IdValueSource.PROVIDED);
|
||||
executionContext.executeInsertBatch(insertBatch);
|
||||
DbAction.BatchInsert<?> batchInsert = new DbAction.BatchInsert<>(
|
||||
singletonList(createInsert(rootInsert, "list", content, 0, IdValueSource.PROVIDED)));
|
||||
executionContext.executeBatchInsert(batchInsert);
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
assertThat(newRoots).containsExactly(root);
|
||||
assertThat(root.id).isEqualTo(123L);
|
||||
assertThat(content.id).isNull();
|
||||
}
|
||||
|
||||
@Test // GH-1201
|
||||
void updates_whenReferencesWithImmutableIdAreInserted() {
|
||||
when(accessStrategy.update(any(), any())).thenReturn(true);
|
||||
|
||||
root.id = 123L;
|
||||
DbAction.UpdateRoot<DummyEntity> rootInsert = new DbAction.UpdateRoot<>(root, null);
|
||||
executionContext.executeUpdateRoot(rootInsert);
|
||||
when(accessStrategy.update(root, DummyEntity.class)).thenReturn(true);
|
||||
DbAction.UpdateRoot<DummyEntity> rootUpdate = new DbAction.UpdateRoot<>(root, null);
|
||||
executionContext.executeUpdateRoot(rootUpdate);
|
||||
|
||||
ContentImmutableId contentImmutableId = new ContentImmutableId(null);
|
||||
root.contentImmutableId = contentImmutableId;
|
||||
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(rootInsert, "contentImmutableId", contentImmutableId, null));
|
||||
executionContext.executeInsert(createInsert(rootUpdate, "contentImmutableId", contentImmutableId, null, IdValueSource.GENERATED));
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
assertThat(newRoots).containsExactly(root);
|
||||
assertThat(root.id).isEqualTo(123L);
|
||||
assertThat(root.contentImmutableId.id).isEqualTo(456L);
|
||||
}
|
||||
|
||||
@Test // GH-537
|
||||
void populatesIdsIfNecessaryForAllRootsThatWereProcessed() {
|
||||
|
||||
DummyEntity root1 = new DummyEntity();
|
||||
root1.id = 123L;
|
||||
when(accessStrategy.update(root1, DummyEntity.class)).thenReturn(true);
|
||||
DbAction.UpdateRoot<DummyEntity> rootUpdate1 = new DbAction.UpdateRoot<>(root1, null);
|
||||
executionContext.executeUpdateRoot(rootUpdate1);
|
||||
Content content1 = new Content();
|
||||
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);
|
||||
executionContext.executeInsertRoot(rootInsert2);
|
||||
Content content2 = new Content();
|
||||
when(accessStrategy.insert(content2, Content.class, createBackRef(456L), IdValueSource.GENERATED)).thenReturn(12L);
|
||||
executionContext.executeInsert(createInsert(rootInsert2, "content", content2, null, IdValueSource.GENERATED));
|
||||
|
||||
List<DummyEntity> newRoots = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoots).containsExactly(root1, root2);
|
||||
assertThat(root1.id).isEqualTo(123L);
|
||||
assertThat(content1.id).isEqualTo(11L);
|
||||
assertThat(root2.id).isEqualTo(456L);
|
||||
assertThat(content2.id).isEqualTo(12L);
|
||||
}
|
||||
|
||||
DbAction.Insert<?> createInsert(DbAction.WithEntity<?> parent, String propertyName, Object value,
|
||||
@Nullable Object key) {
|
||||
@Nullable Object key, IdValueSource idValueSource) {
|
||||
|
||||
return new DbAction.Insert<>(value, getPersistentPropertyPath(propertyName), parent,
|
||||
key == null ? emptyMap() : singletonMap(toPath(propertyName), key), IdValueSource.GENERATED);
|
||||
key == null ? emptyMap() : singletonMap(toPath(propertyName), key), idValueSource);
|
||||
}
|
||||
|
||||
PersistentPropertyPathExtension toPathExt(String path) {
|
||||
@@ -199,8 +230,8 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
return context.getPersistentPropertyPath(propertyName, DummyEntity.class);
|
||||
}
|
||||
|
||||
Identifier createBackRef() {
|
||||
return JdbcIdentifierBuilder.forBackReferences(converter, toPathExt("content"), 23L).build();
|
||||
Identifier createBackRef(long value) {
|
||||
return forBackReferences(converter, toPathExt("content"), value).build();
|
||||
}
|
||||
|
||||
PersistentPropertyPath<RelationalPersistentProperty> toPath(String path) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.Collections.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.SoftAssertions.*;
|
||||
import static org.springframework.test.context.TestExecutionListeners.MergeMode.*;
|
||||
@@ -47,6 +48,8 @@ import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
||||
import org.springframework.data.relational.core.mapping.MappedCollection;
|
||||
import org.springframework.data.relational.repository.Lock;
|
||||
import org.springframework.data.jdbc.repository.query.Modifying;
|
||||
import org.springframework.data.jdbc.repository.query.Query;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
@@ -59,6 +62,7 @@ import org.springframework.data.relational.core.mapping.event.AfterConvertEvent;
|
||||
import org.springframework.data.relational.core.sql.LockMode;
|
||||
import org.springframework.data.relational.repository.Lock;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.ListCrudRepository;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
@@ -90,6 +94,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
@Autowired NamedParameterJdbcTemplate template;
|
||||
@Autowired DummyEntityRepository repository;
|
||||
@Autowired MyEventListener eventListener;
|
||||
@Autowired RootRepository rootRepository;
|
||||
|
||||
private static DummyEntity createDummyEntity() {
|
||||
|
||||
@@ -129,7 +134,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void savesManyEntities() {
|
||||
public void insertsManyEntities() {
|
||||
|
||||
DummyEntity entity = createDummyEntity();
|
||||
DummyEntity other = createDummyEntity();
|
||||
@@ -283,6 +288,20 @@ public class JdbcRepositoryIntegrationTests {
|
||||
.containsExactlyInAnyOrder(entity.getName(), other.getName());
|
||||
}
|
||||
|
||||
@Test // GH-537
|
||||
void insertsOrUpdatesManyEntities() {
|
||||
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
entity.setName("something else");
|
||||
DummyEntity other = createDummyEntity();
|
||||
other.setName("others name");
|
||||
repository.saveAll(asList(other, entity));
|
||||
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getName) //
|
||||
.containsExactlyInAnyOrder(entity.getName(), other.getName());
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-112
|
||||
public void findByIdReturnsEmptyWhenNoneFound() {
|
||||
|
||||
@@ -608,6 +627,84 @@ public class JdbcRepositoryIntegrationTests {
|
||||
.containsExactlyInAnyOrder(Direction.CENTER);
|
||||
}
|
||||
|
||||
@Test // GH-537
|
||||
void manyInsertsWithNestedEntities() {
|
||||
Root root1 = createRoot("root1");
|
||||
Root root2 = createRoot("root2");
|
||||
|
||||
List<Root> savedRoots = rootRepository.saveAll(asList(root1, root2));
|
||||
|
||||
List<Root> reloadedRoots = rootRepository.findAllByOrderByIdAsc();
|
||||
assertThat(reloadedRoots).isEqualTo(savedRoots);
|
||||
assertThat(reloadedRoots).hasSize(2);
|
||||
assertIsEqualToWithNonNullIds(reloadedRoots.get(0), root1);
|
||||
assertIsEqualToWithNonNullIds(reloadedRoots.get(1), root2);
|
||||
}
|
||||
|
||||
@Test // GH-537
|
||||
@EnabledOnFeature(TestDatabaseFeatures.Feature.SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES)
|
||||
void manyUpdatesWithNestedEntities() {
|
||||
Root root1 = createRoot("root1");
|
||||
Root root2 = createRoot("root2");
|
||||
List<Root> roots = rootRepository.saveAll(asList(root1, root2));
|
||||
Root savedRoot1 = roots.get(0);
|
||||
Root updatedRoot1 = new Root(savedRoot1.id, "updated" + savedRoot1.name,
|
||||
new Intermediate(savedRoot1.intermediate.id, "updated" + savedRoot1.intermediate.name,
|
||||
new Leaf(savedRoot1.intermediate.leaf.id, "updated" + savedRoot1.intermediate.leaf.name), emptyList()),
|
||||
savedRoot1.intermediates);
|
||||
Root savedRoot2 = roots.get(1);
|
||||
Root updatedRoot2 = new Root(savedRoot2.id, "updated" + savedRoot2.name, savedRoot2.intermediate,
|
||||
singletonList(
|
||||
new Intermediate(savedRoot2.intermediates.get(0).id, "updated" + savedRoot2.intermediates.get(0).name, null,
|
||||
singletonList(new Leaf(savedRoot2.intermediates.get(0).leaves.get(0).id,
|
||||
"updated" + savedRoot2.intermediates.get(0).leaves.get(0).name)))));
|
||||
|
||||
List<Root> updatedRoots = rootRepository.saveAll(asList(updatedRoot1, updatedRoot2));
|
||||
|
||||
List<Root> reloadedRoots = rootRepository.findAllByOrderByIdAsc();
|
||||
assertThat(reloadedRoots).isEqualTo(updatedRoots);
|
||||
assertThat(reloadedRoots).containsExactly(updatedRoot1, updatedRoot2);
|
||||
}
|
||||
|
||||
@Test // GH-537
|
||||
@EnabledOnFeature(TestDatabaseFeatures.Feature.SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES)
|
||||
void manyInsertsAndUpdatesWithNestedEntities() {
|
||||
Root root1 = createRoot("root1");
|
||||
Root savedRoot1 = rootRepository.save(root1);
|
||||
Root updatedRoot1 = new Root(savedRoot1.id, "updated" + savedRoot1.name,
|
||||
new Intermediate(savedRoot1.intermediate.id, "updated" + savedRoot1.intermediate.name,
|
||||
new Leaf(savedRoot1.intermediate.leaf.id, "updated" + savedRoot1.intermediate.leaf.name), emptyList()),
|
||||
savedRoot1.intermediates);
|
||||
Root root2 = createRoot("root2");
|
||||
List<Root> savedRoots = rootRepository.saveAll(asList(updatedRoot1, root2));
|
||||
|
||||
List<Root> reloadedRoots = rootRepository.findAllByOrderByIdAsc();
|
||||
assertThat(reloadedRoots).isEqualTo(savedRoots);
|
||||
assertThat(reloadedRoots.get(0)).isEqualTo(updatedRoot1);
|
||||
assertIsEqualToWithNonNullIds(reloadedRoots.get(1), root2);
|
||||
}
|
||||
|
||||
private Root createRoot(String namePrefix) {
|
||||
return new Root(null, namePrefix,
|
||||
new Intermediate(null, namePrefix + "Intermediate", new Leaf(null, namePrefix + "Leaf"), emptyList()),
|
||||
singletonList(new Intermediate(null, namePrefix + "QualifiedIntermediate", null,
|
||||
singletonList(new Leaf(null, namePrefix + "QualifiedLeaf")))));
|
||||
}
|
||||
|
||||
private void assertIsEqualToWithNonNullIds(Root reloadedRoot1, Root root1) {
|
||||
assertThat(reloadedRoot1.id).isNotNull();
|
||||
assertThat(reloadedRoot1.name).isEqualTo(root1.name);
|
||||
assertThat(reloadedRoot1.intermediate.id).isNotNull();
|
||||
assertThat(reloadedRoot1.intermediate.name).isEqualTo(root1.intermediate.name);
|
||||
assertThat(reloadedRoot1.intermediates.get(0).id).isNotNull();
|
||||
assertThat(reloadedRoot1.intermediates.get(0).name).isEqualTo(root1.intermediates.get(0).name);
|
||||
assertThat(reloadedRoot1.intermediate.leaf.id).isNotNull();
|
||||
assertThat(reloadedRoot1.intermediate.leaf.name).isEqualTo(root1.intermediate.leaf.name);
|
||||
assertThat(reloadedRoot1.intermediates.get(0).leaves.get(0).id).isNotNull();
|
||||
assertThat(reloadedRoot1.intermediates.get(0).leaves.get(0).name)
|
||||
.isEqualTo(root1.intermediates.get(0).leaves.get(0).name);
|
||||
}
|
||||
|
||||
private Instant createDummyBeforeAndAfterNow() {
|
||||
|
||||
Instant now = Instant.now();
|
||||
@@ -717,6 +814,11 @@ public class JdbcRepositoryIntegrationTests {
|
||||
return factory.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
RootRepository rootRepository() {
|
||||
return factory.getRepository(RootRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
NamedQueries namedQueries() throws IOException {
|
||||
|
||||
@@ -732,6 +834,32 @@ public class JdbcRepositoryIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
interface RootRepository extends ListCrudRepository<Root, Long> {
|
||||
List<Root> findAllByOrderByIdAsc();
|
||||
}
|
||||
|
||||
@Value
|
||||
static class Root {
|
||||
@Id Long id;
|
||||
String name;
|
||||
Intermediate intermediate;
|
||||
@MappedCollection(idColumn = "ROOT_ID", keyColumn = "ROOT_KEY") List<Intermediate> intermediates;
|
||||
}
|
||||
|
||||
@Value
|
||||
static class Intermediate {
|
||||
@Id Long id;
|
||||
String name;
|
||||
Leaf leaf;
|
||||
@MappedCollection(idColumn = "INTERMEDIATE_ID", keyColumn = "INTERMEDIATE_KEY") List<Leaf> leaves;
|
||||
}
|
||||
|
||||
@Value
|
||||
static class Leaf {
|
||||
@Id Long id;
|
||||
String name;
|
||||
}
|
||||
|
||||
static class MyEventListener implements ApplicationListener<AbstractRelationalEvent<?>> {
|
||||
|
||||
private List<AbstractRelationalEvent<?>> events = new ArrayList<>();
|
||||
|
||||
@@ -68,6 +68,8 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
|
||||
private static final long generatedId = 4711L;
|
||||
|
||||
CollectingEventPublisher publisher = new CollectingEventPublisher();
|
||||
|
||||
DummyEntityRepository repository;
|
||||
@@ -125,14 +127,14 @@ public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
repository.saveAll(asList(entity1, entity2));
|
||||
|
||||
assertThat(publisher.events) //
|
||||
.extracting(e -> (Class) e.getClass()) //
|
||||
.extracting(RelationalEvent::getClass, e -> ((DummyEntity) e.getEntity()).getId()) //
|
||||
.containsExactly( //
|
||||
BeforeConvertEvent.class, //
|
||||
BeforeSaveEvent.class, //
|
||||
AfterSaveEvent.class, //
|
||||
BeforeConvertEvent.class, //
|
||||
BeforeSaveEvent.class, //
|
||||
AfterSaveEvent.class //
|
||||
Tuple.tuple(BeforeConvertEvent.class, null), //
|
||||
Tuple.tuple(BeforeSaveEvent.class, null), //
|
||||
Tuple.tuple(BeforeConvertEvent.class, 23L), //
|
||||
Tuple.tuple(BeforeSaveEvent.class, 23L), //
|
||||
Tuple.tuple(AfterSaveEvent.class, generatedId), //
|
||||
Tuple.tuple(AfterSaveEvent.class, 23L) //
|
||||
);
|
||||
}
|
||||
|
||||
@@ -275,7 +277,7 @@ public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
Answer<Integer> setIdInKeyHolder = invocation -> {
|
||||
|
||||
HashMap<String, Object> keys = new HashMap<>();
|
||||
keys.put("id", 4711L);
|
||||
keys.put("id", generatedId);
|
||||
KeyHolder keyHolder = invocation.getArgument(2);
|
||||
keyHolder.getKeyList().add(keys);
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
DROP TABLE dummy_entity;
|
||||
DROP TABLE ROOT;
|
||||
DROP TABLE INTERMEDIATE;
|
||||
DROP TABLE LEAF;
|
||||
|
||||
CREATE TABLE dummy_entity
|
||||
(
|
||||
@@ -10,3 +13,25 @@ CREATE TABLE dummy_entity
|
||||
REF BIGINT,
|
||||
DIRECTION VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE TABLE ROOT
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
CREATE TABLE INTERMEDIATE
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
ROOT BIGINT,
|
||||
ROOT_ID BIGINT,
|
||||
ROOT_KEY INTEGER
|
||||
);
|
||||
CREATE TABLE LEAF
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
INTERMEDIATE BIGINT,
|
||||
INTERMEDIATE_ID BIGINT,
|
||||
INTERMEDIATE_KEY INTEGER
|
||||
);
|
||||
|
||||
@@ -8,3 +8,25 @@ CREATE TABLE dummy_entity
|
||||
REF BIGINT,
|
||||
DIRECTION VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE TABLE ROOT
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
CREATE TABLE INTERMEDIATE
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
ROOT BIGINT,
|
||||
ROOT_ID BIGINT,
|
||||
ROOT_KEY INTEGER
|
||||
);
|
||||
CREATE TABLE LEAF
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
INTERMEDIATE BIGINT,
|
||||
INTERMEDIATE_ID BIGINT,
|
||||
INTERMEDIATE_KEY INTEGER
|
||||
);
|
||||
|
||||
@@ -8,3 +8,25 @@ CREATE TABLE dummy_entity
|
||||
REF BIGINT,
|
||||
DIRECTION VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE TABLE ROOT
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
CREATE TABLE INTERMEDIATE
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
ROOT BIGINT,
|
||||
ROOT_ID BIGINT,
|
||||
ROOT_KEY INTEGER
|
||||
);
|
||||
CREATE TABLE LEAF
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
INTERMEDIATE BIGINT,
|
||||
INTERMEDIATE_ID BIGINT,
|
||||
INTERMEDIATE_KEY INTEGER
|
||||
);
|
||||
|
||||
@@ -8,3 +8,25 @@ CREATE TABLE dummy_entity
|
||||
REF BIGINT,
|
||||
DIRECTION VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE TABLE ROOT
|
||||
(
|
||||
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
CREATE TABLE INTERMEDIATE
|
||||
(
|
||||
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
ROOT BIGINT,
|
||||
ROOT_ID BIGINT,
|
||||
ROOT_KEY INTEGER
|
||||
);
|
||||
CREATE TABLE LEAF
|
||||
(
|
||||
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
INTERMEDIATE BIGINT,
|
||||
INTERMEDIATE_ID BIGINT,
|
||||
INTERMEDIATE_KEY INTEGER
|
||||
);
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
DROP TABLE IF EXISTS dummy_entity;
|
||||
DROP TABLE IF EXISTS ROOT;
|
||||
DROP TABLE IF EXISTS INTERMEDIATE;
|
||||
DROP TABLE IF EXISTS LEAF;
|
||||
|
||||
CREATE TABLE dummy_entity
|
||||
(
|
||||
id_Prop BIGINT IDENTITY PRIMARY KEY,
|
||||
@@ -9,3 +13,25 @@ CREATE TABLE dummy_entity
|
||||
REF BIGINT,
|
||||
DIRECTION VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE TABLE ROOT
|
||||
(
|
||||
ID BIGINT IDENTITY PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
CREATE TABLE INTERMEDIATE
|
||||
(
|
||||
ID BIGINT IDENTITY PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
ROOT BIGINT,
|
||||
ROOT_ID BIGINT,
|
||||
ROOT_KEY INTEGER
|
||||
);
|
||||
CREATE TABLE LEAF
|
||||
(
|
||||
ID BIGINT IDENTITY PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
INTERMEDIATE BIGINT,
|
||||
INTERMEDIATE_ID BIGINT,
|
||||
INTERMEDIATE_KEY INTEGER
|
||||
);
|
||||
|
||||
@@ -11,3 +11,26 @@ CREATE TABLE DUMMY_ENTITY
|
||||
REF BIGINT,
|
||||
DIRECTION VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE TABLE ROOT
|
||||
(
|
||||
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
CREATE TABLE INTERMEDIATE
|
||||
(
|
||||
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
ROOT BIGINT,
|
||||
ROOT_ID BIGINT,
|
||||
ROOT_KEY INTEGER
|
||||
);
|
||||
CREATE TABLE LEAF
|
||||
(
|
||||
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
INTERMEDIATE BIGINT,
|
||||
INTERMEDIATE_ID BIGINT,
|
||||
INTERMEDIATE_KEY INTEGER
|
||||
);
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
DROP TABLE DUMMY_ENTITY CASCADE CONSTRAINTS PURGE;
|
||||
DROP TABLE ROOT CASCADE CONSTRAINTS PURGE;
|
||||
DROP TABLE INTERMEDIATE CASCADE CONSTRAINTS PURGE;
|
||||
DROP TABLE LEAF CASCADE CONSTRAINTS PURGE;
|
||||
|
||||
CREATE TABLE DUMMY_ENTITY
|
||||
(
|
||||
@@ -10,3 +13,25 @@ CREATE TABLE DUMMY_ENTITY
|
||||
REF NUMBER,
|
||||
DIRECTION VARCHAR2(100)
|
||||
);
|
||||
|
||||
CREATE TABLE ROOT
|
||||
(
|
||||
ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
|
||||
NAME VARCHAR2(100)
|
||||
);
|
||||
CREATE TABLE INTERMEDIATE
|
||||
(
|
||||
ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
|
||||
NAME VARCHAR2(100),
|
||||
ROOT NUMBER,
|
||||
ROOT_ID NUMBER,
|
||||
ROOT_KEY NUMBER
|
||||
);
|
||||
CREATE TABLE LEAF
|
||||
(
|
||||
ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
|
||||
NAME VARCHAR2(100),
|
||||
INTERMEDIATE NUMBER,
|
||||
INTERMEDIATE_ID NUMBER,
|
||||
INTERMEDIATE_KEY NUMBER
|
||||
);
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
DROP TABLE dummy_entity;
|
||||
DROP TABLE ROOT;
|
||||
DROP TABLE INTERMEDIATE;
|
||||
DROP TABLE LEAF;
|
||||
|
||||
CREATE TABLE dummy_entity
|
||||
(
|
||||
id_Prop SERIAL PRIMARY KEY,
|
||||
@@ -9,3 +13,25 @@ CREATE TABLE dummy_entity
|
||||
REF BIGINT,
|
||||
DIRECTION VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE TABLE ROOT
|
||||
(
|
||||
ID SERIAL PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
CREATE TABLE INTERMEDIATE
|
||||
(
|
||||
ID SERIAL PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
ROOT BIGINT,
|
||||
"ROOT_ID" BIGINT,
|
||||
"ROOT_KEY" INTEGER
|
||||
);
|
||||
CREATE TABLE LEAF
|
||||
(
|
||||
ID SERIAL PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
INTERMEDIATE BIGINT,
|
||||
"INTERMEDIATE_ID" BIGINT,
|
||||
"INTERMEDIATE_KEY" INTEGER
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user