Skip to content

Commit 3db2669

Browse files
mp911dechristophstrobl
authored andcommitted
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
1 parent 8dbfb9a commit 3db2669

File tree

2 files changed

+81
-17
lines changed

2 files changed

+81
-17
lines changed

src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java

+41-9
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.springframework.data.mapping.model.SimpleTypeHolder;
6060
import org.springframework.data.spel.EvaluationContextProvider;
6161
import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider;
62+
import org.springframework.data.util.CustomCollections;
6263
import org.springframework.data.util.KotlinReflectionUtils;
6364
import org.springframework.data.util.NullableWrapperConverters;
6465
import org.springframework.data.util.Optionals;
@@ -143,7 +144,6 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
143144
*
144145
* @param initialEntitySet
145146
* @see #setManagedTypes(ManagedTypes)
146-
*
147147
*/
148148
public void setInitialEntitySet(Set<? extends Class<?>> initialEntitySet) {
149149
setManagedTypes(ManagedTypes.fromIterable(initialEntitySet));
@@ -415,16 +415,18 @@ private E doAddPersistentEntity(TypeInformation<?> typeInformation) {
415415
persistentEntities.put(typeInformation, Optional.of(entity));
416416
}
417417

418-
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(type);
419-
Map<String, PropertyDescriptor> descriptors = new HashMap<>();
418+
if (shouldCreateProperties(userTypeInformation)) {
419+
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(type);
420+
Map<String, PropertyDescriptor> descriptors = new HashMap<>();
420421

421-
for (PropertyDescriptor descriptor : pds) {
422-
descriptors.put(descriptor.getName(), descriptor);
423-
}
422+
for (PropertyDescriptor descriptor : pds) {
423+
descriptors.put(descriptor.getName(), descriptor);
424+
}
424425

425-
PersistentPropertyCreator persistentPropertyCreator = new PersistentPropertyCreator(entity, descriptors);
426-
ReflectionUtils.doWithFields(type, persistentPropertyCreator, PersistentPropertyFilter.INSTANCE);
427-
persistentPropertyCreator.addPropertiesForRemainingDescriptors();
426+
PersistentPropertyCreator persistentPropertyCreator = new PersistentPropertyCreator(entity, descriptors);
427+
ReflectionUtils.doWithFields(type, persistentPropertyCreator, PersistentPropertyFilter.INSTANCE);
428+
persistentPropertyCreator.addPropertiesForRemainingDescriptors();
429+
}
428430

429431
entity.verify();
430432

@@ -475,6 +477,35 @@ public Collection<TypeInformation<?>> getManagedTypes() {
475477
*/
476478
protected abstract P createPersistentProperty(Property property, E owner, SimpleTypeHolder simpleTypeHolder);
477479

480+
/**
481+
* Whether to create the {@link PersistentProperty}s for the given {@link TypeInformation}.
482+
*
483+
* @param typeInformation must not be {@literal null}.
484+
* @return {@literal true} properties should be created, {@literal false} otherwise
485+
*/
486+
protected boolean shouldCreateProperties(TypeInformation<?> typeInformation) {
487+
488+
Class<?> type = typeInformation.getType();
489+
490+
return !typeInformation.isMap() && !isCollectionLike(type);
491+
}
492+
493+
/**
494+
* In contrast to TypeInformation, we do not consider types implementing Streamable collection-like as domain types
495+
* can implement that type.
496+
*
497+
* @param type must not be {@literal null}.
498+
* @return
499+
* @see TypeInformation#isCollectionLike()
500+
*/
501+
private static boolean isCollectionLike(Class<?> type) {
502+
503+
return type.isArray() //
504+
|| Iterable.class.equals(type) //
505+
|| Streamable.class.equals(type) //
506+
|| Collection.class.isAssignableFrom(type) || CustomCollections.isCollection(type);
507+
}
508+
478509
@Override
479510
public void afterPropertiesSet() {
480511
initialize();
@@ -532,6 +563,7 @@ private PersistentPropertyCreator(E entity, Map<String, PropertyDescriptor> desc
532563
this.remainingDescriptors = remainingDescriptors;
533564
}
534565

566+
@Override
535567
public void doWith(Field field) {
536568

537569
String fieldName = field.getName();

src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java

+40-8
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,7 @@
2222
import groovy.lang.MetaClass;
2323

2424
import java.time.LocalDateTime;
25-
import java.util.ArrayList;
26-
import java.util.Arrays;
27-
import java.util.Collection;
28-
import java.util.Collections;
29-
import java.util.List;
30-
import java.util.Map;
31-
import java.util.Optional;
32-
import java.util.TreeMap;
25+
import java.util.*;
3326
import java.util.function.Supplier;
3427

3528
import org.junit.jupiter.api.BeforeEach;
@@ -49,6 +42,7 @@
4942
import org.springframework.data.mapping.model.BasicPersistentEntity;
5043
import org.springframework.data.mapping.model.SimpleTypeHolder;
5144
import org.springframework.data.util.StreamUtils;
45+
import org.springframework.data.util.Streamable;
5246
import org.springframework.data.util.TypeInformation;
5347

5448
/**
@@ -285,6 +279,20 @@ void shouldNotCreatePersistentEntityForListButItsGenericTypeArgument() {
285279
.doesNotContain(List.class, ArrayList.class);
286280
}
287281

282+
@Test // GH-2390
283+
void doesNotCreatePropertiesForMapAndCollectionTypes() {
284+
285+
assertThat(context.getPersistentEntity(HashSet.class)).isEmpty();
286+
assertThat(context.getPersistentEntity(HashMap.class)).isEmpty();
287+
}
288+
289+
@Test // GH-2390
290+
void createsPropertiesForStreamableAndIterableTypes() {
291+
292+
assertThat(context.getPersistentEntity(MyStreamable.class)).hasSize(1);
293+
assertThat(context.getPersistentEntity(MyIterable.class)).hasSize(1);
294+
}
295+
288296
@Test // GH-2390
289297
void detectsEntityTypeEvenIfSimpleTypeHolderConsidersCollectionsSimple() {
290298

@@ -534,4 +542,28 @@ static abstract class Base$$SpringProxy$873fa2e extends Base implements SpringPr
534542

535543
}
536544

545+
static class MyIterable implements Iterable<StreamComponent> {
546+
547+
String name;
548+
549+
@Override
550+
public Iterator<StreamComponent> iterator() {
551+
return Collections.emptyIterator();
552+
}
553+
}
554+
555+
static class MyStreamable implements Streamable<StreamComponent> {
556+
557+
String name;
558+
559+
@Override
560+
public Iterator<StreamComponent> iterator() {
561+
return Collections.emptyIterator();
562+
}
563+
}
564+
565+
record StreamComponent(String name) {
566+
567+
}
568+
537569
}

0 commit comments

Comments
 (0)