Skip to content

Support for association target type detection for jMolecules. #2344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -64,6 +63,9 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
private final Lazy<Boolean> usePropertyAccess;
private final Lazy<Optional<? extends TypeInformation<?>>> entityTypeInformation;

private final Lazy<Boolean> isAssociation;
private final Lazy<Class<?>> associationTargetType;

private final Method getter;
private final Method setter;
private final Field field;
Expand All @@ -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())//
Expand Down Expand Up @@ -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;
Expand All @@ -179,6 +191,7 @@ public Method getGetter() {
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getSetter()
*/
@Nullable
@Override
public Method getSetter() {
return this.setter;
Expand All @@ -188,6 +201,7 @@ public Method getSetter() {
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getWither()
*/
@Nullable
@Override
public Method getWither() {
return this.wither;
Expand Down Expand Up @@ -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();
}

/*
Expand All @@ -259,6 +272,16 @@ public Association<P> 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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
&& (isAnnotationPresent(Reference.class) || super.isAssociation()));
private final Lazy<Boolean> isId = Lazy.of(() -> isAnnotationPresent(Id.class));
private final Lazy<Boolean> isVersion = Lazy.of(() -> isAnnotationPresent(Version.class));
private final Lazy<Class<?>> 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}.
Expand Down Expand Up @@ -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();
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -230,6 +232,7 @@ void detectsJMoleculesAssociation() {
SamplePersistentProperty property = getProperty(JMolecules.class, "association");

assertThat(property.isAssociation()).isTrue();
assertThat(property.getAssociationTargetType()).isEqualTo(JMoleculesAggregate.class);
}

private <T> BasicPersistentEntity<T, SamplePersistentProperty> getEntity(Class<T> type) {
Expand Down Expand Up @@ -371,11 +374,6 @@ public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
public <A extends Annotation> A findPropertyOrOwnerAnnotation(Class<A> annotationType) {
return null;
}

@Override
public Class<?> getAssociationTargetType() {
return null;
}
}

static class Sample {
Expand All @@ -392,6 +390,10 @@ class TreeMapWrapper {
}

class JMolecules {
org.jmolecules.ddd.types.Association association;
org.jmolecules.ddd.types.Association<JMoleculesAggregate, Identifier> association;
}

interface JMoleculesAggregate extends AggregateRoot<JMoleculesAggregate, Identifier> {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<DynamicTest> detectsUltimateAssociationTargetClass() {

Stream.of("toSample", "toSample2", "sample", "withoutAnnotation").forEach(it -> {
assertThat(getProperty(WithReferences.class, it).getAssociationTargetType()).isEqualTo(Sample.class);
});
Function<String, ClassAssert> verifier = it -> assertThat(
getProperty(WithReferences.class, it).getAssociationTargetType());

Stream<DynamicTest> 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<DynamicTest> 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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -484,6 +503,8 @@ interface NoField {
}

static class JMolecules {
Association association;
Association<JMoleculesAggregate, Identifier> association;
}

interface JMoleculesAggregate extends AggregateRoot<JMoleculesAggregate, Identifier> {}
}