diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Identifier.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Identifier.java index 5f9284a54..56f0c1a90 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Identifier.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Identifier.java @@ -99,6 +99,25 @@ public final class Identifier { return new Identifier(Collections.unmodifiableList(values)); } + /** + * Creates a new {@link Identifier} from the current instance and sets the value from {@link Identifier}. Existing key + * definitions for {@code name} are overwritten if they already exist. + * + * @param identifier the identifier to append. + * @return the {@link Identifier} containing all existing keys and the key part for {@code name}, {@code value}, and a + * {@link Class target type}. + * @since 3.5 + */ + public Identifier withPart(Identifier identifier) { + + Identifier result = this; + for (SingleIdentifierValue part : identifier.getParts()) { + result = result.withPart(part.getName(), part.getValue(), part.getTargetType()); + } + + return result; + } + /** * Creates a new {@link Identifier} from the current instance and sets the value for {@code key}. Existing key * definitions for {@code name} are overwritten if they already exist. @@ -188,6 +207,7 @@ public final class Identifier { return null; } + /** * A single value of an Identifier consisting of the column name, the value and the target type which is to be used to * store the element in the database. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java index 41e6b2c48..ef0ff6f46 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java @@ -48,7 +48,7 @@ public class JdbcIdentifierBuilder { public static JdbcIdentifierBuilder forBackReferences(JdbcConverter converter, AggregatePath path, Object value) { RelationalPersistentProperty idProperty = path.getIdDefiningParentPath().getRequiredIdProperty(); - AggregatePath.ColumnInfos reverseColumnInfos = path.getTableInfo().reverseColumnInfos(); + AggregatePath.ColumnInfos infos = path.getTableInfo().reverseColumnInfos(); // create property accessor RelationalMappingContext mappingContext = converter.getMappingContext(); @@ -62,16 +62,14 @@ public class JdbcIdentifierBuilder { valueProvider = ap -> propertyPathAccessor.getProperty(ap.getRequiredPersistentPropertyPath()); } - final Identifier[] identifierHolder = new Identifier[] { Identifier.empty() }; - - reverseColumnInfos.forEach((ap, ci) -> { + Identifier identifierHolder = infos.reduce(Identifier.empty(), (ap, ci) -> { RelationalPersistentProperty property = ap.getRequiredLeafProperty(); - identifierHolder[0] = identifierHolder[0].withPart(ci.name(), valueProvider.apply(ap), + return Identifier.of(ci.name(), valueProvider.apply(ap), converter.getColumnType(property)); - }); + }, Identifier::withPart); - return new JdbcIdentifierBuilder(identifierHolder[0]); + return new JdbcIdentifierBuilder(identifierHolder); } /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java index f721e34ee..4ea0e38b9 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.TreeMap; import java.util.function.BiConsumer; import java.util.function.BiFunction; +import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; @@ -475,14 +476,44 @@ public interface AggregatePath extends Iterable, Comparable + * If {@code ColumnInfos} is empty, then {@code identity} is returned. Without invoking {@code combiner}. The + * {@link BinaryOperator combiner} is called with the current state (or initial {@code identity}) and the + * accumulated {@code T} state to combine both into a single return value. + * + * @param identity the identity (initial) value for the combiner function. + * @param accumulator an associative, non-interfering (free of side effects), stateless function for incorporating + * an additional element into a result. + * @param combiner an associative, non-interfering, stateless function for combining two values, which must be + * compatible with the {@code accumulator} function. + * @return result of the function. + * @param type of the result. + * @since 3.5 + */ + public T reduce(T identity, BiFunction accumulator, BinaryOperator combiner) { + + T result = identity; + + for (Map.Entry entry : columnInfos.entrySet()) { + + T mapped = accumulator.apply(entry.getKey(), entry.getValue()); + result = combiner.apply(result, mapped); + } + + return result; + } + public void forEach(BiConsumer consumer) { columnInfos.forEach(consumer); } - public T any(BiFunction consumer) { + public T any(BiFunction mapper) { Map.Entry any = columnInfos.entrySet().iterator().next(); - return consumer.apply(any.getKey(), any.getValue()); + return mapper.apply(any.getKey(), any.getValue()); } public ColumnInfo get(AggregatePath path) {