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 321503ad97..49618a8a2a 100644
--- a/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java
+++ b/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java
@@ -22,7 +22,6 @@
import java.util.Map;
import java.util.Optional;
-import org.springframework.data.annotation.Reference;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
@@ -64,6 +63,9 @@ public abstract class AbstractPersistentProperty
private final Lazy usePropertyAccess;
private final Lazy>> entityTypeInformation;
+ private final Lazy isAssociation;
+ private final Lazy> associationTargetType;
+
private final Method getter;
private final Method setter;
private final Field field;
@@ -86,6 +88,15 @@ public AbstractPersistentProperty(Property property, PersistentEntity, P> owne
this.hashCode = Lazy.of(property::hashCode);
this.usePropertyAccess = Lazy.of(() -> owner.getType().isInterface() || CAUSE_FIELD.equals(getField()));
+ this.isAssociation = Lazy.of(() -> ASSOCIATION_TYPE != null && ASSOCIATION_TYPE.isAssignableFrom(rawType));
+ this.associationTargetType = ASSOCIATION_TYPE == null
+ ? Lazy.empty()
+ : Lazy.of(() -> Optional.of(getTypeInformation())
+ .map(it -> it.getSuperTypeInformation(ASSOCIATION_TYPE))
+ .map(TypeInformation::getComponentType)
+ .map(TypeInformation::getType)
+ .orElse(null));
+
this.entityTypeInformation = Lazy.of(() -> Optional.ofNullable(information.getActualType())//
.filter(it -> !simpleTypeHolder.isSimpleType(it.getType()))//
.filter(it -> !it.isCollectionLike())//
@@ -170,6 +181,7 @@ public Iterable extends TypeInformation>> getPersistentEntityTypes() {
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getGetter()
*/
+ @Nullable
@Override
public Method getGetter() {
return this.getter;
@@ -179,6 +191,7 @@ public Method getGetter() {
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getSetter()
*/
+ @Nullable
@Override
public Method getSetter() {
return this.setter;
@@ -188,6 +201,7 @@ public Method getSetter() {
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getWither()
*/
+ @Nullable
@Override
public Method getWither() {
return this.wither;
@@ -245,8 +259,7 @@ public boolean isImmutable() {
*/
@Override
public boolean isAssociation() {
- return isAnnotationPresent(Reference.class) //
- || ASSOCIATION_TYPE != null && ASSOCIATION_TYPE.isAssignableFrom(rawType);
+ return isAssociation.get();
}
/*
@@ -259,6 +272,16 @@ public Association getAssociation() {
return association.orElse(null);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mapping.PersistentProperty#getAssociationTargetType()
+ */
+ @Nullable
+ @Override
+ public Class> getAssociationTargetType() {
+ return associationTargetType.getNullable();
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isCollectionLike()
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 b7eb902e8e..ffca2689ee 100644
--- a/src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java
+++ b/src/main/java/org/springframework/data/mapping/model/AnnotationBasedPersistentProperty.java
@@ -74,6 +74,18 @@ public abstract class AnnotationBasedPersistentProperty
isId = Lazy.of(() -> isAnnotationPresent(Id.class));
private final Lazy isVersion = Lazy.of(() -> isAnnotationPresent(Version.class));
+ private final Lazy> associationTargetType = Lazy.of(() -> {
+
+ if (!isAssociation()) {
+ return null;
+ }
+
+ return Optional.of(Reference.class) //
+ .map(this::findAnnotation) //
+ .map(Reference::to) //
+ .map(it -> !Class.class.equals(it) ? it : getActualType()) //
+ .orElseGet(() -> super.getAssociationTargetType());
+ });
/**
* Creates a new {@link AnnotationBasedPersistentProperty}.
@@ -285,18 +297,7 @@ public boolean usePropertyAccess() {
@Nullable
@Override
public Class> getAssociationTargetType() {
-
- Reference reference = findAnnotation(Reference.class);
-
- if (reference == null) {
- return isEntity() ? getActualType() : null;
- }
-
- Class> targetType = reference.to();
-
- return Class.class.equals(targetType) //
- ? isEntity() ? getActualType() : null //
- : targetType;
+ return associationTargetType.getNullable();
}
/*
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 e17b8ec7b0..06cd5300bf 100755
--- a/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java
+++ b/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java
@@ -32,6 +32,8 @@
import java.util.TreeMap;
import java.util.TreeSet;
+import org.jmolecules.ddd.types.AggregateRoot;
+import org.jmolecules.ddd.types.Identifier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.mapping.Association;
@@ -230,6 +232,7 @@ void detectsJMoleculesAssociation() {
SamplePersistentProperty property = getProperty(JMolecules.class, "association");
assertThat(property.isAssociation()).isTrue();
+ assertThat(property.getAssociationTargetType()).isEqualTo(JMoleculesAggregate.class);
}
private BasicPersistentEntity getEntity(Class type) {
@@ -371,11 +374,6 @@ public boolean isAnnotationPresent(Class extends Annotation> annotationType) {
public A findPropertyOrOwnerAnnotation(Class annotationType) {
return null;
}
-
- @Override
- public Class> getAssociationTargetType() {
- return null;
- }
}
static class Sample {
@@ -392,6 +390,10 @@ class TreeMapWrapper {
}
class JMolecules {
- org.jmolecules.ddd.types.Association association;
+ org.jmolecules.ddd.types.Association association;
+ }
+
+ interface JMoleculesAggregate extends AggregateRoot {
+
}
}
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 35c8db2a92..badaf11e56 100755
--- a/src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java
+++ b/src/test/java/org/springframework/data/mapping/model/AnnotationBasedPersistentPropertyUnitTests.java
@@ -24,11 +24,17 @@
import java.lang.annotation.Target;
import java.util.Map;
import java.util.Optional;
+import java.util.function.Function;
import java.util.stream.Stream;
+import org.assertj.core.api.ClassAssert;
+import org.jmolecules.ddd.types.AggregateRoot;
import org.jmolecules.ddd.types.Association;
+import org.jmolecules.ddd.types.Identifier;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestFactory;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.annotation.AccessType;
@@ -242,12 +248,21 @@ public void getRequiredAnnotationThrowsException() {
assertThatThrownBy(() -> property.getRequiredAnnotation(Transient.class)).isInstanceOf(IllegalStateException.class);
}
- @Test // DATACMNS-1318
- public void detectsUltimateAssociationTargetClass() {
+ @TestFactory // DATACMNS-1318
+ public Stream detectsUltimateAssociationTargetClass() {
- Stream.of("toSample", "toSample2", "sample", "withoutAnnotation").forEach(it -> {
- assertThat(getProperty(WithReferences.class, it).getAssociationTargetType()).isEqualTo(Sample.class);
- });
+ Function verifier = it -> assertThat(
+ getProperty(WithReferences.class, it).getAssociationTargetType());
+
+ Stream positives = DynamicTest.stream(Stream.of("toSample", "toSample2", "sample"), //
+ it -> String.format("Property %s resolves to %s.", it, Sample.class.getSimpleName()), //
+ it -> verifier.apply(it).isEqualTo(Sample.class));
+
+ Stream negatives = DynamicTest.stream(Stream.of("withoutAnnotation"), //
+ it -> String.format("Property %s resolves to null.", it), //
+ it -> verifier.apply(it).isNull());
+
+ return Stream.concat(positives, negatives);
}
@Test // DATACMNS-1359
@@ -295,8 +310,12 @@ public void missingRequiredFieldThrowsException() {
}
@Test // GH-2315
- void detectesJMoleculesAssociation() {
- assertThat(getProperty(JMolecules.class, "association").isAssociation()).isTrue();
+ void detectsJMoleculesAssociation() {
+
+ SamplePersistentProperty property = getProperty(JMolecules.class, "association");
+
+ assertThat(property.isAssociation()).isTrue();
+ assertThat(property.getAssociationTargetType()).isEqualTo(JMoleculesAggregate.class);
}
@SuppressWarnings("unchecked")
@@ -484,6 +503,8 @@ interface NoField {
}
static class JMolecules {
- Association association;
+ Association association;
}
+
+ interface JMoleculesAggregate extends AggregateRoot {}
}