Skip to content

Commit 3277467

Browse files
christophstroblmp911de
authored andcommitted
Resolve all persistent entities for a persistent property.
Original pull request: #2394. Closes #2390.
1 parent 1f77786 commit 3277467

File tree

4 files changed

+115
-19
lines changed

4 files changed

+115
-19
lines changed

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

+7-16
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434

3535
import org.slf4j.Logger;
3636
import org.slf4j.LoggerFactory;
37-
3837
import org.springframework.beans.BeanUtils;
3938
import org.springframework.beans.BeansException;
4039
import org.springframework.beans.factory.InitializingBean;
@@ -116,7 +115,8 @@ protected AbstractMappingContext() {
116115
this.persistentPropertyPathFactory = new PersistentPropertyPathFactory<>(this);
117116

118117
EntityInstantiators instantiators = new EntityInstantiators();
119-
PersistentPropertyAccessorFactory accessorFactory = NativeDetector.inNativeImage() ? BeanWrapperPropertyAccessorFactory.INSTANCE
118+
PersistentPropertyAccessorFactory accessorFactory = NativeDetector.inNativeImage()
119+
? BeanWrapperPropertyAccessorFactory.INSTANCE
120120
: new ClassGeneratingPropertyAccessorFactory();
121121

122122
this.persistentPropertyAccessorFactory = new InstantiationAwarePropertyAccessorFactory(accessorFactory,
@@ -485,7 +485,8 @@ protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
485485
if (simpleTypeHolder.isSimpleType(type.getType())) {
486486
return false;
487487
}
488-
if(NullableWrapperConverters.supports(type.getType())) {
488+
489+
if (NullableWrapperConverters.supports(type.getType())) {
489490
return false;
490491
}
491492

@@ -564,6 +565,7 @@ private void createAndRegisterProperty(Property input) {
564565
if (shouldSkipOverrideProperty(property)) {
565566
return;
566567
}
568+
567569
entity.addPersistentProperty(property);
568570

569571
if (property.isAssociation()) {
@@ -574,18 +576,8 @@ private void createAndRegisterProperty(Property input) {
574576
return;
575577
}
576578

577-
StreamSupport.stream(property.getPersistentEntityTypes().spliterator(), false)
578-
.map(it -> {
579-
if(it.isNullableWrapper()) {
580-
return it.getActualType();
581-
}
582-
return it;
583-
})
584-
.filter(it -> {
585-
586-
boolean shouldCreate = AbstractMappingContext.this.shouldCreatePersistentEntityFor(it);
587-
return shouldCreate;
588-
})
579+
StreamSupport.stream(property.getPersistentEntityTypes().spliterator(), false) //
580+
.filter(AbstractMappingContext.this::shouldCreatePersistentEntityFor) //
589581
.forEach(AbstractMappingContext.this::addPersistentEntity);
590582
}
591583

@@ -668,7 +660,6 @@ private Class<?> getPropertyType(PersistentProperty<?> persistentProperty) {
668660
}
669661
}
670662

671-
672663
/**
673664
* Filter rejecting static fields as well as artificially introduced ones. See
674665
* {@link PersistentPropertyFilter#UNMAPPED_PROPERTIES} for details.

src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java

+48-3
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,16 @@
1818
import java.lang.reflect.Field;
1919
import java.lang.reflect.Method;
2020
import java.lang.reflect.Modifier;
21+
import java.util.Collection;
2122
import java.util.Collections;
23+
import java.util.LinkedHashSet;
24+
import java.util.List;
2225
import java.util.Map;
2326
import java.util.Optional;
27+
import java.util.Set;
2428
import java.util.function.Supplier;
2529

30+
import org.springframework.core.GenericTypeResolver;
2631
import org.springframework.data.mapping.Association;
2732
import org.springframework.data.mapping.PersistentEntity;
2833
import org.springframework.data.mapping.PersistentProperty;
@@ -64,6 +69,7 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
6469

6570
private final Lazy<Boolean> isAssociation;
6671
private final Lazy<TypeInformation<?>> associationTargetType;
72+
private final Lazy<Collection<TypeInformation<?>>> entityTypes;
6773

6874
private final Method getter;
6975
private final Method setter;
@@ -111,6 +117,43 @@ public AbstractPersistentProperty(Property property, PersistentEntity<?, P> owne
111117
} else {
112118
this.immutable = false;
113119
}
120+
121+
this.entityTypes = Lazy.of(() -> collectEntityTypes(simpleTypeHolder, information, new LinkedHashSet<>()));
122+
}
123+
124+
protected Set<TypeInformation<?>> collectEntityTypes(SimpleTypeHolder simpleTypeHolder, @Nullable TypeInformation<?> typeInformation, Set<TypeInformation<?>> entityTypes) {
125+
126+
if(typeInformation == null || entityTypes.contains(typeInformation) || simpleTypeHolder.isSimpleType(typeInformation.getType())) {
127+
return entityTypes;
128+
}
129+
130+
if(typeInformation.isMap()) {
131+
132+
collectEntityTypes(simpleTypeHolder, typeInformation.getComponentType(), entityTypes);
133+
collectEntityTypes(simpleTypeHolder, typeInformation.getMapValueType(), entityTypes);
134+
return entityTypes;
135+
}
136+
137+
if(typeInformation.isCollectionLike()) {
138+
139+
collectEntityTypes(simpleTypeHolder, typeInformation.getComponentType(), entityTypes);
140+
return entityTypes;
141+
}
142+
143+
if(typeInformation.isNullableWrapper()) {
144+
145+
collectEntityTypes(simpleTypeHolder, typeInformation.getActualType(), entityTypes);
146+
return entityTypes;
147+
}
148+
149+
if(ASSOCIATION_TYPE != null && ASSOCIATION_TYPE.isAssignableFrom(typeInformation.getType())) {
150+
151+
entityTypes.add(getAssociationOrActualType());
152+
return entityTypes;
153+
}
154+
155+
entityTypes.add(typeInformation);
156+
return entityTypes;
114157
}
115158

116159
protected abstract Association<P> createAssociation();
@@ -167,13 +210,15 @@ public TypeInformation<?> getTypeInformation() {
167210
@Override
168211
public Iterable<? extends TypeInformation<?>> getPersistentEntityTypes() {
169212

213+
if(isMap() || isCollectionLike()) {
214+
return entityTypes.get();
215+
}
216+
170217
if (!isEntity()) {
171218
return Collections.emptySet();
172219
}
173220

174-
TypeInformation<?> result = getAssociationTypeOr(() -> entityTypeInformation.getNullable());
175-
176-
return result != null ? Collections.singleton(result) : Collections.emptySet();
221+
return entityTypes.get();
177222
}
178223

179224
/*

src/main/java/org/springframework/data/util/TypeInformation.java

+5
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,11 @@ default boolean isSubTypeOf(Class<?> type) {
274274
return !type.equals(getType()) && type.isAssignableFrom(getType());
275275
}
276276

277+
/**
278+
* Returns whether the current type is considered a {@literal null} value wrapper or not.
279+
*
280+
* @return {@literal true} if the type is considered to be a {@literal null} value wrapper such as {@link java.util.Optional}.
281+
*/
277282
default boolean isNullableWrapper() {
278283
return NullableWrapperConverters.supports(getType());
279284
}

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

+55
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@
2626
import lombok.Value;
2727

2828
import java.time.LocalDateTime;
29+
import java.util.ArrayList;
2930
import java.util.Arrays;
3031
import java.util.Collection;
3132
import java.util.Collections;
3233
import java.util.Iterator;
3334
import java.util.List;
35+
import java.util.Map;
36+
import java.util.Optional;
3437
import java.util.TreeMap;
3538
import java.util.function.Supplier;
3639

@@ -272,6 +275,36 @@ void shouldIgnoreNonAssignableOverridePropertyInSuperClass() {
272275
});
273276
}
274277

278+
@Test // GH-2390
279+
void shouldNotCreatePersistentEntityForOptionalButItsGenericTypeArgument() {
280+
281+
context.getPersistentEntity(WithOptionals.class);
282+
283+
assertThat(context.getPersistentEntities()).map(it -> (Class) it.getType())
284+
.contains(WithOptionals.class, Person.class, Base.class)
285+
.doesNotContain(Optional.class, List.class, ArrayList.class);
286+
}
287+
288+
@Test // GH-2390
289+
void shouldNotCreatePersistentEntityForListButItsGenericTypeArgument() {
290+
291+
context.getPersistentEntity(WithNestedLists.class);
292+
293+
assertThat(context.getPersistentEntities()).map(it -> (Class) it.getType())
294+
.contains(Base.class)
295+
.doesNotContain(List.class, ArrayList.class);
296+
}
297+
298+
@Test // GH-2390
299+
void shouldNotCreatePersistentEntityForMapButItsGenericTypeArguments() {
300+
301+
context.getPersistentEntity(WithMap.class);
302+
303+
assertThat(context.getPersistentEntities()).map(it -> (Class) it.getType())
304+
.contains(Base.class, Person.class, MapKey.class)
305+
.doesNotContain(List.class, Map.class, String.class, Integer.class);
306+
}
307+
275308
private static void assertHasEntityFor(Class<?> type, SampleMappingContext context, boolean expected) {
276309

277310
boolean found = false;
@@ -432,4 +465,26 @@ static class ShadowingPropertyAssignable extends ShadowedPropertyAssignable {
432465
}
433466
}
434467

468+
class WithOptionals {
469+
470+
Optional<String> optionalOfString;
471+
Optional<Person> optionalOfPerson;
472+
List<Optional<Base>> listOfOptionalOfBase;
473+
}
474+
475+
class WithNestedLists {
476+
ArrayList<List<Base>> arrayListOfOptionalOfBase;
477+
}
478+
479+
class MapKey {
480+
481+
}
482+
483+
class WithMap {
484+
485+
Map<String, List<Base>> mapOfStringToList;
486+
Map<String, Person> mapOfStringToPerson;
487+
Map<MapKey, Integer> mapOfKeyToPerson;
488+
}
489+
435490
}

0 commit comments

Comments
 (0)