conversionService = () -> DefaultConversionService.getSharedInstance();
+
+ IdentifierToPrimitivesConverter toPrimitives = new IdentifierToPrimitivesConverter(conversionService);
+ PrimitivesToIdentifierConverter toIdentifier = new PrimitivesToIdentifierConverter(conversionService);
+
+ converters.add(toPrimitives);
+ converters.add(toIdentifier);
+ converters.add(new AssociationToPrimitivesConverter<>(toPrimitives));
+ converters.add(new PrimitivesToAssociationConverter<>(toIdentifier));
+
+ return converters;
+ }
+}
diff --git a/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java b/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java
index 06a289690a..321503ad97 100644
--- a/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java
+++ b/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java
@@ -43,9 +43,13 @@
public abstract class AbstractPersistentProperty> implements PersistentProperty
{
private static final Field CAUSE_FIELD;
+ private static final Class> ASSOCIATION_TYPE;
static {
+
CAUSE_FIELD = ReflectionUtils.findRequiredField(Throwable.class, "cause");
+ ASSOCIATION_TYPE = ReflectionUtils.loadIfPresent("org.jmolecules.ddd.types.Association",
+ AbstractPersistentProperty.class.getClassLoader());
}
private final String name;
@@ -241,7 +245,8 @@ public boolean isImmutable() {
*/
@Override
public boolean isAssociation() {
- return isAnnotationPresent(Reference.class);
+ return isAnnotationPresent(Reference.class) //
+ || ASSOCIATION_TYPE != null && ASSOCIATION_TYPE.isAssignableFrom(rawType);
}
/*
diff --git a/src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java b/src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java
index 6c6294bd7a..b7eb902e8e 100644
--- a/src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java
+++ b/src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java
@@ -70,7 +70,8 @@ public abstract class AnnotationBasedPersistentProperty
isWritable = Lazy
.of(() -> !isTransient() && !isAnnotationPresent(ReadOnlyProperty.class));
- private final Lazy isReference = Lazy.of(() -> !isTransient() && isAnnotationPresent(Reference.class));
+ private final Lazy isReference = Lazy.of(() -> !isTransient() //
+ && (isAnnotationPresent(Reference.class) || super.isAssociation()));
private final Lazy isId = Lazy.of(() -> isAnnotationPresent(Id.class));
private final Lazy isVersion = Lazy.of(() -> isAnnotationPresent(Version.class));
diff --git a/src/main/java/org/springframework/data/util/ReflectionUtils.java b/src/main/java/org/springframework/data/util/ReflectionUtils.java
index 3946db7c7a..54a5062ae3 100644
--- a/src/main/java/org/springframework/data/util/ReflectionUtils.java
+++ b/src/main/java/org/springframework/data/util/ReflectionUtils.java
@@ -475,4 +475,21 @@ public static Object getPrimitiveDefault(Class> type) {
throw new IllegalArgumentException(String.format("Primitive type %s not supported!", type));
}
+ /**
+ * Loads the class with the given name using the given {@link ClassLoader}.
+ *
+ * @param name the name of the class to be loaded.
+ * @param classLoader the {@link ClassLoader} to use to load the class.
+ * @return the {@link Class} or {@literal null} in case the class can't be loaded for any reason.
+ * @since 2.5
+ */
+ @Nullable
+ public static Class> loadIfPresent(String name, ClassLoader classLoader) {
+
+ try {
+ return ClassUtils.forName(name, classLoader);
+ } catch (Exception o_O) {
+ return null;
+ }
+ }
}
diff --git a/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java b/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java
index c37cbc2512..adf63a721e 100644
--- a/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java
+++ b/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java
@@ -16,6 +16,7 @@
package org.springframework.data.convert;
import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.text.DateFormat;
@@ -28,6 +29,8 @@
import java.util.Map;
import java.util.function.Predicate;
+import org.jmolecules.ddd.types.Association;
+import org.jmolecules.ddd.types.Identifier;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Test;
import org.springframework.aop.framework.ProxyFactory;
@@ -272,6 +275,24 @@ void doesNotSkipUserConverterConverterEvenWhenConfigurationWouldNotAllowIt() {
verify(registry).addConverter(any(LocalDateTimeToDateConverter.class));
}
+ @Test // GH-2315
+ void addsAssociationConvertersByDefault() {
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE, Collections.emptyList());
+
+ assertThat(conversions.hasCustomWriteTarget(Association.class)).isTrue();
+ assertThat(conversions.hasCustomReadTarget(Object.class, Association.class)).isTrue();
+ }
+
+ @Test // GH-2315
+ void addsIdentifierConvertersByDefault() {
+
+ CustomConversions conversions = new CustomConversions(StoreConversions.NONE, Collections.emptyList());
+
+ assertThat(conversions.hasCustomWriteTarget(Identifier.class)).isTrue();
+ assertThat(conversions.hasCustomReadTarget(String.class, Identifier.class)).isTrue();
+ }
+
private static Class> createProxyTypeFor(Class> type) {
ProxyFactory factory = new ProxyFactory();
diff --git a/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java b/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java
index 462b818a94..e17b8ec7b0 100755
--- a/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java
+++ b/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java
@@ -224,6 +224,14 @@ void returnsAccessorsForGenericReturnType() {
assertThat(property.getGetter()).isNotNull();
}
+ @Test // GH-2315
+ void detectsJMoleculesAssociation() {
+
+ SamplePersistentProperty property = getProperty(JMolecules.class, "association");
+
+ assertThat(property.isAssociation()).isTrue();
+ }
+
private BasicPersistentEntity getEntity(Class type) {
return new BasicPersistentEntity<>(ClassTypeInformation.from(type));
}
@@ -344,11 +352,6 @@ public boolean isVersionProperty() {
return false;
}
- @Override
- public boolean isAssociation() {
- return false;
- }
-
@Override
protected Association createAssociation() {
return null;
@@ -387,4 +390,8 @@ static class Sample {
class TreeMapWrapper {
TreeMap> map;
}
+
+ class JMolecules {
+ org.jmolecules.ddd.types.Association association;
+ }
}
diff --git a/src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java b/src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java
index f5daba5210..35c8db2a92 100755
--- a/src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java
+++ b/src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java
@@ -26,6 +26,7 @@
import java.util.Optional;
import java.util.stream.Stream;
+import org.jmolecules.ddd.types.Association;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.annotation.AliasFor;
@@ -293,6 +294,11 @@ public void missingRequiredFieldThrowsException() {
.withMessageContaining(NoField.class.getName());
}
+ @Test // GH-2315
+ void detectesJMoleculesAssociation() {
+ assertThat(getProperty(JMolecules.class, "association").isAssociation()).isTrue();
+ }
+
@SuppressWarnings("unchecked")
private Map, Annotation> getAnnotationCache(SamplePersistentProperty property) {
return (Map, Annotation>) ReflectionTestUtils.getField(property, "annotationCache");
@@ -414,8 +420,7 @@ public String getProperty() {
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { FIELD, METHOD, ANNOTATION_TYPE })
@Id
- public @interface MyId {
- }
+ public @interface MyId {}
static class FieldAccess {
String name;
@@ -477,4 +482,8 @@ interface NoField {
String getFirstname();
}
+
+ static class JMolecules {
+ Association association;
+ }
}