From 3db2669512467ab7dab89f8e74d6142a68f751d5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 7 Mar 2024 09:32:56 +0100 Subject: [PATCH] Do not create persistent properties for Map and Collection-like entities. These types are expected to behave like maps and collections and should not carry properties. The only exception are types implementing Streamable as Streamable can be used in domain types. Resolves: #3056 Original Pull Request: #3059 --- .../context/AbstractMappingContext.java | 50 +++++++++++++++---- .../AbstractMappingContextUnitTests.java | 48 +++++++++++++++--- 2 files changed, 81 insertions(+), 17 deletions(-) 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 4c2ecc88f..1fbc89791 100644 --- a/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java +++ b/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java @@ -59,6 +59,7 @@ import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider; +import org.springframework.data.util.CustomCollections; import org.springframework.data.util.KotlinReflectionUtils; import org.springframework.data.util.NullableWrapperConverters; import org.springframework.data.util.Optionals; @@ -143,7 +144,6 @@ public abstract class AbstractMappingContext> initialEntitySet) { setManagedTypes(ManagedTypes.fromIterable(initialEntitySet)); @@ -415,17 +415,19 @@ public abstract class AbstractMappingContext descriptors = new HashMap<>(); + if (shouldCreateProperties(userTypeInformation)) { + PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(type); + Map descriptors = new HashMap<>(); - for (PropertyDescriptor descriptor : pds) { - descriptors.put(descriptor.getName(), descriptor); + for (PropertyDescriptor descriptor : pds) { + descriptors.put(descriptor.getName(), descriptor); + } + + PersistentPropertyCreator persistentPropertyCreator = new PersistentPropertyCreator(entity, descriptors); + ReflectionUtils.doWithFields(type, persistentPropertyCreator, PersistentPropertyFilter.INSTANCE); + persistentPropertyCreator.addPropertiesForRemainingDescriptors(); } - PersistentPropertyCreator persistentPropertyCreator = new PersistentPropertyCreator(entity, descriptors); - ReflectionUtils.doWithFields(type, persistentPropertyCreator, PersistentPropertyFilter.INSTANCE); - persistentPropertyCreator.addPropertiesForRemainingDescriptors(); - entity.verify(); if (persistentPropertyAccessorFactory.isSupported(entity)) { @@ -475,6 +477,35 @@ public abstract class AbstractMappingContext typeInformation) { + + Class type = typeInformation.getType(); + + return !typeInformation.isMap() && !isCollectionLike(type); + } + + /** + * In contrast to TypeInformation, we do not consider types implementing Streamable collection-like as domain types + * can implement that type. + * + * @param type must not be {@literal null}. + * @return + * @see TypeInformation#isCollectionLike() + */ + private static boolean isCollectionLike(Class type) { + + return type.isArray() // + || Iterable.class.equals(type) // + || Streamable.class.equals(type) // + || Collection.class.isAssignableFrom(type) || CustomCollections.isCollection(type); + } + @Override public void afterPropertiesSet() { initialize(); @@ -532,6 +563,7 @@ public abstract class AbstractMappingContext { + + String name; + + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + } + + static class MyStreamable implements Streamable { + + String name; + + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + } + + record StreamComponent(String name) { + + } + }