diff --git a/pom.xml b/pom.xml
index db989ba7a34..2cb442fe7d8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-1907-partial-insert-immutables-SNAPSHOT
pom
Spring Data Relational Parent
diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml
index 84eeb178e07..d57e0612270 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.4.0-SNAPSHOT
+ 3.4.0-1907-partial-insert-immutables-SNAPSHOT
../pom.xml
diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml
index 266af71b96c..db079067359 100644
--- a/spring-data-jdbc/pom.xml
+++ b/spring-data-jdbc/pom.xml
@@ -6,7 +6,7 @@
4.0.0
spring-data-jdbc
- 3.4.0-SNAPSHOT
+ 3.4.0-1907-partial-insert-immutables-SNAPSHOT
Spring Data JDBC
Spring Data module for JDBC repositories.
@@ -15,7 +15,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-1907-partial-insert-immutables-SNAPSHOT
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 23647874f9b..df87d3813d0 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,16 +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.Optional;
-import java.util.Set;
+import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@@ -241,7 +232,7 @@ private Object getIdFrom(DbAction.WithEntity> idOwningAction) {
RelationalPersistentEntity> persistentEntity = getRequiredPersistentEntity(idOwningAction.getEntityType());
Object identifier = persistentEntity.getIdentifierAccessor(idOwningAction.getEntity()).getIdentifier();
- Assert.state(identifier != null,() -> "Couldn't obtain a required id value for " + persistentEntity);
+ Assert.state(identifier != null, () -> "Couldn't obtain a required id value for " + persistentEntity);
return identifier;
}
@@ -268,12 +259,22 @@ List populateIdsIfNecessary() {
}
// the id property was immutable, so we have to propagate changes up the tree
- if (newEntity != action.getEntity() && action instanceof DbAction.Insert> insert) {
+ if (action instanceof DbAction.Insert> insert) {
Pair, ?> qualifier = insert.getQualifier();
+ Object qualifierValue = qualifier == null ? null : qualifier.getSecond();
- cascadingValues.stage(insert.getDependingOn(), insert.getPropertyPath(),
- qualifier == null ? null : qualifier.getSecond(), newEntity);
+ if (newEntity != action.getEntity()) {
+
+ cascadingValues.stage(insert.getDependingOn(), insert.getPropertyPath(),
+ qualifierValue, newEntity);
+
+ } else if (insert.getPropertyPath().getLeafProperty().isCollectionLike()) {
+
+ cascadingValues.gather(insert.getDependingOn(), insert.getPropertyPath(),
+ qualifierValue, newEntity);
+
+ }
}
}
@@ -360,7 +361,7 @@ private static class StagedValues {
static final List aggregators = Arrays.asList(SetAggregator.INSTANCE, MapAggregator.INSTANCE,
ListAggregator.INSTANCE, SingleElementAggregator.INSTANCE);
- Map> values = new HashMap<>();
+ Map> values = new HashMap<>();
/**
* Adds a value that needs to be set in an entity higher up in the tree of entities in the aggregate. If the
@@ -375,18 +376,26 @@ private static class StagedValues {
*/
@SuppressWarnings("unchecked")
void stage(DbAction> action, PersistentPropertyPath path, @Nullable Object qualifier, Object value) {
+ gather(action, path, qualifier, value);
+ values.get(action).get(path).isStaged = true;
+ }
+
+ void gather(DbAction> action, PersistentPropertyPath path, @Nullable Object qualifier, Object value) {
MultiValueAggregator aggregator = getAggregatorFor(path);
- Map valuesForPath = this.values.computeIfAbsent(action,
+ Map valuesForPath = this.values.computeIfAbsent(action,
dbAction -> new HashMap<>());
- T currentValue = (T) valuesForPath.computeIfAbsent(path,
- persistentPropertyPath -> aggregator.createEmptyInstance());
+ StagedValue stagedValue = valuesForPath.computeIfAbsent(path,
+ persistentPropertyPath -> new StagedValue(aggregator.createEmptyInstance()));
+ T currentValue = (T) stagedValue.value;
Object newValue = aggregator.add(currentValue, qualifier, value);
- valuesForPath.put(path, newValue);
+ stagedValue.value = newValue;
+
+ valuesForPath.put(path, stagedValue);
}
private MultiValueAggregator getAggregatorFor(PersistentPropertyPath path) {
@@ -408,7 +417,21 @@ private MultiValueAggregator getAggregatorFor(PersistentPropertyPath path) {
* property.
*/
void forEachPath(DbAction> dbAction, BiConsumer action) {
- values.getOrDefault(dbAction, Collections.emptyMap()).forEach(action);
+ values.getOrDefault(dbAction, Collections.emptyMap()).forEach((persistentPropertyPath, stagedValue) -> {
+ if (stagedValue.isStaged) {
+ action.accept(persistentPropertyPath, stagedValue.value);
+ }
+ });
+ }
+
+ }
+
+ private static class StagedValue {
+ Object value;
+ boolean isStaged;
+
+ public StagedValue(Object value) {
+ this.value = value;
}
}
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithListsIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithListsIntegrationTests.java
index 4ab222fdb72..78b33fa65b1 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithListsIntegrationTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithListsIntegrationTests.java
@@ -32,6 +32,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.PersistenceCreator;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.jdbc.testing.EnabledOnFeature;
import org.springframework.data.jdbc.testing.IntegrationTest;
@@ -55,8 +56,7 @@ public class JdbcRepositoryWithListsIntegrationTests {
private static DummyEntity createDummyEntity() {
- DummyEntity entity = new DummyEntity();
- entity.setName("Entity Name");
+ DummyEntity entity = new DummyEntity(null, "Entity Name", new ArrayList<>());
return entity;
}
@@ -94,7 +94,7 @@ public void saveAndLoadNonEmptyList() {
assertThat(reloaded.content) //
.isNotNull() //
.extracting(e -> e.id) //
- .containsExactlyInAnyOrder(element1.id, element2.id);
+ .containsExactlyInAnyOrder(entity.content.get(0).id, entity.content.get(1).id);
}
@Test // GH-1159
@@ -147,9 +147,9 @@ public void findAllLoadsList() {
@EnabledOnFeature(SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES)
public void updateList() {
- Element element1 = createElement("one");
- Element element2 = createElement("two");
- Element element3 = createElement("three");
+ Element element1 = new Element("one");
+ Element element2 = new Element("two");
+ Element element3 = new Element("three");
DummyEntity entity = createDummyEntity();
entity.content.add(element1);
@@ -157,14 +157,15 @@ public void updateList() {
entity = repository.save(entity);
- entity.content.remove(element1);
- element2.content = "two changed";
+ entity.content.remove(0);
+ entity.content.set(0, new Element(entity.content.get(0).id, "two changed"));
entity.content.add(element3);
entity = repository.save(entity);
assertThat(entity.id).isNotNull();
assertThat(entity.content).allMatch(v -> v.id != null);
+ assertThat(entity.content).hasSize(2);
DummyEntity reloaded = repository.findById(entity.id).orElseThrow(AssertionFailedError::new);
@@ -175,8 +176,8 @@ public void updateList() {
assertThat(reloaded.content) //
.extracting(e -> e.id, e -> e.content) //
.containsExactly( //
- tuple(element2.id, "two changed"), //
- tuple(element3.id, "three") //
+ tuple(entity.content.get(0).id, "two changed"), //
+ tuple(entity.content.get(1).id, "three") //
);
Long count = template.queryForObject("SELECT count(1) FROM Element", new HashMap<>(), Long.class);
@@ -186,8 +187,8 @@ public void updateList() {
@Test // DATAJDBC-130
public void deletingWithList() {
- Element element1 = createElement("one");
- Element element2 = createElement("two");
+ Element element1 = new Element("one");
+ Element element2 = new Element("two");
DummyEntity entity = createDummyEntity();
entity.content.add(element1);
@@ -203,13 +204,6 @@ public void deletingWithList() {
assertThat(count).isEqualTo(0);
}
- private Element createElement(String content) {
-
- Element element = new Element();
- element.content = content;
- return element;
- }
-
interface DummyEntityRepository extends CrudRepository {}
interface RootRepository extends CrudRepository {}
@@ -229,43 +223,22 @@ RootRepository rootRepository(JdbcRepositoryFactory factory) {
}
}
- static class DummyEntity {
+ record DummyEntity(@Id Long id, String name, List content) {
+ }
- String name;
- List content = new ArrayList<>();
- @Id private Long id;
+ record Element(@Id Long id, String content) {
- public String getName() {
- return this.name;
- }
+ @PersistenceCreator
+ Element {}
- public List getContent() {
- return this.content;
+ Element() {
+ this(null, null);
}
- public Long getId() {
- return this.id;
+ Element(String content) {
+ this(null, content);
}
- public void setName(String name) {
- this.name = name;
- }
-
- public void setContent(List content) {
- this.content = content;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
- }
-
- static class Element {
-
- String content;
- @Id private Long id;
-
- public Element() {}
}
static class Root {
diff --git a/spring-data-r2dbc/pom.xml b/spring-data-r2dbc/pom.xml
index e335b56501b..b016a64d373 100644
--- a/spring-data-r2dbc/pom.xml
+++ b/spring-data-r2dbc/pom.xml
@@ -6,7 +6,7 @@
4.0.0
spring-data-r2dbc
- 3.4.0-SNAPSHOT
+ 3.4.0-1907-partial-insert-immutables-SNAPSHOT
Spring Data R2DBC
Spring Data module for R2DBC
@@ -15,7 +15,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-1907-partial-insert-immutables-SNAPSHOT
diff --git a/spring-data-relational/pom.xml b/spring-data-relational/pom.xml
index 6be1155d38e..df4ef947623 100644
--- a/spring-data-relational/pom.xml
+++ b/spring-data-relational/pom.xml
@@ -6,7 +6,7 @@
4.0.0
spring-data-relational
- 3.4.0-SNAPSHOT
+ 3.4.0-1907-partial-insert-immutables-SNAPSHOT
Spring Data Relational
Spring Data Relational support
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-1907-partial-insert-immutables-SNAPSHOT
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java
index 28699c3bdae..1ccca7355ed 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java
@@ -15,9 +15,12 @@
*/
package org.springframework.data.relational.core.conversion;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
import java.util.function.Function;
import org.springframework.data.mapping.PersistentPropertyPath;
@@ -479,15 +482,13 @@ interface WithDependingOn extends WithPropertyPath, WithEntity {
default Pair, Object> getQualifier() {
Map, Object> qualifiers = getQualifiers();
- if (qualifiers.isEmpty())
+ if (qualifiers.isEmpty()) {
return null;
-
- if (qualifiers.size() > 1) {
- throw new IllegalStateException("Can't handle more then one qualifier");
}
- Map.Entry, Object> entry = qualifiers.entrySet().iterator()
- .next();
+ Set, Object>> entries = qualifiers.entrySet();
+ Map.Entry, Object> entry = entries.stream().sorted(Comparator.comparing(e -> -e.getKey().getLength())).findFirst().get();
+
if (entry.getValue() == null) {
return null;
}