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 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> 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 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 {} }