diff --git a/pom.xml b/pom.xml
index eeaa0b9e93..cae7dc4252 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-relational-parent
- 2.3.0-SNAPSHOT
+ 2.3.0-992-agg-ref-converters-SNAPSHOT
pom
Spring Data Relational Parent
diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml
index 03d6a5c2a0..004f714bc2 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
- 2.3.0-SNAPSHOT
+ 2.3.0-992-agg-ref-converters-SNAPSHOT
../pom.xml
diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml
index af9ad0904e..3800c394dd 100644
--- a/spring-data-jdbc/pom.xml
+++ b/spring-data-jdbc/pom.xml
@@ -6,7 +6,7 @@
4.0.0
spring-data-jdbc
- 2.3.0-SNAPSHOT
+ 2.3.0-992-agg-ref-converters-SNAPSHOT
Spring Data JDBC
Spring Data module for JDBC repositories.
@@ -15,7 +15,7 @@
org.springframework.data
spring-data-relational-parent
- 2.3.0-SNAPSHOT
+ 2.3.0-992-agg-ref-converters-SNAPSHOT
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReferenceConverters.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReferenceConverters.java
new file mode 100644
index 0000000000..f8d7ea0b56
--- /dev/null
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReferenceConverters.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2021 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.core.convert;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.core.convert.TypeDescriptor;
+import org.springframework.core.convert.converter.GenericConverter;
+import org.springframework.data.convert.ReadingConverter;
+import org.springframework.data.convert.WritingConverter;
+import org.springframework.data.jdbc.core.mapping.AggregateReference;
+import org.springframework.lang.Nullable;
+
+/**
+ * Converters for aggregate references. They need a {@link ConversionService} in order to delegate the conversion of the
+ * content of the {@link AggregateReference}.
+ *
+ * @author Jens Schauder
+ * @since 2.6
+ */
+class AggregateReferenceConverters {
+ /**
+ * Prevent instantiation.
+ */
+ private AggregateReferenceConverters() {}
+
+ /**
+ * Converts from an AggregateReference to its id, leaving the conversion of the id to the ultimate target type to the
+ * delegate {@link ConversionService}.
+ */
+ @WritingConverter
+ static class AggregateReferenceToSimpleTypeConverter implements GenericConverter {
+
+ private final ConversionService delegate;
+
+ AggregateReferenceToSimpleTypeConverter(ConversionService delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Set getConvertibleTypes() {
+ return Collections.singleton(new ConvertiblePair(AggregateReference.class, Object.class));
+ }
+
+ @Override
+ public Object convert(@Nullable Object source, TypeDescriptor sourceDescriptor, TypeDescriptor targetDescriptor) {
+
+ if (source == null) {
+ return null;
+ }
+
+ // if the target type is an AggregateReference we just going to assume it is of the correct type,
+ // because it was already converted.
+ Class> objectType = targetDescriptor.getObjectType();
+ if (objectType.isAssignableFrom(AggregateReference.class)) {
+ return source;
+ }
+
+ Object id = ((AggregateReference, ?>) source).getId();
+
+ if (id == null) {
+ throw new IllegalStateException(
+ String.format("Aggregate references id must not be null when converting to %s from %s to %s", source,
+ sourceDescriptor, targetDescriptor));
+ }
+
+ return delegate.convert(id, TypeDescriptor.valueOf(id.getClass()), targetDescriptor);
+ }
+ }
+
+ /**
+ * Convert any simple type to an {@link AggregateReference}. If the {@literal targetDescriptor} contains information
+ * about the generic type id will properly get converted to the desired type by the delegate
+ * {@link ConversionService}.
+ */
+ @ReadingConverter
+ static class SimpleTypeToAggregateReferenceConverter implements GenericConverter {
+
+ private final ConversionService delegate;
+
+ SimpleTypeToAggregateReferenceConverter(ConversionService delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Set getConvertibleTypes() {
+ return Collections.singleton(new ConvertiblePair(Object.class, AggregateReference.class));
+ }
+
+ @Override
+ public Object convert(@Nullable Object source, TypeDescriptor sourceDescriptor, TypeDescriptor targetDescriptor) {
+
+ if (source == null) {
+ return null;
+ }
+
+ ResolvableType componentType = targetDescriptor.getResolvableType().getGenerics()[1];
+ TypeDescriptor targetType = TypeDescriptor.valueOf(componentType.resolve());
+ Object convertedId = delegate.convert(source, TypeDescriptor.valueOf(source.getClass()), targetType);
+
+ return AggregateReference.to(convertedId);
+ }
+ }
+}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
index 84b3fc665d..4a2a13945a 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
@@ -26,7 +26,9 @@
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConverterNotFoundException;
+import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
@@ -220,16 +222,12 @@ public Object readValue(@Nullable Object value, TypeInformation> type) {
}
if (getConversions().hasCustomReadTarget(value.getClass(), type.getType())) {
- return getConversionService().convert(value, type.getType());
- }
-
- if (AggregateReference.class.isAssignableFrom(type.getType())) {
- if (type.getType().isAssignableFrom(value.getClass())) {
- return value;
- }
+ TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(value.getClass());
+ TypeDescriptor targetDescriptor = typeInformationToTypeDescriptor(type);
- return readAggregateReference(value, type);
+ return getConversionService().convert(value, sourceDescriptor,
+ targetDescriptor);
}
if (value instanceof Array) {
@@ -243,12 +241,11 @@ public Object readValue(@Nullable Object value, TypeInformation> type) {
return super.readValue(value, type);
}
- @SuppressWarnings("ConstantConditions")
- private Object readAggregateReference(@Nullable Object value, TypeInformation> type) {
+ private static TypeDescriptor typeInformationToTypeDescriptor(TypeInformation> type) {
- TypeInformation> idType = type.getSuperTypeInformation(AggregateReference.class).getTypeArguments().get(1);
+ Class>[] generics = type.getTypeArguments().stream().map(TypeInformation::getType).toArray(Class[]::new);
- return AggregateReference.to(readValue(value, idType));
+ return new TypeDescriptor(ResolvableType.forClassWithGenerics(type.getType(), generics), null, null);
}
/*
@@ -263,10 +260,6 @@ public Object writeValue(@Nullable Object value, TypeInformation> type) {
return null;
}
- if (AggregateReference.class.isAssignableFrom(value.getClass())) {
- return writeValue(((AggregateReference) value).getId(), type);
- }
-
return super.writeValue(value, type);
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcCustomConversions.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcCustomConversions.java
index e91329b8b6..a0a6809c1b 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcCustomConversions.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcCustomConversions.java
@@ -15,11 +15,15 @@
*/
package org.springframework.data.jdbc.core.convert;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
+import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
@@ -36,8 +40,19 @@
*/
public class JdbcCustomConversions extends CustomConversions {
- private static final Collection