Capture execution results for update db actions to fix root references with immutably typed ids.
+ Added #getIdValueSource to DbAction.WithEntity since it now applies to all such DbAction. + Removed DbAction.WithGeneratedId in favor of DbAction.WithEntity where it was used. Removes unused DbAction.Update. Passes the root from BeforeSaveCallback through to InsertRoot,UpdateRoot actions. + Isolate aggregate change #getEntity #setEntity behaviors into a new interface for "save" changes. + Extract #setEntity from DbAction.WithEntity to a new DbAction interface just for root actions. + JdbcAggregateChangeExecutionContext#populateIdsIfNecessary returns the root entity from DbAction.InsertRoot or DbAction.UpdateRoot regardless of the immutability of its @Id property. Closes #1199 Closes #1201 Original pull request #1208
This commit is contained in:
committed by
Jens Schauder
parent
0500442ac3
commit
508791b7df
@@ -18,10 +18,10 @@ 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 org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Executes an {@link MutableAggregateChange}.
|
||||
@@ -42,20 +42,22 @@ class AggregateChangeExecutor {
|
||||
this.accessStrategy = accessStrategy;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
<T> T execute(AggregateChange<T> aggregateChange) {
|
||||
<T> T execute(AggregateChangeWithRoot<T> aggregateChange) {
|
||||
|
||||
JdbcAggregateChangeExecutionContext executionContext = new JdbcAggregateChangeExecutionContext(converter,
|
||||
accessStrategy);
|
||||
|
||||
aggregateChange.forEachAction(action -> execute(action, executionContext));
|
||||
|
||||
T root = executionContext.populateIdsIfNecessary();
|
||||
if (root == null) {
|
||||
root = aggregateChange.getEntity();
|
||||
}
|
||||
return executionContext.populateIdsIfNecessary();
|
||||
}
|
||||
|
||||
return root;
|
||||
<T> void execute(AggregateChange<T> aggregateChange) {
|
||||
|
||||
JdbcAggregateChangeExecutionContext executionContext = new JdbcAggregateChangeExecutionContext(converter,
|
||||
accessStrategy);
|
||||
|
||||
aggregateChange.forEachAction(action -> execute(action, executionContext));
|
||||
}
|
||||
|
||||
private void execute(DbAction<?> action, JdbcAggregateChangeExecutionContext executionContext) {
|
||||
@@ -69,8 +71,6 @@ class AggregateChangeExecutor {
|
||||
executionContext.executeInsertBatch((DbAction.InsertBatch<?>) action);
|
||||
} else if (action instanceof DbAction.UpdateRoot) {
|
||||
executionContext.executeUpdateRoot((DbAction.UpdateRoot<?>) action);
|
||||
} else if (action instanceof DbAction.Update) {
|
||||
executionContext.executeUpdate((DbAction.Update<?>) action);
|
||||
} else if (action instanceof DbAction.Delete) {
|
||||
executionContext.executeDelete((DbAction.Delete<?>) action);
|
||||
} else if (action instanceof DbAction.DeleteAll) {
|
||||
|
||||
@@ -15,15 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -40,6 +32,7 @@ import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.relational.core.conversion.DbAction;
|
||||
import org.springframework.data.relational.core.conversion.DbActionExecutionResult;
|
||||
import org.springframework.data.relational.core.conversion.IdValueSource;
|
||||
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
|
||||
@@ -108,15 +101,7 @@ class JdbcAggregateChangeExecutionContext {
|
||||
} else {
|
||||
updateWithoutVersion(update);
|
||||
}
|
||||
}
|
||||
|
||||
<T> void executeUpdate(DbAction.Update<T> update) {
|
||||
|
||||
if (!accessStrategy.update(update.getEntity(), update.getEntityType())) {
|
||||
|
||||
throw new IncorrectUpdateSemanticsDataAccessException(
|
||||
String.format(UPDATE_FAILED, update.getEntity(), getIdFrom(update)));
|
||||
}
|
||||
add(new DbActionExecutionResult(update));
|
||||
}
|
||||
|
||||
<T> void executeDeleteRoot(DbAction.DeleteRoot<T> delete) {
|
||||
@@ -203,11 +188,12 @@ class JdbcAggregateChangeExecutionContext {
|
||||
|
||||
private Object getPotentialGeneratedIdFrom(DbAction.WithEntity<?> idOwningAction) {
|
||||
|
||||
if (idOwningAction instanceof DbAction.WithGeneratedId) {
|
||||
if (IdValueSource.GENERATED.equals(idOwningAction.getIdValueSource())) {
|
||||
|
||||
Object generatedId;
|
||||
DbActionExecutionResult dbActionExecutionResult = results.get(idOwningAction);
|
||||
generatedId = dbActionExecutionResult == null ? null : dbActionExecutionResult.getId();
|
||||
Object generatedId = Optional.ofNullable(dbActionExecutionResult) //
|
||||
.map(DbActionExecutionResult::getGeneratedId) //
|
||||
.orElse(null);
|
||||
|
||||
if (generatedId != null) {
|
||||
return generatedId;
|
||||
@@ -227,12 +213,8 @@ class JdbcAggregateChangeExecutionContext {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
<T> T populateIdsIfNecessary() {
|
||||
|
||||
T newRoot = null;
|
||||
|
||||
// have the results so that the inserts on the leaves come first.
|
||||
List<DbActionExecutionResult> reverseResults = new ArrayList<>(results.values());
|
||||
Collections.reverse(reverseResults);
|
||||
@@ -241,33 +223,29 @@ class JdbcAggregateChangeExecutionContext {
|
||||
|
||||
for (DbActionExecutionResult result : reverseResults) {
|
||||
|
||||
DbAction<?> action = result.getAction();
|
||||
DbAction.WithEntity<?> action = result.getAction();
|
||||
|
||||
if (!(action instanceof DbAction.WithGeneratedId)) {
|
||||
continue;
|
||||
Object newEntity = setIdAndCascadingProperties(action, result.getGeneratedId(), cascadingValues);
|
||||
|
||||
if (action instanceof DbAction.InsertRoot || action instanceof DbAction.UpdateRoot) {
|
||||
//noinspection unchecked
|
||||
return (T) newEntity;
|
||||
}
|
||||
|
||||
DbAction.WithEntity<?> withEntity = (DbAction.WithGeneratedId<?>) action;
|
||||
Object newEntity = setIdAndCascadingProperties(withEntity, result.getId(), cascadingValues);
|
||||
|
||||
// the id property was immutable so we have to propagate changes up the tree
|
||||
if (newEntity != withEntity.getEntity()) {
|
||||
if (newEntity != action.getEntity() && action instanceof DbAction.Insert) {
|
||||
DbAction.Insert<?> insert = (DbAction.Insert<?>) action;
|
||||
|
||||
if (action instanceof DbAction.Insert) {
|
||||
DbAction.Insert<?> insert = (DbAction.Insert<?>) action;
|
||||
Pair<?, ?> qualifier = insert.getQualifier();
|
||||
|
||||
Pair<?, ?> qualifier = insert.getQualifier();
|
||||
|
||||
cascadingValues.stage(insert.getDependingOn(), insert.getPropertyPath(),
|
||||
qualifier == null ? null : qualifier.getSecond(), newEntity);
|
||||
|
||||
} else if (action instanceof DbAction.InsertRoot) {
|
||||
newRoot = (T) newEntity;
|
||||
}
|
||||
cascadingValues.stage(insert.getDependingOn(), insert.getPropertyPath(),
|
||||
qualifier == null ? null : qualifier.getSecond(), newEntity);
|
||||
}
|
||||
}
|
||||
|
||||
return newRoot;
|
||||
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()));
|
||||
}
|
||||
|
||||
private <S> Object setIdAndCascadingProperties(DbAction.WithEntity<S> action, @Nullable Object generatedId,
|
||||
@@ -279,7 +257,7 @@ class JdbcAggregateChangeExecutionContext {
|
||||
.getRequiredPersistentEntity(action.getEntityType());
|
||||
PersistentPropertyAccessor<S> propertyAccessor = converter.getPropertyAccessor(persistentEntity, originalEntity);
|
||||
|
||||
if (generatedId != null && persistentEntity.hasIdProperty()) {
|
||||
if (IdValueSource.GENERATED.equals(action.getIdValueSource())) {
|
||||
propertyAccessor.setProperty(persistentEntity.getRequiredIdProperty(), generatedId);
|
||||
}
|
||||
|
||||
@@ -301,6 +279,10 @@ class JdbcAggregateChangeExecutionContext {
|
||||
return pathToValue;
|
||||
}
|
||||
|
||||
if (action instanceof DbAction.UpdateRoot) {
|
||||
return pathToValue;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("DbAction of type %s is not supported.", action.getClass()));
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ 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.MutableAggregateChange;
|
||||
import org.springframework.data.relational.core.conversion.RelationalEntityDeleteWriter;
|
||||
import org.springframework.data.relational.core.conversion.RelationalEntityInsertWriter;
|
||||
@@ -61,8 +62,6 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
private final RelationalMappingContext context;
|
||||
|
||||
private final RelationalEntityDeleteWriter jdbcEntityDeleteWriter;
|
||||
private final RelationalEntityInsertWriter jdbcEntityInsertWriter;
|
||||
private final RelationalEntityUpdateWriter jdbcEntityUpdateWriter;
|
||||
|
||||
private final DataAccessStrategy accessStrategy;
|
||||
private final AggregateChangeExecutor executor;
|
||||
@@ -92,8 +91,6 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
this.accessStrategy = dataAccessStrategy;
|
||||
this.converter = converter;
|
||||
|
||||
this.jdbcEntityInsertWriter = new RelationalEntityInsertWriter(context);
|
||||
this.jdbcEntityUpdateWriter = new RelationalEntityUpdateWriter(context);
|
||||
this.jdbcEntityDeleteWriter = new RelationalEntityDeleteWriter(context);
|
||||
|
||||
this.executor = new AggregateChangeExecutor(converter, accessStrategy);
|
||||
@@ -122,8 +119,6 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
this.accessStrategy = dataAccessStrategy;
|
||||
this.converter = converter;
|
||||
|
||||
this.jdbcEntityInsertWriter = new RelationalEntityInsertWriter(context);
|
||||
this.jdbcEntityUpdateWriter = new RelationalEntityUpdateWriter(context);
|
||||
this.jdbcEntityDeleteWriter = new RelationalEntityDeleteWriter(context);
|
||||
this.executor = new AggregateChangeExecutor(converter, accessStrategy);
|
||||
}
|
||||
@@ -146,7 +141,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
|
||||
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(instance.getClass());
|
||||
|
||||
Function<T, MutableAggregateChange<T>> changeCreator = persistentEntity.isNew(instance)
|
||||
Function<T, AggregateChangeWithRoot<T>> changeCreator = persistentEntity.isNew(instance)
|
||||
? entity -> createInsertChange(prepareVersionForInsert(entity))
|
||||
: entity -> createUpdateChange(prepareVersionForUpdate(entity));
|
||||
|
||||
@@ -286,18 +281,18 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
executor.execute(change);
|
||||
}
|
||||
|
||||
private <T> T store(T aggregateRoot, Function<T, MutableAggregateChange<T>> changeCreator,
|
||||
private <T> T store(T aggregateRoot, Function<T, AggregateChangeWithRoot<T>> changeCreator,
|
||||
RelationalPersistentEntity<?> persistentEntity) {
|
||||
|
||||
Assert.notNull(aggregateRoot, "Aggregate instance must not be null!");
|
||||
|
||||
aggregateRoot = triggerBeforeConvert(aggregateRoot);
|
||||
|
||||
MutableAggregateChange<T> change = changeCreator.apply(aggregateRoot);
|
||||
AggregateChangeWithRoot<T> change = changeCreator.apply(aggregateRoot);
|
||||
|
||||
aggregateRoot = triggerBeforeSave(change.getEntity(), change);
|
||||
aggregateRoot = triggerBeforeSave(change.getRoot(), change);
|
||||
|
||||
change.setEntity(aggregateRoot);
|
||||
change.setRoot(aggregateRoot);
|
||||
|
||||
T entityAfterExecution = executor.execute(change);
|
||||
|
||||
@@ -313,25 +308,24 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
MutableAggregateChange<T> change = createDeletingChange(id, entity, domainType);
|
||||
|
||||
entity = triggerBeforeDelete(entity, id, change);
|
||||
change.setEntity(entity);
|
||||
|
||||
executor.execute(change);
|
||||
|
||||
triggerAfterDelete(entity, id, change);
|
||||
}
|
||||
|
||||
private <T> MutableAggregateChange<T> createInsertChange(T instance) {
|
||||
private <T> AggregateChangeWithRoot<T> createInsertChange(T instance) {
|
||||
|
||||
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(instance);
|
||||
jdbcEntityInsertWriter.write(instance, aggregateChange);
|
||||
AggregateChangeWithRoot<T> aggregateChange = MutableAggregateChange.forSave(instance);
|
||||
new RelationalEntityInsertWriter<T>(context).write(instance, aggregateChange);
|
||||
return aggregateChange;
|
||||
}
|
||||
|
||||
private <T> MutableAggregateChange<T> createUpdateChange(EntityAndPreviousVersion<T> entityAndVersion) {
|
||||
private <T> AggregateChangeWithRoot<T> createUpdateChange(EntityAndPreviousVersion<T> entityAndVersion) {
|
||||
|
||||
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity,
|
||||
AggregateChangeWithRoot<T> aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity,
|
||||
entityAndVersion.version);
|
||||
jdbcEntityUpdateWriter.write(entityAndVersion.entity, aggregateChange);
|
||||
new RelationalEntityUpdateWriter<T>(context).write(entityAndVersion.entity, aggregateChange);
|
||||
return aggregateChange;
|
||||
}
|
||||
|
||||
@@ -383,14 +377,14 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
previousVersion = RelationalEntityVersionUtils.getVersionNumberFromEntity(entity, persistentEntity, converter);
|
||||
}
|
||||
}
|
||||
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forDelete(domainType, entity, previousVersion);
|
||||
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forDelete(domainType, previousVersion);
|
||||
jdbcEntityDeleteWriter.write(id, aggregateChange);
|
||||
return aggregateChange;
|
||||
}
|
||||
|
||||
private MutableAggregateChange<?> createDeletingChange(Class<?> domainType) {
|
||||
|
||||
MutableAggregateChange<?> aggregateChange = MutableAggregateChange.forDelete(domainType, null);
|
||||
MutableAggregateChange<?> aggregateChange = MutableAggregateChange.forDelete(domainType);
|
||||
jdbcEntityDeleteWriter.write(null, aggregateChange);
|
||||
return aggregateChange;
|
||||
}
|
||||
|
||||
@@ -39,6 +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.DbAction;
|
||||
import org.springframework.data.relational.core.conversion.IdValueSource;
|
||||
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
|
||||
@@ -70,7 +71,7 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
|
||||
RelationalMappingContext context = new RelationalMappingContext();
|
||||
JdbcConverter converter = mock(JdbcConverter.class);
|
||||
DbAction.WithEntity<?> rootInsert = new DbAction.InsertRoot<>(entity, IdValueSource.GENERATED);
|
||||
DbAction.WithRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(entity, IdValueSource.GENERATED);
|
||||
|
||||
DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class);
|
||||
|
||||
@@ -79,8 +80,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
@Test // DATAJDBC-291
|
||||
public void singleRoot() {
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
|
||||
@@ -92,8 +93,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
|
||||
entity = entity.withSingle(content);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("single", content, null));
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
@@ -110,8 +111,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
|
||||
entity = entity.withContentList(asList(content, content2));
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("contentList", content, 0));
|
||||
aggregateChange.addAction(createInsert("contentList", content2, 1));
|
||||
|
||||
@@ -129,8 +130,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
|
||||
entity = entity.withContentMap(createContentMap("a", content, "b", content2));
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("contentMap", content, "a"));
|
||||
aggregateChange.addAction(createInsert("contentMap", content2, "b"));
|
||||
|
||||
@@ -149,8 +150,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> parentInsert = createInsert("single", content, null);
|
||||
DbAction.Insert<?> insert = createDeepInsert("single", tag1, null, parentInsert);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert);
|
||||
|
||||
@@ -171,8 +172,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("tagList", tag1, 0, parentInsert);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagList", tag2, 1, parentInsert);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
@@ -197,8 +198,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("tagSet", tag1, null, parentInsert);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagSet", tag2, null, parentInsert);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
@@ -230,8 +231,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
aggregateChange.addAction(insert1);
|
||||
@@ -261,8 +262,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagList", tag2, 0, parentInsert2);
|
||||
DbAction.Insert<?> insert3 = createDeepInsert("tagList", tag3, 1, parentInsert2);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
aggregateChange.addAction(insert1);
|
||||
@@ -296,8 +297,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagMap", tag2, "222", parentInsert2);
|
||||
DbAction.Insert<?> insert3 = createDeepInsert("tagMap", tag3, "333", parentInsert2);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
aggregateChange.addAction(insert1);
|
||||
@@ -335,8 +336,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
aggregateChange.addAction(insert1);
|
||||
@@ -361,8 +362,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
|
||||
|
||||
DbAction.Insert<?> parentInsert = createInsert("embedded.single", tag1, null);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
|
||||
entity = executor.execute(aggregateChange);
|
||||
|
||||
@@ -36,6 +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.DbAction;
|
||||
import org.springframework.data.relational.core.conversion.IdValueSource;
|
||||
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
|
||||
@@ -63,15 +64,15 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
JdbcConverter converter = new BasicJdbcConverter(context, (identifier, path) -> {
|
||||
throw new UnsupportedOperationException();
|
||||
});
|
||||
DbAction.WithEntity<?> rootInsert = new DbAction.InsertRoot<>(entity, IdValueSource.GENERATED);
|
||||
DbAction.WithRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(entity, IdValueSource.GENERATED);
|
||||
DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class, new IncrementingIds());
|
||||
AggregateChangeExecutor executor = new AggregateChangeExecutor(converter, accessStrategy);
|
||||
|
||||
@Test // DATAJDBC-291
|
||||
public void singleRoot() {
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
|
||||
@@ -83,8 +84,8 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
|
||||
entity.single = content;
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("single", content, null));
|
||||
|
||||
executor.execute(aggregateChange);
|
||||
@@ -102,8 +103,8 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
entity.contentList.add(content);
|
||||
entity.contentList.add(content2);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("contentList", content, 0));
|
||||
aggregateChange.addAction(createInsert("contentList", content2, 1));
|
||||
|
||||
@@ -122,8 +123,8 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
entity.contentMap.put("a", content);
|
||||
entity.contentMap.put("b", content2);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(createInsert("contentMap", content, "a"));
|
||||
aggregateChange.addAction(createInsert("contentMap", content2, "b"));
|
||||
|
||||
@@ -142,8 +143,8 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> parentInsert = createInsert("single", content, null);
|
||||
DbAction.Insert<?> insert = createDeepInsert("single", tag1, null, parentInsert);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert);
|
||||
|
||||
@@ -165,8 +166,8 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("tagList", tag1, 0, parentInsert);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagList", tag2, 1, parentInsert);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
@@ -192,8 +193,8 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("tagSet", tag1, null, parentInsert);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagSet", tag2, null, parentInsert);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert);
|
||||
aggregateChange.addAction(insert1);
|
||||
aggregateChange.addAction(insert2);
|
||||
@@ -226,8 +227,8 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
aggregateChange.addAction(insert1);
|
||||
@@ -259,8 +260,8 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagList", tag2, 0, parentInsert2);
|
||||
DbAction.Insert<?> insert3 = createDeepInsert("tagList", tag3, 1, parentInsert2);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
aggregateChange.addAction(insert1);
|
||||
@@ -297,8 +298,8 @@ public class AggregateChangeIdGenerationUnitTests {
|
||||
DbAction.Insert<?> insert2 = createDeepInsert("tagMap", tag2, "222", parentInsert2);
|
||||
DbAction.Insert<?> insert3 = createDeepInsert("tagMap", tag3, "333", parentInsert2);
|
||||
|
||||
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.addAction(rootInsert);
|
||||
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
aggregateChange.setRootAction(rootInsert);
|
||||
aggregateChange.addAction(parentInsert1);
|
||||
aggregateChange.addAction(parentInsert2);
|
||||
aggregateChange.addAction(insert1);
|
||||
|
||||
@@ -18,11 +18,9 @@ package org.springframework.data.jdbc.core;
|
||||
import static java.util.Collections.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.assertj.core.api.SoftAssertions;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -38,10 +36,11 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.junit4.rules.SpringClassRule;
|
||||
import org.springframework.test.context.junit4.rules.SpringMethodRule;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link JdbcAggregateTemplate} and it's handling of immutable entities.
|
||||
*
|
||||
@@ -238,6 +237,26 @@ public class ImmutableAggregateTemplateHsqlIntegrationTests {
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // GH-1201
|
||||
void replaceReferencedEntity_saveResult() {
|
||||
|
||||
Root root = new Root(null, "originalRoot", new NonRoot(null, "originalNonRoot"));
|
||||
Root originalSavedRoot = template.save(root);
|
||||
|
||||
assertThat(originalSavedRoot.id).isNotNull();
|
||||
assertThat(originalSavedRoot.name).isEqualTo("originalRoot");
|
||||
assertThat(originalSavedRoot.reference.id).isNotNull();
|
||||
assertThat(originalSavedRoot.reference.name).isEqualTo("originalNonRoot");
|
||||
|
||||
Root updatedRoot = new Root(originalSavedRoot.id, "updatedRoot", new NonRoot(null, "updatedNonRoot"));
|
||||
Root updatedSavedRoot = template.save(updatedRoot);
|
||||
|
||||
assertThat(updatedSavedRoot.id).isNotNull();
|
||||
assertThat(updatedSavedRoot.name).isEqualTo("updatedRoot");
|
||||
assertThat(updatedSavedRoot.reference.id).isNotNull().isNotEqualTo(originalSavedRoot.reference.id);
|
||||
assertThat(updatedSavedRoot.reference.name).isEqualTo("updatedNonRoot");
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-241
|
||||
public void changeReferencedEntity() {
|
||||
|
||||
@@ -306,6 +325,21 @@ public class ImmutableAggregateTemplateHsqlIntegrationTests {
|
||||
String name;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
static class Root {
|
||||
@Id private Long id;
|
||||
private String name;
|
||||
private NonRoot reference;
|
||||
}
|
||||
|
||||
@Value
|
||||
@With
|
||||
static class NonRoot {
|
||||
@Id Long id;
|
||||
String name;
|
||||
}
|
||||
|
||||
static class WithCopyConstructor {
|
||||
@Id
|
||||
private final Long id;
|
||||
|
||||
@@ -55,14 +55,6 @@ public class JdbcAggregateChangeExecutorContextImmutableUnitTests {
|
||||
|
||||
DummyEntity root = new DummyEntity();
|
||||
|
||||
@Test // DATAJDBC-453
|
||||
public void rootOfEmptySetOfActionsisNull() {
|
||||
|
||||
Object root = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(root).isNull();
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-453
|
||||
public void afterInsertRootIdMaybeUpdated() {
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Value;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
@@ -61,14 +62,6 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
|
||||
DummyEntity root = new DummyEntity();
|
||||
|
||||
@Test // DATAJDBC-453
|
||||
public void rootOfEmptySetOfActionIsNull() {
|
||||
|
||||
Object root = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(root).isNull();
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-453
|
||||
public void afterInsertRootIdMaybeUpdated() {
|
||||
|
||||
@@ -78,7 +71,7 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isNull();
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
assertThat(root.id).isEqualTo(23L);
|
||||
}
|
||||
|
||||
@@ -96,7 +89,7 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isNull();
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
assertThat(root.id).isEqualTo(23L);
|
||||
|
||||
assertThat(content.id).isEqualTo(24L);
|
||||
@@ -117,7 +110,7 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isNull();
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
assertThat(root.id).isEqualTo(23L);
|
||||
|
||||
assertThat(content.id).isEqualTo(24L);
|
||||
@@ -142,7 +135,7 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isNull();
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
assertThat(root.id).isEqualTo(123L);
|
||||
assertThat(content.id).isEqualTo(456L);
|
||||
}
|
||||
@@ -166,11 +159,30 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
|
||||
assertThat(newRoot).isNull();
|
||||
assertThat(newRoot).isEqualTo(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);
|
||||
|
||||
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));
|
||||
|
||||
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
|
||||
assertThat(newRoot).isEqualTo(root);
|
||||
assertThat(root.id).isEqualTo(123L);
|
||||
assertThat(root.contentImmutableId.id).isEqualTo(456L);
|
||||
}
|
||||
|
||||
DbAction.Insert<?> createInsert(DbAction.WithEntity<?> parent, String propertyName, Object value,
|
||||
@Nullable Object key) {
|
||||
|
||||
@@ -205,6 +217,8 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
|
||||
Content content;
|
||||
|
||||
ContentImmutableId contentImmutableId;
|
||||
|
||||
List<Content> list = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -212,4 +226,9 @@ public class JdbcAggregateChangeExecutorContextUnitTests {
|
||||
@Id Long id;
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class ContentImmutableId {
|
||||
@Id Long id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.relational.core.mapping.NamingStrategy;
|
||||
import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback;
|
||||
import org.springframework.data.repository.ListCrudRepository;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
||||
/**
|
||||
* Integration tests for the {@link BeforeSaveCallback}.
|
||||
*
|
||||
* @author Chirag Tailor
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ActiveProfiles("hsql")
|
||||
public class JdbcRepositoryBeforeSaveHsqlIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import(TestConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@Autowired JdbcRepositoryFactory factory;
|
||||
|
||||
@Bean
|
||||
Class<?> testClass() {
|
||||
return JdbcRepositoryBeforeSaveHsqlIntegrationTests.class;
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired NamedParameterJdbcTemplate template;
|
||||
@Autowired ImmutableEntityRepository immutableWithManualIdEntityRepository;
|
||||
@Autowired MutableEntityRepository mutableEntityRepository;
|
||||
@Autowired MutableWithImmutableIdEntityRepository mutableWithImmutableIdEntityRepository;
|
||||
@Autowired ImmutableWithMutableIdEntityRepository immutableWithMutableIdEntityRepository;
|
||||
|
||||
@Test // GH-1199
|
||||
public void immutableEntity() {
|
||||
|
||||
ImmutableEntity entity = new ImmutableEntity(null, "immutable");
|
||||
ImmutableEntity saved = immutableWithManualIdEntityRepository.save(entity);
|
||||
|
||||
assertThat(saved.getId()).isNotNull();
|
||||
assertThat(saved.getName()).isEqualTo("fromBeforeSaveCallback");
|
||||
|
||||
List<ImmutableEntity> entities = immutableWithManualIdEntityRepository.findAll();
|
||||
assertThat(entities).hasSize(1);
|
||||
ImmutableEntity reloaded = entities.get(0);
|
||||
assertThat(reloaded.getId()).isNotNull();
|
||||
assertThat(reloaded.getName()).isEqualTo("fromBeforeSaveCallback");
|
||||
}
|
||||
|
||||
@Test // GH-1199
|
||||
public void mutableEntity() {
|
||||
|
||||
MutableEntity entity = new MutableEntity(null, "immutable");
|
||||
MutableEntity saved = mutableEntityRepository.save(entity);
|
||||
|
||||
assertThat(saved.getId()).isNotNull();
|
||||
assertThat(saved.getName()).isEqualTo("fromBeforeSaveCallback");
|
||||
|
||||
List<MutableEntity> entities = mutableEntityRepository.findAll();
|
||||
assertThat(entities).hasSize(1);
|
||||
MutableEntity reloaded = entities.get(0);
|
||||
assertThat(reloaded.getId()).isNotNull();
|
||||
assertThat(reloaded.getName()).isEqualTo("fromBeforeSaveCallback");
|
||||
}
|
||||
|
||||
@Test // GH-1199
|
||||
public void mutableWithImmutableIdEntity() {
|
||||
|
||||
MutableWithImmutableIdEntity entity = new MutableWithImmutableIdEntity(null, "immutable");
|
||||
MutableWithImmutableIdEntity saved = mutableWithImmutableIdEntityRepository.save(entity);
|
||||
|
||||
assertThat(saved.getId()).isNotNull();
|
||||
assertThat(saved.getName()).isEqualTo("fromBeforeSaveCallback");
|
||||
|
||||
List<MutableWithImmutableIdEntity> entities = mutableWithImmutableIdEntityRepository.findAll();
|
||||
assertThat(entities).hasSize(1);
|
||||
MutableWithImmutableIdEntity reloaded = entities.get(0);
|
||||
assertThat(reloaded.getId()).isNotNull();
|
||||
assertThat(reloaded.getName()).isEqualTo("fromBeforeSaveCallback");
|
||||
}
|
||||
|
||||
@Test // GH-1199
|
||||
public void immutableWithMutableIdEntity() {
|
||||
|
||||
ImmutableWithMutableIdEntity entity = new ImmutableWithMutableIdEntity(null, "immutable");
|
||||
ImmutableWithMutableIdEntity saved = immutableWithMutableIdEntityRepository.save(entity);
|
||||
|
||||
assertThat(saved.getId()).isNotNull();
|
||||
assertThat(saved.getName()).isEqualTo("fromBeforeSaveCallback");
|
||||
|
||||
List<ImmutableWithMutableIdEntity> entities = immutableWithMutableIdEntityRepository.findAll();
|
||||
assertThat(entities).hasSize(1);
|
||||
ImmutableWithMutableIdEntity reloaded = entities.get(0);
|
||||
assertThat(reloaded.getId()).isNotNull();
|
||||
assertThat(reloaded.getName()).isEqualTo("fromBeforeSaveCallback");
|
||||
}
|
||||
|
||||
private interface ImmutableEntityRepository extends ListCrudRepository<ImmutableEntity, Long> {}
|
||||
|
||||
@Value
|
||||
@With
|
||||
static class ImmutableEntity {
|
||||
@Id Long id;
|
||||
String name;
|
||||
}
|
||||
|
||||
private interface MutableEntityRepository extends ListCrudRepository<MutableEntity, Long> {}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
static class MutableEntity {
|
||||
@Id private Long id;
|
||||
private String name;
|
||||
}
|
||||
|
||||
private interface MutableWithImmutableIdEntityRepository extends ListCrudRepository<MutableWithImmutableIdEntity, Long> {}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
static class MutableWithImmutableIdEntity {
|
||||
@Id private final Long id;
|
||||
private String name;
|
||||
}
|
||||
|
||||
private interface ImmutableWithMutableIdEntityRepository extends ListCrudRepository<ImmutableWithMutableIdEntity, Long> {}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
static class ImmutableWithMutableIdEntity {
|
||||
@Id private Long id;
|
||||
@With
|
||||
private final String name;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan("org.springframework.data.jdbc.testing")
|
||||
@EnableJdbcRepositories(considerNestedRepositories = true,
|
||||
includeFilters = @ComponentScan.Filter(value = ListCrudRepository.class, type = FilterType.ASSIGNABLE_TYPE))
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
Class<?> testClass() {
|
||||
return JdbcRepositoryBeforeSaveHsqlIntegrationTests.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link NamingStrategy} that harmlessly uppercases the table name, demonstrating how to inject one while not
|
||||
* breaking existing SQL operations.
|
||||
*/
|
||||
@Bean
|
||||
NamingStrategy namingStrategy() {
|
||||
|
||||
return new NamingStrategy() {
|
||||
|
||||
@Override
|
||||
public String getTableName(Class<?> type) {
|
||||
return type.getSimpleName().toUpperCase();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeforeSaveCallback<ImmutableEntity> nameSetterImmutable() {
|
||||
return (aggregate, aggregateChange) -> aggregate.withName("fromBeforeSaveCallback");
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeforeSaveCallback<MutableEntity> nameSetterMutable() {
|
||||
return (aggregate, aggregateChange) -> {
|
||||
aggregate.setName("fromBeforeSaveCallback");
|
||||
return aggregate;
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeforeSaveCallback<MutableWithImmutableIdEntity> nameSetterMutableWithImmutableId() {
|
||||
return (aggregate, aggregateChange) -> {
|
||||
aggregate.setName("fromBeforeSaveCallback");
|
||||
return aggregate;
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeforeSaveCallback<ImmutableWithMutableIdEntity> nameSetterImmutableWithMutableId() {
|
||||
return (aggregate, aggregateChange) -> aggregate.withName("fromBeforeSaveCallback");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,3 +29,16 @@ CREATE TABLE WITH_COPY_CONSTRUCTOR
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
|
||||
NAME VARCHAR(30)
|
||||
);
|
||||
|
||||
CREATE TABLE ROOT
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
|
||||
NAME VARCHAR(30)
|
||||
);
|
||||
CREATE TABLE NON_ROOT
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
|
||||
ROOT BIGINT NOT NULL,
|
||||
NAME VARCHAR(30)
|
||||
);
|
||||
ALTER TABLE NON_ROOT ADD FOREIGN KEY (ROOT) REFERENCES ROOT (ID);
|
||||
@@ -0,0 +1,22 @@
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
|
||||
CREATE TABLE ImmutableEntity
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
CREATE TABLE MutableEntity
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
CREATE TABLE MutableWithImmutableIdEntity
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
CREATE TABLE ImmutableWithMutableIdEntity
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
|
||||
NAME VARCHAR(100)
|
||||
);
|
||||
@@ -9,7 +9,7 @@
|
||||
<arg column="name" javaType="String"/>
|
||||
</constructor>
|
||||
</resultMap>
|
||||
<insert id="insert" parameterType="MyBatisContext" useGeneratedKeys="true" keyProperty="instance.id" keyColumn="ID">
|
||||
<insert id="insert" parameterType="MyBatisContext" useGeneratedKeys="true" keyProperty="id" keyColumn="ID">
|
||||
INSERT INTO DummyEntity (id) VALUES (DEFAULT)
|
||||
</insert>
|
||||
<select id="findById" resultType="MyBatisContext" resultMap="dummyEntityMap">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<arg column="name" javaType="String"/>
|
||||
</constructor>
|
||||
</resultMap>
|
||||
<insert id="insert" parameterType="MyBatisContext" useGeneratedKeys="true" keyProperty="instance.id" keyColumn="ID">
|
||||
<insert id="insert" parameterType="MyBatisContext" useGeneratedKeys="true" keyProperty="id" keyColumn="ID">
|
||||
INSERT INTO DummyEntity (id) VALUES (DEFAULT)
|
||||
</insert>
|
||||
<select id="findById" resultType="MyBatisContext" resultMap="dummyEntityMap">
|
||||
|
||||
@@ -17,13 +17,12 @@ package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Represents the change happening to the aggregate (as used in the context of Domain Driven Design) as a whole.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Mark Paluch
|
||||
* @author Chirag Tailor
|
||||
*/
|
||||
public interface AggregateChange<T> {
|
||||
|
||||
@@ -41,14 +40,6 @@ public interface AggregateChange<T> {
|
||||
*/
|
||||
Class<T> getEntityType();
|
||||
|
||||
/**
|
||||
* The entity to which this {@link AggregateChange} relates.
|
||||
*
|
||||
* @return may be {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
T getEntity();
|
||||
|
||||
/**
|
||||
* Applies the given consumer to each {@link DbAction} in this {@code AggregateChange}.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
/**
|
||||
* Represents the change happening to the aggregate (as used in the context of Domain Driven Design) as a whole.
|
||||
*
|
||||
* @author Chirag Tailor
|
||||
* @since 2.6
|
||||
*/
|
||||
public interface AggregateChangeWithRoot<T> extends MutableAggregateChange<T> {
|
||||
|
||||
/**
|
||||
* The root object to which this {@link AggregateChange} relates. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
T getRoot();
|
||||
|
||||
/**
|
||||
* Set the root object of the {@code AggregateChange}.
|
||||
*
|
||||
* @param aggregateRoot must not be {@literal null}.
|
||||
*/
|
||||
void setRoot(T aggregateRoot);
|
||||
|
||||
/**
|
||||
* Sets the action for the root object of this {@code AggregateChange}.
|
||||
*
|
||||
* @param action must not be {@literal null}.
|
||||
*/
|
||||
void setRootAction(DbAction.WithRoot<T> action);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public interface DbAction<T> {
|
||||
*
|
||||
* @param <T> type of the entity for which this represents a database interaction.
|
||||
*/
|
||||
class Insert<T> implements WithGeneratedId<T>, WithDependingOn<T> {
|
||||
class Insert<T> implements WithDependingOn<T> {
|
||||
|
||||
private final T entity;
|
||||
private final PersistentPropertyPath<RelationalPersistentProperty> propertyPath;
|
||||
@@ -104,9 +104,9 @@ public interface DbAction<T> {
|
||||
*
|
||||
* @param <T> type of the entity for which this represents a database interaction.
|
||||
*/
|
||||
class InsertRoot<T> implements WithGeneratedId<T> {
|
||||
class InsertRoot<T> implements WithRoot<T> {
|
||||
|
||||
private final T entity;
|
||||
private T entity;
|
||||
private final IdValueSource idValueSource;
|
||||
|
||||
public InsertRoot(T entity, IdValueSource idValueSource) {
|
||||
@@ -119,6 +119,11 @@ public interface DbAction<T> {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntity(T entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
public IdValueSource getIdValueSource() {
|
||||
return idValueSource;
|
||||
}
|
||||
@@ -129,42 +134,14 @@ public interface DbAction<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an update statement for a single entity that is not the root of an aggregate.
|
||||
*
|
||||
* @param <T> type of the entity for which this represents a database interaction.
|
||||
*/
|
||||
final class Update<T> implements WithEntity<T> {
|
||||
|
||||
private final T entity;
|
||||
private final PersistentPropertyPath<RelationalPersistentProperty> propertyPath;
|
||||
|
||||
public Update(T entity, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
|
||||
this.entity = entity;
|
||||
this.propertyPath = propertyPath;
|
||||
}
|
||||
|
||||
public T getEntity() {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
public PersistentPropertyPath<RelationalPersistentProperty> getPropertyPath() {
|
||||
return this.propertyPath;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "DbAction.Update(entity=" + this.getEntity() + ", propertyPath=" + this.getPropertyPath() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an update statement for the aggregate root.
|
||||
*
|
||||
* @param <T> type of the entity for which this represents a database interaction.
|
||||
*/
|
||||
class UpdateRoot<T> implements WithEntity<T> {
|
||||
class UpdateRoot<T> implements WithRoot<T> {
|
||||
|
||||
private final T entity;
|
||||
private T entity;
|
||||
|
||||
@Nullable private final Number previousVersion;
|
||||
|
||||
@@ -177,6 +154,16 @@ public interface DbAction<T> {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntity(T entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdValueSource getIdValueSource() {
|
||||
return IdValueSource.PROVIDED;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Number getPreviousVersion() {
|
||||
return previousVersion;
|
||||
@@ -448,6 +435,7 @@ public interface DbAction<T> {
|
||||
* A {@link DbAction} that stores the information of a single entity in the database.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Chirag Tailor
|
||||
*/
|
||||
interface WithEntity<T> extends DbAction<T> {
|
||||
|
||||
@@ -461,21 +449,23 @@ public interface DbAction<T> {
|
||||
default Class<T> getEntityType() {
|
||||
return (Class<T>) getEntity().getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link IdValueSource} for the entity to persist. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
IdValueSource getIdValueSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link DbAction} that may "update" its entity. In order to support immutable entities this requires at least
|
||||
* potentially creating a new instance, which this interface makes available.
|
||||
* A {@link DbAction} pertaining to the root on an aggregate.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Chirag Tailor
|
||||
*/
|
||||
interface WithGeneratedId<T> extends WithEntity<T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
default Class<T> getEntityType() {
|
||||
return (Class<T>) getEntity().getClass();
|
||||
}
|
||||
interface WithRoot<T> extends WithEntity<T> {
|
||||
/**
|
||||
* @param entity the entity to persist. Must not be {@code null}.
|
||||
*/
|
||||
void setEntity(T entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,31 +19,38 @@ import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
* @author Chirag Tailor
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DbActionExecutionResult {
|
||||
|
||||
private final Object id;
|
||||
private final DbAction<?> action;
|
||||
private final Object generatedId;
|
||||
private final DbAction.WithEntity<?> action;
|
||||
|
||||
public DbActionExecutionResult(DbAction<?> action, @Nullable Object newId) {
|
||||
public DbActionExecutionResult(DbAction.WithEntity<?> action) {
|
||||
|
||||
this.action = action;
|
||||
this.id = newId;
|
||||
this.generatedId = null;
|
||||
}
|
||||
|
||||
public DbActionExecutionResult(DbAction.WithEntity<?> action, @Nullable Object generatedId) {
|
||||
|
||||
this.action = action;
|
||||
this.generatedId = generatedId;
|
||||
}
|
||||
|
||||
public DbActionExecutionResult() {
|
||||
|
||||
action = null;
|
||||
id = null;
|
||||
generatedId = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object getId() {
|
||||
return id;
|
||||
public Object getGeneratedId() {
|
||||
return generatedId;
|
||||
}
|
||||
|
||||
public DbAction<?> getAction() {
|
||||
public DbAction.WithEntity<?> getAction() {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,17 +39,13 @@ class DefaultAggregateChange<T> implements MutableAggregateChange<T> {
|
||||
|
||||
private final List<DbAction<?>> actions = new ArrayList<>();
|
||||
|
||||
/** Aggregate root, to which the change applies, if available */
|
||||
private @Nullable T entity;
|
||||
|
||||
/** The previous version assigned to the instance being changed, if available */
|
||||
@Nullable private final Number previousVersion;
|
||||
|
||||
public DefaultAggregateChange(Kind kind, Class<T> entityType, @Nullable T entity, @Nullable Number previousVersion) {
|
||||
public DefaultAggregateChange(Kind kind, Class<T> entityType, @Nullable Number previousVersion) {
|
||||
|
||||
this.kind = kind;
|
||||
this.entityType = entityType;
|
||||
this.entity = entity;
|
||||
this.previousVersion = previousVersion;
|
||||
}
|
||||
|
||||
@@ -76,33 +72,12 @@ class DefaultAggregateChange<T> implements MutableAggregateChange<T> {
|
||||
return this.entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the root object of the {@code AggregateChange}.
|
||||
*
|
||||
* @param aggregateRoot may be {@literal null} if the change refers to a list of aggregates or references it by id.
|
||||
*/
|
||||
@Override
|
||||
public void setEntity(@Nullable T aggregateRoot) {
|
||||
|
||||
if (aggregateRoot != null) {
|
||||
Assert.isInstanceOf(this.entityType, aggregateRoot,
|
||||
String.format("AggregateRoot must be of type %s", entityType.getName()));
|
||||
}
|
||||
|
||||
this.entity = aggregateRoot;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Number getPreviousVersion() {
|
||||
return previousVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getEntity() {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachAction(Consumer<? super DbAction<?>> consumer) {
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Represents the change happening to the aggregate (as used in the context of Domain Driven Design) as a whole.
|
||||
*
|
||||
* @author Chirag Tailor
|
||||
* @since 2.6
|
||||
*/
|
||||
class DefaultAggregateChangeWithRoot<T> implements AggregateChangeWithRoot<T> {
|
||||
|
||||
private final Kind kind;
|
||||
|
||||
/** Type of the aggregate root to be changed */
|
||||
private final Class<T> entityType;
|
||||
|
||||
private final List<DbAction<?>> actions = new ArrayList<>();
|
||||
|
||||
private DbAction.WithRoot<T> rootAction;
|
||||
|
||||
/** The previous version assigned to the instance being changed, if available */
|
||||
@Nullable private final Number previousVersion;
|
||||
|
||||
public DefaultAggregateChangeWithRoot(Kind kind, Class<T> entityType, @Nullable Number previousVersion) {
|
||||
|
||||
this.kind = kind;
|
||||
this.entityType = entityType;
|
||||
this.previousVersion = previousVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action to this {@code AggregateChange}.
|
||||
*
|
||||
* @param action must not be {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
public void addAction(DbAction<?> action) {
|
||||
|
||||
Assert.notNull(action, "Action must not be null.");
|
||||
|
||||
actions.add(action);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.relational.core.conversion.AggregateChange#getKind()
|
||||
*/
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return this.kind;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.relational.core.conversion.AggregateChange#getEntityType()
|
||||
*/
|
||||
@Override
|
||||
public Class<T> getEntityType() {
|
||||
return this.entityType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRoot(T aggregateRoot) {
|
||||
|
||||
Assert.isInstanceOf(this.entityType, aggregateRoot,
|
||||
String.format("AggregateRoot must be of type %s", entityType.getName()));
|
||||
|
||||
rootAction.setEntity(aggregateRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRootAction(DbAction.WithRoot<T> action) {
|
||||
|
||||
Assert.isNull(this.rootAction, "The rootAction must be set exactly once");
|
||||
|
||||
this.rootAction = action;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Number getPreviousVersion() {
|
||||
return previousVersion;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.relational.core.conversion.AggregateChange#getEntity()
|
||||
*/
|
||||
@Override
|
||||
public T getRoot() {
|
||||
return this.rootAction.getEntity();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.relational.core.conversion.AggregateChange#forEachAction(java.util.function.Consumer)
|
||||
*/
|
||||
@Override
|
||||
public void forEachAction(Consumer<? super DbAction<?>> consumer) {
|
||||
|
||||
Assert.notNull(consumer, "Consumer must not be null.");
|
||||
Assert.notNull(rootAction, "DbAction.WithRoot must not be null.");
|
||||
|
||||
consumer.accept(rootAction);
|
||||
actions.forEach(consumer);
|
||||
}
|
||||
}
|
||||
@@ -30,32 +30,32 @@ import org.springframework.util.ClassUtils;
|
||||
public interface MutableAggregateChange<T> extends AggregateChange<T> {
|
||||
|
||||
/**
|
||||
* Factory method to create an {@link MutableAggregateChange} for saving entities.
|
||||
* Factory method to create an {@link AggregateChangeWithRoot} for saving entities.
|
||||
*
|
||||
* @param entity aggregate root to save.
|
||||
* @param <T> entity type.
|
||||
* @return the {@link MutableAggregateChange} for saving the root {@code entity}.
|
||||
* @return the {@link AggregateChangeWithRoot} for saving the root {@code entity}.
|
||||
* @since 1.2
|
||||
*/
|
||||
static <T> MutableAggregateChange<T> forSave(T entity) {
|
||||
static <T> AggregateChangeWithRoot<T> forSave(T entity) {
|
||||
return forSave(entity, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create an {@link MutableAggregateChange} for saving entities.
|
||||
* Factory method to create an {@link AggregateChangeWithRoot} for saving entities.
|
||||
*
|
||||
* @param entity aggregate root to save.
|
||||
* @param previousVersion the previous version assigned to the instance being saved. May be {@literal null}.
|
||||
* @param <T> entity type.
|
||||
* @return the {@link MutableAggregateChange} for saving the root {@code entity}.
|
||||
* @return the {@link AggregateChangeWithRoot} for saving the root {@code entity}.
|
||||
* @since 2.4
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> MutableAggregateChange<T> forSave(T entity, @Nullable Number previousVersion) {
|
||||
static <T> AggregateChangeWithRoot<T> forSave(T entity, @Nullable Number previousVersion) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
|
||||
return new DefaultAggregateChange<>(Kind.SAVE, (Class<T>) ClassUtils.getUserClass(entity), entity, previousVersion);
|
||||
return new DefaultAggregateChangeWithRoot<>(Kind.SAVE, (Class<T>) ClassUtils.getUserClass(entity), previousVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,37 +71,35 @@ public interface MutableAggregateChange<T> extends AggregateChange<T> {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
|
||||
return forDelete((Class<T>) ClassUtils.getUserClass(entity), entity);
|
||||
return forDelete((Class<T>) ClassUtils.getUserClass(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create an {@link MutableAggregateChange} for deleting entities.
|
||||
*
|
||||
* @param entityClass aggregate root type.
|
||||
* @param entity aggregate root to delete.
|
||||
* @param <T> entity type.
|
||||
* @return the {@link MutableAggregateChange} for deleting the root {@code entity}.
|
||||
* @since 1.2
|
||||
*/
|
||||
static <T> MutableAggregateChange<T> forDelete(Class<T> entityClass, @Nullable T entity) {
|
||||
return forDelete(entityClass, entity, null);
|
||||
static <T> MutableAggregateChange<T> forDelete(Class<T> entityClass) {
|
||||
return forDelete(entityClass, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create an {@link MutableAggregateChange} for deleting entities.
|
||||
*
|
||||
* @param entityClass aggregate root type.
|
||||
* @param entity aggregate root to delete.
|
||||
* @param previousVersion the previous version assigned to the instance being saved. May be {@literal null}.
|
||||
* @param <T> entity type.
|
||||
* @return the {@link MutableAggregateChange} for deleting the root {@code entity}.
|
||||
* @since 2.4
|
||||
*/
|
||||
static <T> MutableAggregateChange<T> forDelete(Class<T> entityClass, @Nullable T entity, @Nullable Number previousVersion) {
|
||||
static <T> MutableAggregateChange<T> forDelete(Class<T> entityClass, @Nullable Number previousVersion) {
|
||||
|
||||
Assert.notNull(entityClass, "Entity class must not be null");
|
||||
|
||||
return new DefaultAggregateChange<>(Kind.DELETE, entityClass, entity, previousVersion);
|
||||
return new DefaultAggregateChange<>(Kind.DELETE, entityClass, previousVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,13 +109,6 @@ public interface MutableAggregateChange<T> extends AggregateChange<T> {
|
||||
*/
|
||||
void addAction(DbAction<?> action);
|
||||
|
||||
/**
|
||||
* Set the root object of the {@code AggregateChange}.
|
||||
*
|
||||
* @param aggregateRoot may be {@literal null} if the change refers to a list of aggregates or references it by id.
|
||||
*/
|
||||
void setEntity(@Nullable T aggregateRoot);
|
||||
|
||||
@Nullable
|
||||
Number getPreviousVersion();
|
||||
}
|
||||
|
||||
@@ -15,20 +15,19 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.convert.EntityWriter;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
/**
|
||||
* Converts an aggregate represented by its root into an {@link MutableAggregateChange}. Does not perform any isNew
|
||||
* Converts an aggregate represented by its root into an {@link AggregateChangeWithRoot}. Does not perform any isNew
|
||||
* check.
|
||||
*
|
||||
* @author Thomas Lang
|
||||
* @author Jens Schauder
|
||||
* @author Chirag Tailor
|
||||
* @since 1.1
|
||||
*/
|
||||
public class RelationalEntityInsertWriter implements EntityWriter<Object, MutableAggregateChange<?>> {
|
||||
public class RelationalEntityInsertWriter<T> implements EntityWriter<T, AggregateChangeWithRoot<T>> {
|
||||
|
||||
private final RelationalMappingContext context;
|
||||
|
||||
@@ -37,9 +36,7 @@ public class RelationalEntityInsertWriter implements EntityWriter<Object, Mutabl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Object root, MutableAggregateChange<?> aggregateChange) {
|
||||
|
||||
List<DbAction<?>> actions = new WritingContext(context, root, aggregateChange).insert();
|
||||
actions.forEach(aggregateChange::addAction);
|
||||
public void write(T root, AggregateChangeWithRoot<T> aggregateChange) {
|
||||
new WritingContext<>(context, root, aggregateChange).insert();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,20 +15,19 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.convert.EntityWriter;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
/**
|
||||
* Converts an aggregate represented by its root into an {@link MutableAggregateChange}. Does not perform any isNew
|
||||
* Converts an aggregate represented by its root into an {@link AggregateChangeWithRoot}. Does not perform any isNew
|
||||
* check.
|
||||
*
|
||||
* @author Thomas Lang
|
||||
* @author Jens Schauder
|
||||
* @author Chirag Tailor
|
||||
* @since 1.1
|
||||
*/
|
||||
public class RelationalEntityUpdateWriter implements EntityWriter<Object, MutableAggregateChange<?>> {
|
||||
public class RelationalEntityUpdateWriter<T> implements EntityWriter<T, AggregateChangeWithRoot<T>> {
|
||||
|
||||
private final RelationalMappingContext context;
|
||||
|
||||
@@ -37,9 +36,7 @@ public class RelationalEntityUpdateWriter implements EntityWriter<Object, Mutabl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Object root, MutableAggregateChange<?> aggregateChange) {
|
||||
|
||||
List<DbAction<?>> actions = new WritingContext(context, root, aggregateChange).update();
|
||||
actions.forEach(aggregateChange::addAction);
|
||||
public void write(T root, AggregateChangeWithRoot<T> aggregateChange) {
|
||||
new WritingContext<>(context, root, aggregateChange).update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,17 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.convert.EntityWriter;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
/**
|
||||
* Converts an aggregate represented by its root into an {@link MutableAggregateChange}.
|
||||
* Converts an aggregate represented by its root into an {@link AggregateChangeWithRoot}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Mark Paluch
|
||||
* @author Chirag Tailor
|
||||
*/
|
||||
public class RelationalEntityWriter implements EntityWriter<Object, MutableAggregateChange<?>> {
|
||||
public class RelationalEntityWriter<T> implements EntityWriter<T, AggregateChangeWithRoot<T>> {
|
||||
|
||||
private final RelationalMappingContext context;
|
||||
|
||||
@@ -35,9 +34,7 @@ public class RelationalEntityWriter implements EntityWriter<Object, MutableAggre
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Object root, MutableAggregateChange<?> aggregateChange) {
|
||||
|
||||
List<DbAction<?>> actions = new WritingContext(context, root, aggregateChange).save();
|
||||
actions.forEach(aggregateChange::addAction);
|
||||
public void write(T root, AggregateChangeWithRoot<T> aggregateChange) {
|
||||
new WritingContext<>(context, root, aggregateChange).save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,25 +42,25 @@ import org.springframework.util.Assert;
|
||||
* @author Myeonghyeon Lee
|
||||
* @author Chirag Tailor
|
||||
*/
|
||||
class WritingContext {
|
||||
class WritingContext<T> {
|
||||
|
||||
private final RelationalMappingContext context;
|
||||
private final Object root;
|
||||
private final Object entity;
|
||||
private final Class<?> entityType;
|
||||
private final T root;
|
||||
private final Class<T> entityType;
|
||||
private final PersistentPropertyPaths<?, RelationalPersistentProperty> paths;
|
||||
private final Map<PathNode, DbAction<?>> previousActions = new HashMap<>();
|
||||
private final Map<PersistentPropertyPath<RelationalPersistentProperty>, List<PathNode>> nodesCache = new HashMap<>();
|
||||
private final IdValueSource rootIdValueSource;
|
||||
@Nullable private final Number previousVersion;
|
||||
private final AggregateChangeWithRoot<T> aggregateChange;
|
||||
|
||||
WritingContext(RelationalMappingContext context, Object root, MutableAggregateChange<?> aggregateChange) {
|
||||
WritingContext(RelationalMappingContext context, T root, AggregateChangeWithRoot<T> aggregateChange) {
|
||||
|
||||
this.context = context;
|
||||
this.root = root;
|
||||
this.entity = aggregateChange.getEntity();
|
||||
this.entityType = aggregateChange.getEntityType();
|
||||
this.previousVersion = aggregateChange.getPreviousVersion();
|
||||
this.aggregateChange = aggregateChange;
|
||||
this.rootIdValueSource = IdValueSource.forInstance(root,
|
||||
context.getRequiredPersistentEntity(aggregateChange.getEntityType()));
|
||||
this.paths = context.findPersistentPropertyPaths(entityType, (p) -> p.isEntity() && !p.isEmbedded());
|
||||
@@ -69,48 +69,39 @@ class WritingContext {
|
||||
/**
|
||||
* Leaves out the isNew check as defined in #DATAJDBC-282
|
||||
*
|
||||
* @return List of {@link DbAction}s
|
||||
* @see <a href="https://github.com/spring-projects/spring-data-jdbc/issues/507">DAJDBC-282</a>
|
||||
*/
|
||||
List<DbAction<?>> insert() {
|
||||
void insert() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
actions.add(setRootAction(new DbAction.InsertRoot<>(entity, rootIdValueSource)));
|
||||
actions.addAll(insertReferenced());
|
||||
return actions;
|
||||
setRootAction(new DbAction.InsertRoot<>(root, rootIdValueSource));
|
||||
insertReferenced().forEach(aggregateChange::addAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leaves out the isNew check as defined in #DATAJDBC-282 Possible Deadlocks in Execution Order in #DATAJDBC-488
|
||||
*
|
||||
* @return List of {@link DbAction}s
|
||||
* @see <a href="https://github.com/spring-projects/spring-data-jdbc/issues/507">DAJDBC-282</a>
|
||||
* @see <a href="https://github.com/spring-projects/spring-data-jdbc/issues/714">DAJDBC-488</a>
|
||||
*/
|
||||
List<DbAction<?>> update() {
|
||||
void update() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
actions.add(setRootAction(new DbAction.UpdateRoot<>(entity, previousVersion)));
|
||||
actions.addAll(deleteReferenced());
|
||||
actions.addAll(insertReferenced());
|
||||
return actions;
|
||||
setRootAction(new DbAction.UpdateRoot<>(root, previousVersion));
|
||||
deleteReferenced().forEach(aggregateChange::addAction);
|
||||
insertReferenced().forEach(aggregateChange::addAction);
|
||||
}
|
||||
|
||||
List<DbAction<?>> save() {
|
||||
void save() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
if (isNew(root)) {
|
||||
|
||||
actions.add(setRootAction(new DbAction.InsertRoot<>(entity, rootIdValueSource)));
|
||||
actions.addAll(insertReferenced());
|
||||
setRootAction(new DbAction.InsertRoot<>(root, rootIdValueSource));
|
||||
insertReferenced().forEach(aggregateChange::addAction);
|
||||
} else {
|
||||
|
||||
actions.add(setRootAction(new DbAction.UpdateRoot<>(entity, previousVersion)));
|
||||
actions.addAll(deleteReferenced());
|
||||
actions.addAll(insertReferenced());
|
||||
setRootAction(new DbAction.UpdateRoot<>(root, previousVersion));
|
||||
deleteReferenced().forEach(aggregateChange::addAction);
|
||||
insertReferenced().forEach(aggregateChange::addAction);
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private boolean isNew(Object o) {
|
||||
@@ -181,17 +172,16 @@ class WritingContext {
|
||||
|
||||
private DbAction.Delete<?> deleteReferenced(PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
|
||||
Object id = context.getRequiredPersistentEntity(entityType).getIdentifierAccessor(entity).getIdentifier();
|
||||
Object id = context.getRequiredPersistentEntity(entityType).getIdentifierAccessor(root).getIdentifier();
|
||||
|
||||
return new DbAction.Delete<>(id, path);
|
||||
}
|
||||
|
||||
//// methods not directly related to the creation of DbActions
|
||||
|
||||
private DbAction<?> setRootAction(DbAction<?> dbAction) {
|
||||
|
||||
private void setRootAction(DbAction.WithRoot<T> dbAction) {
|
||||
aggregateChange.setRootAction(dbAction);
|
||||
previousActions.put(null, dbAction);
|
||||
return dbAction;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -263,7 +253,7 @@ class WritingContext {
|
||||
private Object getFromRootValue(PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
|
||||
if (path.getLength() == 0) {
|
||||
return entity;
|
||||
return root;
|
||||
}
|
||||
|
||||
Object parent = getFromRootValue(path.getParentPath());
|
||||
|
||||
@@ -50,7 +50,7 @@ public class RelationalEntityDeleteWriterUnitTests {
|
||||
|
||||
SomeEntity entity = new SomeEntity(23L);
|
||||
|
||||
MutableAggregateChange<SomeEntity> aggregateChange = MutableAggregateChange.forDelete(SomeEntity.class, entity);
|
||||
MutableAggregateChange<SomeEntity> aggregateChange = MutableAggregateChange.forDelete(SomeEntity.class);
|
||||
|
||||
converter.write(entity.id, aggregateChange);
|
||||
|
||||
@@ -69,7 +69,7 @@ public class RelationalEntityDeleteWriterUnitTests {
|
||||
|
||||
SingleEntity entity = new SingleEntity(23L);
|
||||
|
||||
MutableAggregateChange<SingleEntity> aggregateChange = MutableAggregateChange.forDelete(SingleEntity.class, entity);
|
||||
MutableAggregateChange<SingleEntity> aggregateChange = MutableAggregateChange.forDelete(SingleEntity.class);
|
||||
|
||||
converter.write(entity.id, aggregateChange);
|
||||
|
||||
@@ -81,7 +81,7 @@ public class RelationalEntityDeleteWriterUnitTests {
|
||||
@Test // DATAJDBC-188
|
||||
public void deleteAllDeletesAllEntitiesAndReferencedEntities() {
|
||||
|
||||
MutableAggregateChange<SomeEntity> aggregateChange = MutableAggregateChange.forDelete(SomeEntity.class, null);
|
||||
MutableAggregateChange<SomeEntity> aggregateChange = MutableAggregateChange.forDelete(SomeEntity.class);
|
||||
|
||||
converter.write(null, aggregateChange);
|
||||
|
||||
@@ -98,7 +98,7 @@ public class RelationalEntityDeleteWriterUnitTests {
|
||||
@Test // DATAJDBC-493
|
||||
public void deleteAllDeletesAllEntitiesAndNoReferencedEntities() {
|
||||
|
||||
MutableAggregateChange<SingleEntity> aggregateChange = MutableAggregateChange.forDelete(SingleEntity.class, null);
|
||||
MutableAggregateChange<SingleEntity> aggregateChange = MutableAggregateChange.forDelete(SingleEntity.class);
|
||||
|
||||
converter.write(null, aggregateChange);
|
||||
|
||||
|
||||
@@ -38,16 +38,15 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
|
||||
public class RelationalEntityInsertWriterUnitTests {
|
||||
|
||||
public static final long SOME_ENTITY_ID = 23L;
|
||||
RelationalEntityInsertWriter converter = new RelationalEntityInsertWriter(new RelationalMappingContext());
|
||||
RelationalMappingContext context = new RelationalMappingContext();
|
||||
|
||||
@Test // DATAJDBC-112
|
||||
public void newEntityGetsConvertedToOneInsert() {
|
||||
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(null);
|
||||
MutableAggregateChange<SingleReferenceEntity> aggregateChange = //
|
||||
MutableAggregateChange.forSave(entity);
|
||||
AggregateChangeWithRoot<SingleReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityInsertWriter<SingleReferenceEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
|
||||
@@ -62,10 +61,9 @@ public class RelationalEntityInsertWriterUnitTests {
|
||||
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(SOME_ENTITY_ID);
|
||||
|
||||
MutableAggregateChange<SingleReferenceEntity> aggregateChange = //
|
||||
MutableAggregateChange.forSave(entity);
|
||||
AggregateChangeWithRoot<SingleReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityInsertWriter<SingleReferenceEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
|
||||
|
||||
@@ -38,16 +38,16 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
|
||||
public class RelationalEntityUpdateWriterUnitTests {
|
||||
|
||||
public static final long SOME_ENTITY_ID = 23L;
|
||||
RelationalEntityUpdateWriter converter = new RelationalEntityUpdateWriter(new RelationalMappingContext());
|
||||
private RelationalMappingContext context = new RelationalMappingContext();
|
||||
|
||||
@Test // DATAJDBC-112
|
||||
public void existingEntityGetsConvertedToDeletePlusUpdate() {
|
||||
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(SOME_ENTITY_ID);
|
||||
|
||||
MutableAggregateChange<SingleReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
AggregateChangeWithRoot<SingleReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityUpdateWriter<SingleReferenceEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
|
||||
|
||||
@@ -60,7 +60,6 @@ public class RelationalEntityWriterUnitTests {
|
||||
|
||||
static final long SOME_ENTITY_ID = 23L;
|
||||
final RelationalMappingContext context = new RelationalMappingContext();
|
||||
final RelationalEntityWriter converter = new RelationalEntityWriter(context);
|
||||
|
||||
final PersistentPropertyPath<RelationalPersistentProperty> listContainerElements = toPath("elements",
|
||||
ListContainer.class, context);
|
||||
@@ -84,10 +83,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
public void newEntityGetsConvertedToOneInsert() {
|
||||
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(null);
|
||||
MutableAggregateChange<SingleReferenceEntity> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, SingleReferenceEntity.class, entity, null);
|
||||
AggregateChangeWithRoot<SingleReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<SingleReferenceEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -106,10 +104,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
void newEntityWithPrimitiveLongId_insertDoesNotIncludeId_whenIdValueIsZero() {
|
||||
PrimitiveLongIdEntity entity = new PrimitiveLongIdEntity();
|
||||
|
||||
MutableAggregateChange<PrimitiveLongIdEntity> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, PrimitiveLongIdEntity.class, entity, null);
|
||||
AggregateChangeWithRoot<PrimitiveLongIdEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<PrimitiveLongIdEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -128,10 +125,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
void newEntityWithPrimitiveIntId_insertDoesNotIncludeId_whenIdValueIsZero() {
|
||||
PrimitiveIntIdEntity entity = new PrimitiveIntIdEntity();
|
||||
|
||||
MutableAggregateChange<PrimitiveIntIdEntity> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, PrimitiveIntIdEntity.class, entity, null);
|
||||
AggregateChangeWithRoot<PrimitiveIntIdEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<PrimitiveIntIdEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -152,10 +148,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
EmbeddedReferenceEntity entity = new EmbeddedReferenceEntity(null);
|
||||
entity.other = new Element(2L);
|
||||
|
||||
MutableAggregateChange<EmbeddedReferenceEntity> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, EmbeddedReferenceEntity.class, entity, null);
|
||||
AggregateChangeWithRoot<EmbeddedReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<EmbeddedReferenceEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -176,10 +171,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(null);
|
||||
entity.other = new Element(null);
|
||||
|
||||
MutableAggregateChange<SingleReferenceEntity> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, SingleReferenceEntity.class, entity, null);
|
||||
AggregateChangeWithRoot<SingleReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<SingleReferenceEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -202,10 +196,10 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.primitiveLongIdEntity = new PrimitiveLongIdEntity();
|
||||
entity.primitiveIntIdEntity = new PrimitiveIntIdEntity();
|
||||
|
||||
MutableAggregateChange<EntityWithReferencesToPrimitiveIdEntity> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, EntityWithReferencesToPrimitiveIdEntity.class, entity, null);
|
||||
AggregateChangeWithRoot<EntityWithReferencesToPrimitiveIdEntity> aggregateChange = MutableAggregateChange
|
||||
.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<EntityWithReferencesToPrimitiveIdEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -229,10 +223,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(SOME_ENTITY_ID);
|
||||
|
||||
MutableAggregateChange<SingleReferenceEntity> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, SingleReferenceEntity.class, entity, 1L);
|
||||
AggregateChangeWithRoot<SingleReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity, 1L);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<SingleReferenceEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -253,10 +246,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(SOME_ENTITY_ID);
|
||||
entity.other = new Element(null);
|
||||
|
||||
MutableAggregateChange<SingleReferenceEntity> aggregateChange = new DefaultAggregateChange<>(
|
||||
AggregateChange.Kind.SAVE, SingleReferenceEntity.class, entity, 1L);
|
||||
AggregateChangeWithRoot<SingleReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity, 1L);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<SingleReferenceEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -276,10 +268,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
public void newEntityWithEmptySetResultsInSingleInsert() {
|
||||
|
||||
SetContainer entity = new SetContainer(null);
|
||||
MutableAggregateChange<SetContainer> aggregateChange = new DefaultAggregateChange<>(AggregateChange.Kind.SAVE,
|
||||
SetContainer.class, entity, null);
|
||||
AggregateChangeWithRoot<SetContainer> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<SetContainer>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -299,9 +290,8 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.elements.add(new Element(null));
|
||||
entity.elements.add(new Element(null));
|
||||
|
||||
MutableAggregateChange<SetContainer> aggregateChange = new DefaultAggregateChange<>(AggregateChange.Kind.SAVE,
|
||||
SetContainer.class, entity, null);
|
||||
converter.write(entity, aggregateChange);
|
||||
AggregateChangeWithRoot<SetContainer> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
new RelationalEntityWriter<SetContainer>(context).write(entity, aggregateChange);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(aggregateChange);
|
||||
assertThat(actions).extracting(DbAction::getClass, //
|
||||
@@ -342,10 +332,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
new Element(null)) //
|
||||
);
|
||||
|
||||
MutableAggregateChange<CascadingReferenceEntity> aggregateChange = new DefaultAggregateChange<>(
|
||||
AggregateChange.Kind.SAVE, CascadingReferenceEntity.class, entity, null);
|
||||
AggregateChangeWithRoot<CascadingReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<CascadingReferenceEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(aggregateChange);
|
||||
assertThat(actions).extracting(DbAction::getClass, //
|
||||
@@ -404,10 +393,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
new Element(null)) //
|
||||
);
|
||||
|
||||
MutableAggregateChange<CascadingReferenceEntity> aggregateChange = new DefaultAggregateChange<>(
|
||||
AggregateChange.Kind.SAVE, CascadingReferenceEntity.class, entity, 1L);
|
||||
AggregateChangeWithRoot<CascadingReferenceEntity> aggregateChange = MutableAggregateChange.forSave(entity, 1L);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<CascadingReferenceEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(aggregateChange);
|
||||
assertThat(actions).extracting(DbAction::getClass, //
|
||||
@@ -456,10 +444,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
public void newEntityWithEmptyMapResultsInSingleInsert() {
|
||||
|
||||
MapContainer entity = new MapContainer(null);
|
||||
MutableAggregateChange<MapContainer> aggregateChange = new DefaultAggregateChange<>(AggregateChange.Kind.SAVE,
|
||||
MapContainer.class, entity, null);
|
||||
AggregateChangeWithRoot<MapContainer> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<MapContainer>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)).extracting(DbAction::getClass, //
|
||||
DbAction::getEntityType, //
|
||||
@@ -476,9 +463,8 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.elements.put("one", new Element(null));
|
||||
entity.elements.put("two", new Element(null));
|
||||
|
||||
MutableAggregateChange<MapContainer> aggregateChange = new DefaultAggregateChange<>(AggregateChange.Kind.SAVE,
|
||||
MapContainer.class, entity, null);
|
||||
converter.write(entity, aggregateChange);
|
||||
AggregateChangeWithRoot<MapContainer> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
new RelationalEntityWriter<MapContainer>(context).write(entity, aggregateChange);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(aggregateChange);
|
||||
assertThat(actions).extracting(DbAction::getClass, //
|
||||
@@ -520,9 +506,8 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.elements.put("a", new Element(null));
|
||||
entity.elements.put("b", new Element(null));
|
||||
|
||||
MutableAggregateChange<MapContainer> aggregateChange = new DefaultAggregateChange<>(AggregateChange.Kind.SAVE,
|
||||
MapContainer.class, entity, null);
|
||||
converter.write(entity, aggregateChange);
|
||||
AggregateChangeWithRoot<MapContainer> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
new RelationalEntityWriter<MapContainer>(context).write(entity, aggregateChange);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(aggregateChange);
|
||||
assertThat(actions).extracting(DbAction::getClass, //
|
||||
@@ -560,10 +545,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
public void newEntityWithEmptyListResultsInSingleInsert() {
|
||||
|
||||
ListContainer entity = new ListContainer(null);
|
||||
MutableAggregateChange<ListContainer> aggregateChange = new DefaultAggregateChange<>(AggregateChange.Kind.SAVE,
|
||||
ListContainer.class, entity, null);
|
||||
AggregateChangeWithRoot<ListContainer> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<ListContainer>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)).extracting(DbAction::getClass, //
|
||||
DbAction::getEntityType, //
|
||||
@@ -580,9 +564,8 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.elements.add(new Element(null));
|
||||
entity.elements.add(new Element(null));
|
||||
|
||||
MutableAggregateChange<ListContainer> aggregateChange = new DefaultAggregateChange<>(AggregateChange.Kind.SAVE,
|
||||
ListContainer.class, entity, null);
|
||||
converter.write(entity, aggregateChange);
|
||||
AggregateChangeWithRoot<ListContainer> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
new RelationalEntityWriter<ListContainer>(context).write(entity, aggregateChange);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(aggregateChange);
|
||||
assertThat(actions).extracting(DbAction::getClass, //
|
||||
@@ -612,10 +595,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
MapContainer entity = new MapContainer(SOME_ENTITY_ID);
|
||||
entity.elements.put("one", new Element(null));
|
||||
|
||||
MutableAggregateChange<MapContainer> aggregateChange = new DefaultAggregateChange<>(AggregateChange.Kind.SAVE,
|
||||
MapContainer.class, entity, 1L);
|
||||
AggregateChangeWithRoot<MapContainer> aggregateChange = MutableAggregateChange.forSave(entity, 1L);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<MapContainer>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -636,10 +618,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
ListContainer entity = new ListContainer(SOME_ENTITY_ID);
|
||||
entity.elements.add(new Element(null));
|
||||
|
||||
MutableAggregateChange<ListContainer> aggregateChange = new DefaultAggregateChange<>(AggregateChange.Kind.SAVE,
|
||||
ListContainer.class, entity, 1L);
|
||||
AggregateChangeWithRoot<ListContainer> aggregateChange = MutableAggregateChange.forSave(entity, 1L);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<ListContainer>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -661,10 +642,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
listMapContainer.maps.add(new MapContainer(SOME_ENTITY_ID));
|
||||
listMapContainer.maps.get(0).elements.put("one", new Element(null));
|
||||
|
||||
MutableAggregateChange<ListMapContainer> aggregateChange = new DefaultAggregateChange<>(AggregateChange.Kind.SAVE,
|
||||
ListMapContainer.class, listMapContainer, 1L);
|
||||
AggregateChangeWithRoot<ListMapContainer> aggregateChange = MutableAggregateChange.forSave(listMapContainer, 1L);
|
||||
|
||||
converter.write(listMapContainer, aggregateChange);
|
||||
new RelationalEntityWriter<ListMapContainer>(context).write(listMapContainer, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -689,10 +669,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
listMapContainer.maps.add(new NoIdMapContainer());
|
||||
listMapContainer.maps.get(0).elements.put("one", new NoIdElement());
|
||||
|
||||
MutableAggregateChange<NoIdListMapContainer> aggregateChange = new DefaultAggregateChange<>(
|
||||
AggregateChange.Kind.SAVE, NoIdListMapContainer.class, listMapContainer, 1L);
|
||||
AggregateChangeWithRoot<NoIdListMapContainer> aggregateChange = MutableAggregateChange.forSave(listMapContainer, 1L);
|
||||
|
||||
converter.write(listMapContainer, aggregateChange);
|
||||
new RelationalEntityWriter<NoIdListMapContainer>(context).write(listMapContainer, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -716,10 +695,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
EmbeddedReferenceChainEntity entity = new EmbeddedReferenceChainEntity(null);
|
||||
// the embedded is null !!!
|
||||
|
||||
MutableAggregateChange<EmbeddedReferenceChainEntity> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, EmbeddedReferenceChainEntity.class, entity, null);
|
||||
AggregateChangeWithRoot<EmbeddedReferenceChainEntity> aggregateChange = MutableAggregateChange.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<EmbeddedReferenceChainEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -741,10 +719,10 @@ public class RelationalEntityWriterUnitTests {
|
||||
root.other = new EmbeddedReferenceChainEntity(null);
|
||||
// the embedded is null !!!
|
||||
|
||||
MutableAggregateChange<RootWithEmbeddedReferenceChainEntity> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, RootWithEmbeddedReferenceChainEntity.class, root, null);
|
||||
AggregateChangeWithRoot<RootWithEmbeddedReferenceChainEntity> aggregateChange = MutableAggregateChange
|
||||
.forSave(root);
|
||||
|
||||
converter.write(root, aggregateChange);
|
||||
new RelationalEntityWriter<RootWithEmbeddedReferenceChainEntity>(context).write(root, aggregateChange);
|
||||
|
||||
assertThat(extractActions(aggregateChange)) //
|
||||
.extracting(DbAction::getClass, //
|
||||
@@ -769,10 +747,9 @@ public class RelationalEntityWriterUnitTests {
|
||||
root.elements.add(new Element(1L));
|
||||
root.elements.add(new Element(null));
|
||||
root.elements.add(new Element(2L));
|
||||
MutableAggregateChange<ListContainer> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, ListContainer.class, root, null);
|
||||
AggregateChangeWithRoot<ListContainer> aggregateChange = MutableAggregateChange.forSave(root);
|
||||
|
||||
converter.write(root, aggregateChange);
|
||||
new RelationalEntityWriter<ListContainer>(context).write(root, aggregateChange);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(aggregateChange);
|
||||
assertThat(actions).extracting(DbAction::getClass, //
|
||||
@@ -817,10 +794,10 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.primitiveLongIdEntities.add(new PrimitiveLongIdEntity());
|
||||
entity.primitiveIntIdEntities.add(new PrimitiveIntIdEntity());
|
||||
|
||||
MutableAggregateChange<EntityWithReferencesToPrimitiveIdEntity> aggregateChange = //
|
||||
new DefaultAggregateChange<>(AggregateChange.Kind.SAVE, EntityWithReferencesToPrimitiveIdEntity.class, entity, null);
|
||||
AggregateChangeWithRoot<EntityWithReferencesToPrimitiveIdEntity> aggregateChange = MutableAggregateChange
|
||||
.forSave(entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
new RelationalEntityWriter<EntityWithReferencesToPrimitiveIdEntity>(context).write(entity, aggregateChange);
|
||||
|
||||
List<DbAction<?>> actions = extractActions(aggregateChange);
|
||||
assertThat(actions).extracting(DbAction::getClass, //
|
||||
|
||||
Reference in New Issue
Block a user