Browse Source

Resolve all persistent entities for a persistent property.

Original pull request: #2394.
Closes #2390.
pull/2410/head
Christoph Strobl 5 years ago committed by Mark Paluch
parent
commit
3277467b74
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 23
      src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java
  2. 51
      src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java
  3. 5
      src/main/java/org/springframework/data/util/TypeInformation.java
  4. 55
      src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java

23
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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
@ -116,7 +115,8 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
this.persistentPropertyPathFactory = new PersistentPropertyPathFactory<>(this); this.persistentPropertyPathFactory = new PersistentPropertyPathFactory<>(this);
EntityInstantiators instantiators = new EntityInstantiators(); EntityInstantiators instantiators = new EntityInstantiators();
PersistentPropertyAccessorFactory accessorFactory = NativeDetector.inNativeImage() ? BeanWrapperPropertyAccessorFactory.INSTANCE PersistentPropertyAccessorFactory accessorFactory = NativeDetector.inNativeImage()
? BeanWrapperPropertyAccessorFactory.INSTANCE
: new ClassGeneratingPropertyAccessorFactory(); : new ClassGeneratingPropertyAccessorFactory();
this.persistentPropertyAccessorFactory = new InstantiationAwarePropertyAccessorFactory(accessorFactory, this.persistentPropertyAccessorFactory = new InstantiationAwarePropertyAccessorFactory(accessorFactory,
@ -485,7 +485,8 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
if (simpleTypeHolder.isSimpleType(type.getType())) { if (simpleTypeHolder.isSimpleType(type.getType())) {
return false; return false;
} }
if(NullableWrapperConverters.supports(type.getType())) {
if (NullableWrapperConverters.supports(type.getType())) {
return false; return false;
} }
@ -564,6 +565,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
if (shouldSkipOverrideProperty(property)) { if (shouldSkipOverrideProperty(property)) {
return; return;
} }
entity.addPersistentProperty(property); entity.addPersistentProperty(property);
if (property.isAssociation()) { if (property.isAssociation()) {
@ -574,18 +576,8 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
return; return;
} }
StreamSupport.stream(property.getPersistentEntityTypes().spliterator(), false) StreamSupport.stream(property.getPersistentEntityTypes().spliterator(), false) //
.map(it -> { .filter(AbstractMappingContext.this::shouldCreatePersistentEntityFor) //
if(it.isNullableWrapper()) {
return it.getActualType();
}
return it;
})
.filter(it -> {
boolean shouldCreate = AbstractMappingContext.this.shouldCreatePersistentEntityFor(it);
return shouldCreate;
})
.forEach(AbstractMappingContext.this::addPersistentEntity); .forEach(AbstractMappingContext.this::addPersistentEntity);
} }
@ -668,7 +660,6 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
} }
} }
/** /**
* Filter rejecting static fields as well as artificially introduced ones. See * Filter rejecting static fields as well as artificially introduced ones. See
* {@link PersistentPropertyFilter#UNMAPPED_PROPERTIES} for details. * {@link PersistentPropertyFilter#UNMAPPED_PROPERTIES} for details.

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

@ -18,11 +18,16 @@ package org.springframework.data.mapping.model;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.springframework.core.GenericTypeResolver;
import org.springframework.data.mapping.Association; import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentProperty;
@ -64,6 +69,7 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
private final Lazy<Boolean> isAssociation; private final Lazy<Boolean> isAssociation;
private final Lazy<TypeInformation<?>> associationTargetType; private final Lazy<TypeInformation<?>> associationTargetType;
private final Lazy<Collection<TypeInformation<?>>> entityTypes;
private final Method getter; private final Method getter;
private final Method setter; private final Method setter;
@ -111,6 +117,43 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
} else { } else {
this.immutable = false; this.immutable = false;
} }
this.entityTypes = Lazy.of(() -> collectEntityTypes(simpleTypeHolder, information, new LinkedHashSet<>()));
}
protected Set<TypeInformation<?>> collectEntityTypes(SimpleTypeHolder simpleTypeHolder, @Nullable TypeInformation<?> typeInformation, Set<TypeInformation<?>> 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<P> createAssociation(); protected abstract Association<P> createAssociation();
@ -167,13 +210,15 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
@Override @Override
public Iterable<? extends TypeInformation<?>> getPersistentEntityTypes() { public Iterable<? extends TypeInformation<?>> getPersistentEntityTypes() {
if(isMap() || isCollectionLike()) {
return entityTypes.get();
}
if (!isEntity()) { if (!isEntity()) {
return Collections.emptySet(); return Collections.emptySet();
} }
TypeInformation<?> result = getAssociationTypeOr(() -> entityTypeInformation.getNullable()); return entityTypes.get();
return result != null ? Collections.singleton(result) : Collections.emptySet();
} }
/* /*

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

@ -274,6 +274,11 @@ public interface TypeInformation<S> {
return !type.equals(getType()) && type.isAssignableFrom(getType()); 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() { default boolean isNullableWrapper() {
return NullableWrapperConverters.supports(getType()); return NullableWrapperConverters.supports(getType());
} }

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

@ -26,11 +26,14 @@ import lombok.RequiredArgsConstructor;
import lombok.Value; import lombok.Value;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.function.Supplier; 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) { private static void assertHasEntityFor(Class<?> type, SampleMappingContext context, boolean expected) {
boolean found = false; boolean found = false;
@ -432,4 +465,26 @@ class AbstractMappingContextUnitTests {
} }
} }
class WithOptionals {
Optional<String> optionalOfString;
Optional<Person> optionalOfPerson;
List<Optional<Base>> listOfOptionalOfBase;
}
class WithNestedLists {
ArrayList<List<Base>> arrayListOfOptionalOfBase;
}
class MapKey {
}
class WithMap {
Map<String, List<Base>> mapOfStringToList;
Map<String, Person> mapOfStringToPerson;
Map<MapKey, Integer> mapOfKeyToPerson;
}
} }

Loading…
Cancel
Save