DATACMNS-101 - Made AbstractMappingContext more flexible for extension.
The former getNestedTypeToAdd(…) method in AbstractMappingContext is now located in PersistentProperty to allow easier customization of the implementation. We also not only support returning one type from it but rather an Iterable of them to prepare handling of custom types implementing Collection interfaces.
This commit is contained in:
@@ -2,6 +2,8 @@ package org.springframework.data.mapping;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
@@ -30,39 +32,79 @@ public interface PersistentProperty<P extends PersistentProperty<P>> {
|
||||
|
||||
TypeInformation<?> getTypeInformation();
|
||||
|
||||
/**
|
||||
* Returns the {@link TypeInformation} if the property references a {@link PersistentEntity}. Will return
|
||||
* {@literal null} in case it refers to a simple type. Will return {@link Collection}'s component type or the
|
||||
* {@link Map}'s value type transparently.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Iterable<? extends TypeInformation<?>> getPersistentEntityType();
|
||||
|
||||
/**
|
||||
* Returns the {@link PropertyDescriptor} backing the {@link PersistentProperty}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
PropertyDescriptor getPropertyDescriptor();
|
||||
|
||||
Field getField();
|
||||
|
||||
String getSpelExpression();
|
||||
|
||||
boolean isTransient();
|
||||
|
||||
boolean isAssociation();
|
||||
|
||||
Association<P> getAssociation();
|
||||
|
||||
boolean isCollection();
|
||||
|
||||
boolean isMap();
|
||||
|
||||
boolean isArray();
|
||||
|
||||
boolean isComplexType();
|
||||
|
||||
/**
|
||||
* Returns whether the property has to be regarded as entity which means its type will be also be considered to be a
|
||||
* {@link PersistentEntity}.
|
||||
* Returns whether the property is the ID property of the owning {@link PersistentEntity}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isEntity();
|
||||
boolean isIdProperty();
|
||||
|
||||
/**
|
||||
* Returns whether the property is a {@link Collection}, {@link Iterable} or an array.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isCollectionLike();
|
||||
|
||||
/**
|
||||
* Returns the component type of the type if it is a {@link java.util.Collection}. Will return the type of the key if the
|
||||
* property is a {@link java.util.Map}.
|
||||
* Returns whether the property is a {@link Map}.
|
||||
*
|
||||
* @return the component type, the map's key type or {@literal null} if neither {@link java.util.Collection} nor {@link java.util.Map}.
|
||||
* @return
|
||||
*/
|
||||
boolean isMap();
|
||||
|
||||
/**
|
||||
* Returns whether the property is an array.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isArray();
|
||||
|
||||
/**
|
||||
* Returns whether the property is transient.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isTransient();
|
||||
|
||||
|
||||
boolean shallBePersisted();
|
||||
|
||||
/**
|
||||
* Returns whether the property is an {@link Association}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isAssociation();
|
||||
|
||||
/**
|
||||
* Returns the component type of the type if it is a {@link java.util.Collection}. Will return the type of the key if
|
||||
* the property is a {@link java.util.Map}.
|
||||
*
|
||||
* @return the component type, the map's key type or {@literal null} if neither {@link java.util.Collection} nor
|
||||
* {@link java.util.Map}.
|
||||
*/
|
||||
Class<?> getComponentType();
|
||||
|
||||
@@ -80,5 +122,4 @@ public interface PersistentProperty<P extends PersistentProperty<P>> {
|
||||
*/
|
||||
Class<?> getMapValueType();
|
||||
|
||||
boolean isIdProperty();
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
|
||||
private final ConcurrentMap<TypeInformation<?>, E> persistentEntities = new ConcurrentHashMap<TypeInformation<?>, E>();
|
||||
private final ConcurrentMap<E, List<Validator>> validators = new ConcurrentHashMap<E, List<Validator>>();
|
||||
private final List<Class<?>> customSimpleTypes = new ArrayList<Class<?>>();
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
private Set<? extends Class<?>> initialEntitySet = new HashSet<Class<?>>();
|
||||
private boolean strict = false;
|
||||
@@ -173,22 +173,23 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
|
||||
List<P> result = new ArrayList<P>();
|
||||
E current = getPersistentEntity(propertyPath.getOwningType());
|
||||
|
||||
|
||||
for (PropertyPath segment : propertyPath) {
|
||||
|
||||
|
||||
P persistentProperty = current.getPersistentProperty(segment.getSegment());
|
||||
|
||||
if (persistentProperty == null) {
|
||||
throw new IllegalArgumentException(String.format("No property %s found on %s!", segment.getSegment(), current.getName()));
|
||||
throw new IllegalArgumentException(String.format("No property %s found on %s!", segment.getSegment(),
|
||||
current.getName()));
|
||||
}
|
||||
|
||||
|
||||
result.add(persistentProperty);
|
||||
|
||||
|
||||
if (segment.hasNext()) {
|
||||
current = getPersistentEntity(segment.getType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new DefaultPersistentPropertyPath<P>(result);
|
||||
}
|
||||
|
||||
@@ -243,11 +244,12 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
descriptors.put(descriptor.getName(), descriptor);
|
||||
}
|
||||
|
||||
ReflectionUtils.doWithFields(type, new PersistentPropertyCreator(entity, descriptors), new ReflectionUtils.FieldFilter() {
|
||||
public boolean matches(Field field) {
|
||||
return !Modifier.isStatic(field.getModifiers()) && !UNMAPPED_FIELDS.contains(field.getName());
|
||||
}
|
||||
});
|
||||
ReflectionUtils.doWithFields(type, new PersistentPropertyCreator(entity, descriptors),
|
||||
new ReflectionUtils.FieldFilter() {
|
||||
public boolean matches(Field field) {
|
||||
return !Modifier.isStatic(field.getModifiers()) && !UNMAPPED_FIELDS.contains(field.getName());
|
||||
}
|
||||
});
|
||||
|
||||
entity.verify();
|
||||
|
||||
@@ -265,43 +267,6 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a potential nested type tha needs to be added when adding the given property in the course of adding a
|
||||
* {@link PersistentEntity}. Will return the property's {@link TypeInformation} directly if it is a potential entity,
|
||||
* a collections component type if it's a collection as well as the value type of a {@link Map} if it's a map
|
||||
* property.
|
||||
*
|
||||
* @param property
|
||||
* @return the TypeInformation to be added as {@link PersistentEntity} or {@literal
|
||||
|
||||
*/
|
||||
private TypeInformation<?> getNestedTypeToAdd(P property, PersistentEntity<?, P> entity) {
|
||||
|
||||
if (entity.getType().equals(property.getRawType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeInformation<?> typeInformation = property.getTypeInformation();
|
||||
|
||||
if (customSimpleTypes.contains(typeInformation.getType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (property.isEntity()) {
|
||||
return typeInformation;
|
||||
}
|
||||
|
||||
if (property.isCollection()) {
|
||||
return getTypeInformationIfNotSimpleType(getComponentTypeRecursively(typeInformation));
|
||||
}
|
||||
|
||||
if (property.isMap()) {
|
||||
return getTypeInformationIfNotSimpleType(typeInformation.getMapValueType());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private TypeInformation<?> getComponentTypeRecursively(TypeInformation<?> typeInformation) {
|
||||
TypeInformation<?> componentType = typeInformation.getComponentType();
|
||||
|
||||
@@ -312,10 +277,6 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
return componentType.isCollectionLike() ? getComponentTypeRecursively(componentType) : componentType;
|
||||
}
|
||||
|
||||
private TypeInformation<?> getTypeInformationIfNotSimpleType(TypeInformation<?> information) {
|
||||
return information == null || simpleTypeHolder.isSimpleType(information.getType()) ? null : information;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the concrete {@link PersistentEntity} instance.
|
||||
*
|
||||
@@ -336,7 +297,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
*/
|
||||
protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner,
|
||||
SimpleTypeHolder simpleTypeHolder);
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
@@ -346,14 +307,14 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
addPersistentEntity(initialEntity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link FieldCallback} to create {@link PersistentProperty} instances.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private final class PersistentPropertyCreator implements FieldCallback {
|
||||
|
||||
|
||||
private final E entity;
|
||||
private final Map<String, PropertyDescriptor> descriptors;
|
||||
|
||||
@@ -390,9 +351,12 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
entity.setIdProperty(property);
|
||||
}
|
||||
|
||||
TypeInformation<?> nestedType = getNestedTypeToAdd(property, entity);
|
||||
if (nestedType != null) {
|
||||
addPersistentEntity(nestedType);
|
||||
if (entity.getType().equals(property.getRawType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (TypeInformation<?> candidate : property.getPersistentEntityType()) {
|
||||
addPersistentEntity(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.annotation.Reference;
|
||||
@@ -86,6 +87,34 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
|
||||
return information;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.PersistentProperty#getPersistentEntityType()
|
||||
*/
|
||||
public Iterable<? extends TypeInformation<?>> getPersistentEntityType() {
|
||||
|
||||
List<TypeInformation<?>> result = new ArrayList<TypeInformation<?>>();
|
||||
|
||||
TypeInformation<?> type = getTypeInformation();
|
||||
|
||||
if (isEntity()) {
|
||||
result.add(type);
|
||||
}
|
||||
|
||||
if (type.isCollectionLike() || isMap()) {
|
||||
TypeInformation<?> nestedType = getTypeInformationIfNotSimpleType(getTypeInformation().getActualType());
|
||||
if (nestedType != null) {
|
||||
result.add(nestedType);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private TypeInformation<?> getTypeInformationIfNotSimpleType(TypeInformation<?> information) {
|
||||
return information == null || simpleTypeHolder.isSimpleType(information.getType()) ? null : information;
|
||||
}
|
||||
|
||||
public PropertyDescriptor getPropertyDescriptor() {
|
||||
return propertyDescriptor;
|
||||
}
|
||||
@@ -102,6 +131,14 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
|
||||
return Modifier.isTransient(field.getModifiers());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.PersistentProperty#shallBePersisted()
|
||||
*/
|
||||
public boolean shallBePersisted() {
|
||||
return !isTransient();
|
||||
}
|
||||
|
||||
public boolean isAssociation() {
|
||||
if (field.isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
@@ -119,8 +156,12 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
|
||||
return association;
|
||||
}
|
||||
|
||||
public boolean isCollection() {
|
||||
return Collection.class.isAssignableFrom(getType()) || isArray();
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.PersistentProperty#isCollectionLike()
|
||||
*/
|
||||
public boolean isCollectionLike() {
|
||||
return information.isCollectionLike();
|
||||
}
|
||||
|
||||
public boolean isMap() {
|
||||
@@ -134,21 +175,19 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
|
||||
return getType().isArray();
|
||||
}
|
||||
|
||||
public boolean isComplexType() {
|
||||
if (isCollection() || isArray()) {
|
||||
return !simpleTypeHolder.isSimpleType(getComponentType());
|
||||
} else {
|
||||
return !simpleTypeHolder.isSimpleType(getType());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEntity() {
|
||||
return isComplexType() && !isTransient() && !isCollection() && !isMap();
|
||||
protected boolean isEntity() {
|
||||
|
||||
boolean isComplexType = !simpleTypeHolder.isSimpleType(information.getActualType().getType());
|
||||
return isComplexType && !isTransient() && !isCollectionLike() && !isMap();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.PersistentProperty#getComponentType()
|
||||
*/
|
||||
public Class<?> getComponentType() {
|
||||
|
||||
if (!isMap() && !isCollection()) {
|
||||
if (!isMap() && !isCollectionLike()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
@@ -16,23 +21,40 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class AbstractPersistentPropertyUnitTests {
|
||||
|
||||
TypeInformation<TestClassComplex> typeInfo;
|
||||
PersistentEntity<TestClassComplex, SamplePersistentProperty> entity;
|
||||
SimpleTypeHolder typeHolder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
typeInfo = ClassTypeInformation.from(TestClassComplex.class);
|
||||
entity = new BasicPersistentEntity<TestClassComplex, SamplePersistentProperty>(typeInfo);
|
||||
typeHolder = new SimpleTypeHolder();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see DATACMNS-68
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void discoversComponentTypeCorrectly() throws Exception {
|
||||
|
||||
BasicPersistentEntity<TestClassComplex, SamplePersistentProperty> entity = new BasicPersistentEntity<TestClassComplex, SamplePersistentProperty>(
|
||||
ClassTypeInformation.from(TestClassComplex.class));
|
||||
|
||||
Field field = ReflectionUtils.findField(TestClassComplex.class, "testClassSet");
|
||||
|
||||
SamplePersistentProperty property = new SamplePersistentProperty(field, null, entity, new SimpleTypeHolder());
|
||||
SamplePersistentProperty property = new SamplePersistentProperty(field, null, entity, typeHolder);
|
||||
property.getComponentType();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsNestedEntityTypeCorrectly() {
|
||||
|
||||
Field field = ReflectionUtils.findField(TestClassComplex.class, "testClassSet");
|
||||
|
||||
SamplePersistentProperty property = new SamplePersistentProperty(field, null, entity, typeHolder);
|
||||
assertThat(property.getPersistentEntityType().iterator().hasNext(), is(false));
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class TestClassSet extends TreeSet<Object> {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user