Skip to content

Commit b3b590d

Browse files
committed
Unify entity type detection.
Unified the calculations made for entityTypeInformation and entityTypes in AbstractPersistentProperty. This avoids both calculations getting out of sync. Also we avoid premature calculation abortions if SimpleTypeHolder.isSimpleType(…) returns true for the raw property type. The latter has caused issues for collection properties in Spring Data KeyValue which considers everything in java.util a simple type. Related ticket: #2390.
1 parent ab74551 commit b3b590d

File tree

2 files changed

+58
-64
lines changed

2 files changed

+58
-64
lines changed

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

+40-64
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,17 @@
1818
import java.lang.reflect.Field;
1919
import java.lang.reflect.Method;
2020
import java.lang.reflect.Modifier;
21-
import java.util.Collection;
2221
import java.util.Collections;
23-
import java.util.LinkedHashSet;
22+
import java.util.HashSet;
2423
import java.util.Map;
2524
import java.util.Optional;
2625
import java.util.Set;
27-
import java.util.function.Supplier;
26+
import java.util.stream.Collectors;
2827

2928
import org.springframework.data.mapping.Association;
3029
import org.springframework.data.mapping.PersistentEntity;
3130
import org.springframework.data.mapping.PersistentProperty;
3231
import org.springframework.data.util.Lazy;
33-
import org.springframework.data.util.NullableWrapperConverters;
3432
import org.springframework.data.util.ReflectionUtils;
3533
import org.springframework.data.util.TypeInformation;
3634
import org.springframework.lang.Nullable;
@@ -64,11 +62,10 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
6462
private final Property property;
6563
private final Lazy<Integer> hashCode;
6664
private final Lazy<Boolean> usePropertyAccess;
67-
private final Lazy<TypeInformation<?>> entityTypeInformation;
65+
private final Lazy<Set<TypeInformation<?>>> entityTypeInformation;
6866

6967
private final Lazy<Boolean> isAssociation;
7068
private final Lazy<TypeInformation<?>> associationTargetType;
71-
private final Lazy<Collection<TypeInformation<?>>> entityTypes;
7269

7370
private final Method getter;
7471
private final Method setter;
@@ -100,10 +97,7 @@ public AbstractPersistentProperty(Property property, PersistentEntity<?, P> owne
10097
.map(TypeInformation::getComponentType) //
10198
.orElse(null));
10299

103-
this.entityTypeInformation = Lazy.of(() -> Optional.ofNullable(getAssociationOrActualType())
104-
.filter(it -> !simpleTypeHolder.isSimpleType(it.getType())) //
105-
.filter(it -> !it.isCollectionLike()) //
106-
.filter(it -> !it.isMap()).orElse(null));
100+
this.entityTypeInformation = Lazy.of(() -> detectEntityTypes(simpleTypeHolder));
107101

108102
this.getter = property.getGetter().orElse(null);
109103
this.setter = property.getSetter().orElse(null);
@@ -115,45 +109,6 @@ public AbstractPersistentProperty(Property property, PersistentEntity<?, P> owne
115109
} else {
116110
this.immutable = false;
117111
}
118-
119-
this.entityTypes = Lazy.of(() -> collectEntityTypes(simpleTypeHolder, information, new LinkedHashSet<>()));
120-
}
121-
122-
protected Set<TypeInformation<?>> collectEntityTypes(SimpleTypeHolder simpleTypeHolder,
123-
@Nullable TypeInformation<?> typeInformation, Set<TypeInformation<?>> entityTypes) {
124-
125-
if (typeInformation == null || entityTypes.contains(typeInformation)
126-
|| 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 (NullableWrapperConverters.supports(typeInformation.getType())) {
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;
157112
}
158113

159114
protected abstract Association<P> createAssociation();
@@ -211,14 +166,14 @@ public TypeInformation<?> getTypeInformation() {
211166
public Iterable<? extends TypeInformation<?>> getPersistentEntityTypes() {
212167

213168
if (isMap() || isCollectionLike()) {
214-
return entityTypes.get();
169+
return entityTypeInformation.get();
215170
}
216171

217172
if (!isEntity()) {
218173
return Collections.emptySet();
219174
}
220175

221-
return entityTypes.get();
176+
return entityTypeInformation.get();
222177
}
223178

224179
/*
@@ -362,7 +317,7 @@ public boolean isArray() {
362317
*/
363318
@Override
364319
public boolean isEntity() {
365-
return !isTransient() && entityTypeInformation.getNullable() != null;
320+
return !isTransient() && !entityTypeInformation.get().isEmpty();
366321
}
367322

368323
/*
@@ -401,7 +356,11 @@ public Class<?> getMapValueType() {
401356
*/
402357
@Override
403358
public Class<?> getActualType() {
404-
return getRequiredAssociationOrActualType().getType();
359+
360+
TypeInformation<?> targetType = associationTargetType.getNullable();
361+
TypeInformation<?> result = targetType == null ? information.getRequiredActualType() : targetType;
362+
363+
return result.getType();
405364
}
406365

407366
/*
@@ -454,23 +413,40 @@ public String toString() {
454413
return property.toString();
455414
}
456415

457-
@Nullable
458-
private TypeInformation<?> getAssociationOrActualType() {
459-
return getAssociationTypeOr(() -> information.getActualType());
460-
}
416+
private Set<TypeInformation<?>> detectEntityTypes(SimpleTypeHolder simpleTypes) {
417+
418+
TypeInformation<?> typeToStartWith = ASSOCIATION_TYPE != null && ASSOCIATION_TYPE.isAssignableFrom(rawType)
419+
? information.getComponentType()
420+
: information;
421+
422+
Set<TypeInformation<?>> result = detectEntityTypes(typeToStartWith);
461423

462-
private TypeInformation<?> getRequiredAssociationOrActualType() {
463-
return getAssociationTypeOr(() -> information.getRequiredActualType());
424+
return result.stream()
425+
.filter(it -> !simpleTypes.isSimpleType(it.getType()))
426+
.filter(it -> !it.getType().equals(ASSOCIATION_TYPE))
427+
.collect(Collectors.toSet());
464428
}
465429

466-
private TypeInformation<?> getAssociationTypeOr(Supplier<TypeInformation<?>> fallback) {
430+
private Set<TypeInformation<?>> detectEntityTypes(@Nullable TypeInformation<?> source) {
467431

468-
TypeInformation<?> result = associationTargetType.getNullable();
432+
if (source == null) {
433+
return Collections.emptySet();
434+
}
435+
436+
Set<TypeInformation<?>> result = new HashSet<>();
469437

470-
if (result != null) {
471-
return result;
438+
if (source.isMap()) {
439+
result.addAll(detectEntityTypes(source.getComponentType()));
440+
}
441+
442+
TypeInformation<?> actualType = source.getActualType();
443+
444+
if (source.equals(actualType)) {
445+
result.add(source);
446+
} else {
447+
result.addAll(detectEntityTypes(actualType));
472448
}
473449

474-
return fallback.get();
450+
return result;
475451
}
476452
}

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

+18
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,24 @@ void shouldNotCreatePersistentEntityForListButItsGenericTypeArgument() {
295295
.doesNotContain(List.class, ArrayList.class);
296296
}
297297

298+
@Test // GH-2390
299+
void detectsEntityTypeEveneIfSimpleTypeHolderConsidersCollectionsSimple() {
300+
301+
context.setSimpleTypeHolder(new SimpleTypeHolder(Collections.emptySet(), true) {
302+
303+
@Override
304+
public boolean isSimpleType(Class<?> type) {
305+
return type.getName().startsWith("java.util.");
306+
}
307+
});
308+
309+
context.getPersistentEntity(WithNestedLists.class);
310+
311+
assertThat(context.getPersistentEntities()) //
312+
.map(it -> (Class) it.getType()) //
313+
.contains(Base.class);
314+
}
315+
298316
@Test // GH-2390
299317
void shouldNotCreatePersistentEntityForMapButItsGenericTypeArguments() {
300318

0 commit comments

Comments
 (0)