diff --git a/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java b/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java index 352bcd414..625bc7dab 100644 --- a/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java +++ b/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java @@ -34,7 +34,6 @@ import java.util.stream.StreamSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; @@ -116,7 +115,8 @@ public abstract class AbstractMappingContext(this); EntityInstantiators instantiators = new EntityInstantiators(); - PersistentPropertyAccessorFactory accessorFactory = NativeDetector.inNativeImage() ? BeanWrapperPropertyAccessorFactory.INSTANCE + PersistentPropertyAccessorFactory accessorFactory = NativeDetector.inNativeImage() + ? BeanWrapperPropertyAccessorFactory.INSTANCE : new ClassGeneratingPropertyAccessorFactory(); this.persistentPropertyAccessorFactory = new InstantiationAwarePropertyAccessorFactory(accessorFactory, @@ -485,7 +485,8 @@ public abstract class AbstractMappingContext { - if(it.isNullableWrapper()) { - return it.getActualType(); - } - return it; - }) - .filter(it -> { - - boolean shouldCreate = AbstractMappingContext.this.shouldCreatePersistentEntityFor(it); - return shouldCreate; - }) + StreamSupport.stream(property.getPersistentEntityTypes().spliterator(), false) // + .filter(AbstractMappingContext.this::shouldCreatePersistentEntityFor) // .forEach(AbstractMappingContext.this::addPersistentEntity); } @@ -668,7 +660,6 @@ public abstract class AbstractMappingContext private final Lazy isAssociation; private final Lazy> associationTargetType; + private final Lazy>> entityTypes; private final Method getter; private final Method setter; @@ -111,6 +117,43 @@ public abstract class AbstractPersistentProperty

} else { this.immutable = false; } + + this.entityTypes = Lazy.of(() -> collectEntityTypes(simpleTypeHolder, information, new LinkedHashSet<>())); + } + + protected Set> collectEntityTypes(SimpleTypeHolder simpleTypeHolder, @Nullable TypeInformation typeInformation, Set> entityTypes) { + + if(typeInformation == null || entityTypes.contains(typeInformation) || simpleTypeHolder.isSimpleType(typeInformation.getType())) { + return entityTypes; + } + + if(typeInformation.isMap()) { + + collectEntityTypes(simpleTypeHolder, typeInformation.getComponentType(), entityTypes); + collectEntityTypes(simpleTypeHolder, typeInformation.getMapValueType(), entityTypes); + return entityTypes; + } + + if(typeInformation.isCollectionLike()) { + + collectEntityTypes(simpleTypeHolder, typeInformation.getComponentType(), entityTypes); + return entityTypes; + } + + if(typeInformation.isNullableWrapper()) { + + collectEntityTypes(simpleTypeHolder, typeInformation.getActualType(), entityTypes); + return entityTypes; + } + + if(ASSOCIATION_TYPE != null && ASSOCIATION_TYPE.isAssignableFrom(typeInformation.getType())) { + + entityTypes.add(getAssociationOrActualType()); + return entityTypes; + } + + entityTypes.add(typeInformation); + return entityTypes; } protected abstract Association

createAssociation(); @@ -167,13 +210,15 @@ public abstract class AbstractPersistentProperty

@Override public Iterable> getPersistentEntityTypes() { + if(isMap() || isCollectionLike()) { + return entityTypes.get(); + } + if (!isEntity()) { return Collections.emptySet(); } - TypeInformation result = getAssociationTypeOr(() -> entityTypeInformation.getNullable()); - - return result != null ? Collections.singleton(result) : Collections.emptySet(); + return entityTypes.get(); } /* diff --git a/src/main/java/org/springframework/data/util/TypeInformation.java b/src/main/java/org/springframework/data/util/TypeInformation.java index 2db2b5c91..17ec425d3 100644 --- a/src/main/java/org/springframework/data/util/TypeInformation.java +++ b/src/main/java/org/springframework/data/util/TypeInformation.java @@ -274,6 +274,11 @@ public interface TypeInformation { return !type.equals(getType()) && type.isAssignableFrom(getType()); } + /** + * Returns whether the current type is considered a {@literal null} value wrapper or not. + * + * @return {@literal true} if the type is considered to be a {@literal null} value wrapper such as {@link java.util.Optional}. + */ default boolean isNullableWrapper() { return NullableWrapperConverters.supports(getType()); } diff --git a/src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java b/src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java index 650afde73..4e4de0a7d 100755 --- a/src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java @@ -26,11 +26,14 @@ import lombok.RequiredArgsConstructor; import lombok.Value; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.TreeMap; import java.util.function.Supplier; @@ -272,6 +275,36 @@ class AbstractMappingContextUnitTests { }); } + @Test // GH-2390 + void shouldNotCreatePersistentEntityForOptionalButItsGenericTypeArgument() { + + context.getPersistentEntity(WithOptionals.class); + + assertThat(context.getPersistentEntities()).map(it -> (Class) it.getType()) + .contains(WithOptionals.class, Person.class, Base.class) + .doesNotContain(Optional.class, List.class, ArrayList.class); + } + + @Test // GH-2390 + void shouldNotCreatePersistentEntityForListButItsGenericTypeArgument() { + + context.getPersistentEntity(WithNestedLists.class); + + assertThat(context.getPersistentEntities()).map(it -> (Class) it.getType()) + .contains(Base.class) + .doesNotContain(List.class, ArrayList.class); + } + + @Test // GH-2390 + void shouldNotCreatePersistentEntityForMapButItsGenericTypeArguments() { + + context.getPersistentEntity(WithMap.class); + + assertThat(context.getPersistentEntities()).map(it -> (Class) it.getType()) + .contains(Base.class, Person.class, MapKey.class) + .doesNotContain(List.class, Map.class, String.class, Integer.class); + } + private static void assertHasEntityFor(Class type, SampleMappingContext context, boolean expected) { boolean found = false; @@ -432,4 +465,26 @@ class AbstractMappingContextUnitTests { } } + class WithOptionals { + + Optional optionalOfString; + Optional optionalOfPerson; + List> listOfOptionalOfBase; + } + + class WithNestedLists { + ArrayList> arrayListOfOptionalOfBase; + } + + class MapKey { + + } + + class WithMap { + + Map> mapOfStringToList; + Map mapOfStringToPerson; + Map mapOfKeyToPerson; + } + }