diff --git a/pom.xml b/pom.xml
index dadda28174..4e9cb95441 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-relational-parent
- 3.0.0-SNAPSHOT
+ 3.0.0-1201-update-return-value-SNAPSHOT
pom
Spring Data Relational Parent
diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml
index db3b7ddd1a..12558a13d3 100644
--- a/spring-data-jdbc-distribution/pom.xml
+++ b/spring-data-jdbc-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-relational-parent
- 3.0.0-SNAPSHOT
+ 3.0.0-1201-update-return-value-SNAPSHOT
../pom.xml
diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml
index 547ff62b8b..d5c4ea9525 100644
--- a/spring-data-jdbc/pom.xml
+++ b/spring-data-jdbc/pom.xml
@@ -6,7 +6,7 @@
4.0.0
spring-data-jdbc
- 3.0.0-SNAPSHOT
+ 3.0.0-1201-update-return-value-SNAPSHOT
Spring Data JDBC
Spring Data module for JDBC repositories.
@@ -15,7 +15,7 @@
org.springframework.data
spring-data-relational-parent
- 3.0.0-SNAPSHOT
+ 3.0.0-1201-update-return-value-SNAPSHOT
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/AggregateChangeExecutor.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/AggregateChangeExecutor.java
index 7b016e4179..0f584a0707 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/AggregateChangeExecutor.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/AggregateChangeExecutor.java
@@ -18,10 +18,10 @@
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 execute(AggregateChange aggregateChange) {
+ T execute(AggregateChangeWithRoot 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;
+ void execute(AggregateChange aggregateChange) {
+
+ JdbcAggregateChangeExecutionContext executionContext = new JdbcAggregateChangeExecutionContext(converter,
+ accessStrategy);
+
+ aggregateChange.forEachAction(action -> execute(action, executionContext));
}
private void execute(DbAction> action, JdbcAggregateChangeExecutionContext executionContext) {
@@ -69,8 +71,6 @@ private void execute(DbAction> action, JdbcAggregateChangeExecutionContext exe
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) {
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java
index 7b9368d37e..930b6f89b4 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java
@@ -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.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 @@ void executeUpdateRoot(DbAction.UpdateRoot update) {
} else {
updateWithoutVersion(update);
}
- }
-
- void executeUpdate(DbAction.Update update) {
-
- if (!accessStrategy.update(update.getEntity(), update.getEntityType())) {
-
- throw new IncorrectUpdateSemanticsDataAccessException(
- String.format(UPDATE_FAILED, update.getEntity(), getIdFrom(update)));
- }
+ add(new DbActionExecutionResult(update));
}
void executeDeleteRoot(DbAction.DeleteRoot delete) {
@@ -203,11 +188,12 @@ private DbAction.WithEntity> getIdOwningAction(DbAction.WithEntity> action,
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 @@ private Object getIdFrom(DbAction.WithEntity> idOwningAction) {
return identifier;
}
- @SuppressWarnings("unchecked")
- @Nullable
T populateIdsIfNecessary() {
- T newRoot = null;
-
// have the results so that the inserts on the leaves come first.
List reverseResults = new ArrayList<>(results.values());
Collections.reverse(reverseResults);
@@ -241,33 +223,29 @@ T populateIdsIfNecessary() {
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 (action instanceof DbAction.Insert) {
- DbAction.Insert> insert = (DbAction.Insert>) action;
-
- Pair, ?> qualifier = insert.getQualifier();
+ if (newEntity != action.getEntity() && action instanceof DbAction.Insert) {
+ DbAction.Insert> insert = (DbAction.Insert>) action;
- cascadingValues.stage(insert.getDependingOn(), insert.getPropertyPath(),
- qualifier == null ? null : qualifier.getSecond(), newEntity);
+ Pair, ?> qualifier = insert.getQualifier();
- } 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 Object setIdAndCascadingProperties(DbAction.WithEntity action, @Nullable Object generatedId,
@@ -279,7 +257,7 @@ private Object setIdAndCascadingProperties(DbAction.WithEntity action, @N
.getRequiredPersistentEntity(action.getEntityType());
PersistentPropertyAccessor 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 @@ private PersistentPropertyPath> getRelativePath(DbAction> action, Persistent
return pathToValue;
}
+ if (action instanceof DbAction.UpdateRoot) {
+ return pathToValue;
+ }
+
throw new IllegalArgumentException(String.format("DbAction of type %s is not supported.", action.getClass()));
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java
index f8f7ff6743..24c2de8689 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java
@@ -31,6 +31,7 @@
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 JdbcAggregateTemplate(ApplicationContext publisher, RelationalMappingCont
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 JdbcAggregateTemplate(ApplicationEventPublisher publisher, RelationalMapp
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 T save(T instance) {
RelationalPersistentEntity> persistentEntity = context.getRequiredPersistentEntity(instance.getClass());
- Function> changeCreator = persistentEntity.isNew(instance)
+ Function> changeCreator = persistentEntity.isNew(instance)
? entity -> createInsertChange(prepareVersionForInsert(entity))
: entity -> createUpdateChange(prepareVersionForUpdate(entity));
@@ -286,18 +281,18 @@ public void deleteAll(Class> domainType) {
executor.execute(change);
}
- private T store(T aggregateRoot, Function> changeCreator,
+ private T store(T aggregateRoot, Function> changeCreator,
RelationalPersistentEntity> persistentEntity) {
Assert.notNull(aggregateRoot, "Aggregate instance must not be null!");
aggregateRoot = triggerBeforeConvert(aggregateRoot);
- MutableAggregateChange change = changeCreator.apply(aggregateRoot);
+ AggregateChangeWithRoot 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 @@ private void deleteTree(Object id, @Nullable T entity, Class domainType)
MutableAggregateChange change = createDeletingChange(id, entity, domainType);
entity = triggerBeforeDelete(entity, id, change);
- change.setEntity(entity);
executor.execute(change);
triggerAfterDelete(entity, id, change);
}
- private MutableAggregateChange createInsertChange(T instance) {
+ private AggregateChangeWithRoot createInsertChange(T instance) {
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(instance);
- jdbcEntityInsertWriter.write(instance, aggregateChange);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(instance);
+ new RelationalEntityInsertWriter(context).write(instance, aggregateChange);
return aggregateChange;
}
- private MutableAggregateChange createUpdateChange(EntityAndPreviousVersion entityAndVersion) {
+ private AggregateChangeWithRoot createUpdateChange(EntityAndPreviousVersion entityAndVersion) {
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity,
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity,
entityAndVersion.version);
- jdbcEntityUpdateWriter.write(entityAndVersion.entity, aggregateChange);
+ new RelationalEntityUpdateWriter(context).write(entityAndVersion.entity, aggregateChange);
return aggregateChange;
}
@@ -383,14 +377,14 @@ private MutableAggregateChange createDeletingChange(Object id, @Nullable
previousVersion = RelationalEntityVersionUtils.getVersionNumberFromEntity(entity, persistentEntity, converter);
}
}
- MutableAggregateChange aggregateChange = MutableAggregateChange.forDelete(domainType, entity, previousVersion);
+ MutableAggregateChange 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;
}
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java
index f51b930e2b..3a92cca271 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java
@@ -39,6 +39,7 @@
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 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 aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
entity = executor.execute(aggregateChange);
@@ -92,8 +93,8 @@ public void simpleReference() {
entity = entity.withSingle(content);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(createInsert("single", content, null));
entity = executor.execute(aggregateChange);
@@ -110,8 +111,8 @@ public void listReference() {
entity = entity.withContentList(asList(content, content2));
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(createInsert("contentList", content, 0));
aggregateChange.addAction(createInsert("contentList", content2, 1));
@@ -129,8 +130,8 @@ public void mapReference() {
entity = entity.withContentMap(createContentMap("a", content, "b", content2));
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(createInsert("contentMap", content, "a"));
aggregateChange.addAction(createInsert("contentMap", content2, "b"));
@@ -149,8 +150,8 @@ public void setIdForDeepReference() {
DbAction.Insert> parentInsert = createInsert("single", content, null);
DbAction.Insert> insert = createDeepInsert("single", tag1, null, parentInsert);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert);
aggregateChange.addAction(insert);
@@ -171,8 +172,8 @@ public void setIdForDeepReferenceElementList() {
DbAction.Insert> insert1 = createDeepInsert("tagList", tag1, 0, parentInsert);
DbAction.Insert> insert2 = createDeepInsert("tagList", tag2, 1, parentInsert);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert);
aggregateChange.addAction(insert1);
aggregateChange.addAction(insert2);
@@ -197,8 +198,8 @@ public void setIdForDeepElementSetElementSet() {
DbAction.Insert> insert1 = createDeepInsert("tagSet", tag1, null, parentInsert);
DbAction.Insert> insert2 = createDeepInsert("tagSet", tag2, null, parentInsert);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert);
aggregateChange.addAction(insert1);
aggregateChange.addAction(insert2);
@@ -230,8 +231,8 @@ public void setIdForDeepElementListSingleReference() {
DbAction.Insert> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
DbAction.Insert> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert1);
aggregateChange.addAction(parentInsert2);
aggregateChange.addAction(insert1);
@@ -261,8 +262,8 @@ public void setIdForDeepElementListElementList() {
DbAction.Insert> insert2 = createDeepInsert("tagList", tag2, 0, parentInsert2);
DbAction.Insert> insert3 = createDeepInsert("tagList", tag3, 1, parentInsert2);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert1);
aggregateChange.addAction(parentInsert2);
aggregateChange.addAction(insert1);
@@ -296,8 +297,8 @@ public void setIdForDeepElementMapElementMap() {
DbAction.Insert> insert2 = createDeepInsert("tagMap", tag2, "222", parentInsert2);
DbAction.Insert> insert3 = createDeepInsert("tagMap", tag3, "333", parentInsert2);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert1);
aggregateChange.addAction(parentInsert2);
aggregateChange.addAction(insert1);
@@ -335,8 +336,8 @@ public void setIdForDeepElementListSingleReferenceWithIntermittentNoId() {
DbAction.Insert> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
DbAction.Insert> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert1);
aggregateChange.addAction(parentInsert2);
aggregateChange.addAction(insert1);
@@ -361,8 +362,8 @@ public void setIdForEmbeddedDeepReference() {
DbAction.Insert> parentInsert = createInsert("embedded.single", tag1, null);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert);
entity = executor.execute(aggregateChange);
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java
index ad520ff985..1c7a31e9b0 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java
@@ -36,6 +36,7 @@
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 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 aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
executor.execute(aggregateChange);
@@ -83,8 +84,8 @@ public void simpleReference() {
entity.single = content;
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(createInsert("single", content, null));
executor.execute(aggregateChange);
@@ -102,8 +103,8 @@ public void listReference() {
entity.contentList.add(content);
entity.contentList.add(content2);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(createInsert("contentList", content, 0));
aggregateChange.addAction(createInsert("contentList", content2, 1));
@@ -122,8 +123,8 @@ public void mapReference() {
entity.contentMap.put("a", content);
entity.contentMap.put("b", content2);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(createInsert("contentMap", content, "a"));
aggregateChange.addAction(createInsert("contentMap", content2, "b"));
@@ -142,8 +143,8 @@ public void setIdForDeepReference() {
DbAction.Insert> parentInsert = createInsert("single", content, null);
DbAction.Insert> insert = createDeepInsert("single", tag1, null, parentInsert);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert);
aggregateChange.addAction(insert);
@@ -165,8 +166,8 @@ public void setIdForDeepReferenceElementList() {
DbAction.Insert> insert1 = createDeepInsert("tagList", tag1, 0, parentInsert);
DbAction.Insert> insert2 = createDeepInsert("tagList", tag2, 1, parentInsert);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert);
aggregateChange.addAction(insert1);
aggregateChange.addAction(insert2);
@@ -192,8 +193,8 @@ public void setIdForDeepElementSetElementSet() {
DbAction.Insert> insert1 = createDeepInsert("tagSet", tag1, null, parentInsert);
DbAction.Insert> insert2 = createDeepInsert("tagSet", tag2, null, parentInsert);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert);
aggregateChange.addAction(insert1);
aggregateChange.addAction(insert2);
@@ -226,8 +227,8 @@ public void setIdForDeepElementListSingleReference() {
DbAction.Insert> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
DbAction.Insert> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert1);
aggregateChange.addAction(parentInsert2);
aggregateChange.addAction(insert1);
@@ -259,8 +260,8 @@ public void setIdForDeepElementListElementList() {
DbAction.Insert> insert2 = createDeepInsert("tagList", tag2, 0, parentInsert2);
DbAction.Insert> insert3 = createDeepInsert("tagList", tag3, 1, parentInsert2);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert1);
aggregateChange.addAction(parentInsert2);
aggregateChange.addAction(insert1);
@@ -297,8 +298,8 @@ public void setIdForDeepElementMapElementMap() {
DbAction.Insert> insert2 = createDeepInsert("tagMap", tag2, "222", parentInsert2);
DbAction.Insert> insert3 = createDeepInsert("tagMap", tag3, "333", parentInsert2);
- MutableAggregateChange aggregateChange = MutableAggregateChange.forSave(entity);
- aggregateChange.addAction(rootInsert);
+ AggregateChangeWithRoot aggregateChange = MutableAggregateChange.forSave(entity);
+ aggregateChange.setRootAction(rootInsert);
aggregateChange.addAction(parentInsert1);
aggregateChange.addAction(parentInsert2);
aggregateChange.addAction(insert1);
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java
index f5d8479bb5..9da2eb1e24 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java
@@ -18,11 +18,9 @@
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.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 void replaceReferencedEntity() {
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 @@ static class Author {
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;
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextImmutableUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextImmutableUnitTests.java
index 9a74fcefbc..f62de3498d 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextImmutableUnitTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextImmutableUnitTests.java
@@ -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() {
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextUnitTests.java
index 9db3d8d92c..0bff8beaf4 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextUnitTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextUnitTests.java
@@ -22,6 +22,7 @@
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 void afterInsertRootIdMaybeUpdated() {
DummyEntity newRoot = executionContext.populateIdsIfNecessary();
- assertThat(newRoot).isNull();
+ assertThat(newRoot).isEqualTo(root);
assertThat(root.id).isEqualTo(23L);
}
@@ -96,7 +89,7 @@ public void idGenerationOfChild() {
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 void idGenerationOfChildInList() {
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 @@ void batchInsertOperation_withGeneratedIds() {
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 @@ void batchInsertOperation_withoutGeneratedIds() {
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 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 @@ private static class DummyEntity {
Content content;
+ ContentImmutableId contentImmutableId;
+
List list = new ArrayList<>();
}
@@ -212,4 +226,9 @@ private static class Content {
@Id Long id;
}
+ @Value
+ private static class ContentImmutableId {
+ @Id Long id;
+ }
+
}
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryBeforeSaveHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryBeforeSaveHsqlIntegrationTests.java
new file mode 100644
index 0000000000..516e886b31
--- /dev/null
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryBeforeSaveHsqlIntegrationTests.java
@@ -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 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 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 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 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 {}
+
+ @Value
+ @With
+ static class ImmutableEntity {
+ @Id Long id;
+ String name;
+ }
+
+ private interface MutableEntityRepository extends ListCrudRepository {}
+
+ @Data
+ @AllArgsConstructor
+ static class MutableEntity {
+ @Id private Long id;
+ private String name;
+ }
+
+ private interface MutableWithImmutableIdEntityRepository extends ListCrudRepository {}
+
+ @Data
+ @AllArgsConstructor
+ static class MutableWithImmutableIdEntity {
+ @Id private final Long id;
+ private String name;
+ }
+
+ private interface ImmutableWithMutableIdEntityRepository extends ListCrudRepository {}
+
+ @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 nameSetterImmutable() {
+ return (aggregate, aggregateChange) -> aggregate.withName("fromBeforeSaveCallback");
+ }
+
+ @Bean
+ BeforeSaveCallback nameSetterMutable() {
+ return (aggregate, aggregateChange) -> {
+ aggregate.setName("fromBeforeSaveCallback");
+ return aggregate;
+ };
+ }
+
+ @Bean
+ BeforeSaveCallback nameSetterMutableWithImmutableId() {
+ return (aggregate, aggregateChange) -> {
+ aggregate.setName("fromBeforeSaveCallback");
+ return aggregate;
+ };
+ }
+
+ @Bean
+ BeforeSaveCallback nameSetterImmutableWithMutableId() {
+ return (aggregate, aggregateChange) -> aggregate.withName("fromBeforeSaveCallback");
+ }
+ }
+}
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/ImmutableAggregateTemplateHsqlIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/ImmutableAggregateTemplateHsqlIntegrationTests-hsql.sql
index 42059f7663..fba412c6f2 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/ImmutableAggregateTemplateHsqlIntegrationTests-hsql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/ImmutableAggregateTemplateHsqlIntegrationTests-hsql.sql
@@ -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);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryBeforeSaveHsqlIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryBeforeSaveHsqlIntegrationTests-hsql.sql
new file mode 100644
index 0000000000..3e8d6ed8c3
--- /dev/null
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryBeforeSaveHsqlIntegrationTests-hsql.sql
@@ -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)
+);
diff --git a/spring-data-jdbc/src/test/resources/org/springframework/data/jdbc/mybatis/DummyEntityMapper.xml b/spring-data-jdbc/src/test/resources/org/springframework/data/jdbc/mybatis/DummyEntityMapper.xml
index 3cc47c485b..a123b05ac1 100644
--- a/spring-data-jdbc/src/test/resources/org/springframework/data/jdbc/mybatis/DummyEntityMapper.xml
+++ b/spring-data-jdbc/src/test/resources/org/springframework/data/jdbc/mybatis/DummyEntityMapper.xml
@@ -9,7 +9,7 @@
-
+
INSERT INTO DummyEntity (id) VALUES (DEFAULT)