DATADOC-109 - Refactored mapping subsystem.
Refactored BasicMappingContext into AbstractMappingContext that now only contains the general algorithm to build PersistentEntity and PersistentProperty instances. The algorithm delegates to two template methods that are responsible for instantiating concrete implementations of those classes. Extracted an AnnotationBasedPersistentProperty that now contains all annotation references so that AbstractPersistentEntity does not take annotations into account in the first places. BasicPersistentEntity now does PreferredConstructor lookup as well. Removed unnecessary methods from MappingCOntext and PersistentEntity interface. Added a verify() method to BasicPersistentEntity that AbstractMappingContext calls to verify validity of the entity. This will be used by the Mongo module for example to make sure @Document annotated entities have an @Id property. Refactored getValueAnnotation() to getSpelExpression() to not imply we're using annotations in every case. Added shortcut in TypeDiscoverer.createInfo(…) to prevent endless loop in case a type has a field of it's very same type. TypeDiscoverer now returns the plain compound type for arrays now, which means you get String for String[], String[][] and so on. Opened up signature of AbstractMappingContext.setInitialEntitySet(…) to allow effortless programatic invocation.
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mapping;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.mapping.event.MappingContextEvent;
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.PersistentProperty;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.FieldCallback;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
|
||||
/**
|
||||
* Base class to build mapping metadata and thus create instances of {@link PersistentEntity} and {@link PersistentProperty}.
|
||||
*
|
||||
* @param E the concrete {@link PersistentEntity} type the {@link MappingContext} implementation creates
|
||||
* @param P the concrete {@link PersistentProperty} type the {@link MappingContext} implementation creates
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AbstractMappingContext<E extends BasicPersistentEntity<?>, P extends PersistentProperty> implements MappingContext<E>, InitializingBean, ApplicationEventPublisherAware {
|
||||
|
||||
private static final Set<String> UNMAPPED_FIELDS = new HashSet<String>(Arrays.asList("class", "this$0"));
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
private ConcurrentMap<TypeInformation, E> persistentEntities = new ConcurrentHashMap<TypeInformation, E>();
|
||||
private ConcurrentMap<E, List<Validator>> validators = new ConcurrentHashMap<E, List<Validator>>();
|
||||
private List<Class<?>> customSimpleTypes = new ArrayList<Class<?>>();
|
||||
private Set<? extends Class<?>> initialEntitySet = new HashSet<Class<?>>();
|
||||
private boolean strict = false;
|
||||
|
||||
/**
|
||||
* @param customSimpleTypes the customSimpleTypes to set
|
||||
*/
|
||||
public void setCustomSimpleTypes(List<Class<?>> customSimpleTypes) {
|
||||
this.customSimpleTypes = customSimpleTypes;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
|
||||
*/
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Set} of types to populate the context initially.
|
||||
*
|
||||
* @param initialEntitySet
|
||||
*/
|
||||
public void setInitialEntitySet(Set<? extends Class<?>> initialEntitySet) {
|
||||
this.initialEntitySet = initialEntitySet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether the {@link MappingContext} is in strict mode which means, that it will throw
|
||||
* {@link MappingException}s in case one tries to lookup a {@link PersistentEntity} not already in the context. This
|
||||
* defaults to {@literal false} so that unknown types will be transparently added to the MappingContext if not known
|
||||
* in advance.
|
||||
*
|
||||
* @param strict
|
||||
*/
|
||||
public void setStrict(boolean strict) {
|
||||
this.strict = strict;
|
||||
}
|
||||
|
||||
public Collection<E> getPersistentEntities() {
|
||||
return persistentEntities.values();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(java.lang.Class)
|
||||
*/
|
||||
public E getPersistentEntity(Class<?> type) {
|
||||
|
||||
return getPersistentEntity(new ClassTypeInformation(type));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(org.springframework.data.util.TypeInformation)
|
||||
*/
|
||||
public E getPersistentEntity(TypeInformation type) {
|
||||
|
||||
E entity = persistentEntities.get(type);
|
||||
|
||||
if (entity != null) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
if (strict) {
|
||||
throw new MappingException("Unknown persistent entity " + type);
|
||||
}
|
||||
|
||||
return addPersistentEntity(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given type to the {@link MappingContext}.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
protected E addPersistentEntity(Class<?> type) {
|
||||
|
||||
return addPersistentEntity(new ClassTypeInformation(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given {@link TypeInformation} to the {@link MappingContext}.
|
||||
*
|
||||
* @param typeInformation
|
||||
* @return
|
||||
*/
|
||||
protected E addPersistentEntity(TypeInformation typeInformation) {
|
||||
|
||||
E persistentEntity = persistentEntities.get(typeInformation);
|
||||
|
||||
if (persistentEntity != null) {
|
||||
return persistentEntity;
|
||||
}
|
||||
|
||||
Class<?> type = typeInformation.getType();
|
||||
|
||||
try {
|
||||
final E entity = createPersistentEntity(typeInformation);
|
||||
BeanInfo info = Introspector.getBeanInfo(type);
|
||||
|
||||
final Map<String, PropertyDescriptor> descriptors = new HashMap<String, PropertyDescriptor>();
|
||||
for (PropertyDescriptor descriptor : info.getPropertyDescriptors()) {
|
||||
descriptors.put(descriptor.getName(), descriptor);
|
||||
}
|
||||
|
||||
ReflectionUtils.doWithFields(type, new FieldCallback() {
|
||||
|
||||
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||
|
||||
PropertyDescriptor descriptor = descriptors.get(field.getName());
|
||||
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
P property = createPersistentProperty(field, descriptor, entity);
|
||||
|
||||
if (property.isTransient()) {
|
||||
return;
|
||||
}
|
||||
|
||||
entity.addPersistentProperty(property);
|
||||
|
||||
if (property.isAssociation()) {
|
||||
entity.addAssociation(property.getAssociation());
|
||||
}
|
||||
|
||||
if (property.isIdProperty()) {
|
||||
entity.setIdProperty(property);
|
||||
}
|
||||
|
||||
TypeInformation nestedType = getNestedTypeToAdd(property, entity);
|
||||
if (nestedType != null) {
|
||||
addPersistentEntity(nestedType);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}, new ReflectionUtils.FieldFilter() {
|
||||
public boolean matches(Field field) {
|
||||
return !Modifier.isStatic(field.getModifiers()) && !UNMAPPED_FIELDS.contains(field.getName());
|
||||
}
|
||||
});
|
||||
|
||||
entity.verify();
|
||||
|
||||
// Inform listeners
|
||||
if (null != applicationEventPublisher) {
|
||||
applicationEventPublisher.publishEvent(new MappingContextEvent(entity, typeInformation));
|
||||
}
|
||||
|
||||
// Cache
|
||||
persistentEntities.put(entity.getPropertyInformation(), (E) entity);
|
||||
|
||||
return entity;
|
||||
} catch (IntrospectionException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(PersistentProperty property, PersistentEntity<?> 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(typeInformation.getComponentType());
|
||||
}
|
||||
|
||||
if (property.isMap()) {
|
||||
return getTypeInformationIfNotSimpleType(typeInformation.getMapValueType());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private TypeInformation getTypeInformationIfNotSimpleType(TypeInformation information) {
|
||||
return information == null || MappingBeanHelper.isSimpleType(information.getType()) ? null : information;
|
||||
}
|
||||
|
||||
public List<Validator> getEntityValidators(E entity) {
|
||||
return validators.get(entity);
|
||||
}
|
||||
|
||||
protected abstract E createPersistentEntity(TypeInformation typeInformation);
|
||||
|
||||
protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner);
|
||||
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
for (Class<?> initialEntity : initialEntitySet) {
|
||||
addPersistentEntity(initialEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,13 @@
|
||||
package org.springframework.data.mapping;
|
||||
|
||||
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.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.annotation.Reference;
|
||||
import org.springframework.data.mapping.model.Association;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.PersistentProperty;
|
||||
@@ -37,45 +35,30 @@ import org.springframework.data.util.TypeInformation;
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class BasicPersistentProperty implements PersistentProperty {
|
||||
public abstract class AbstractPersistentProperty implements PersistentProperty {
|
||||
|
||||
protected final String name;
|
||||
protected final PropertyDescriptor propertyDescriptor;
|
||||
protected final TypeInformation information;
|
||||
protected final Class<?> rawType;
|
||||
protected final Field field;
|
||||
protected Association association;
|
||||
protected Value value;
|
||||
protected boolean isTransient = false;
|
||||
protected PersistentEntity<?> owner;
|
||||
protected final Association association;
|
||||
protected final PersistentEntity<?> owner;
|
||||
|
||||
public BasicPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, TypeInformation information) {
|
||||
public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity<?> owner) {
|
||||
this.name = field.getName();
|
||||
this.rawType = field.getType();
|
||||
this.information = information.getProperty(this.name);
|
||||
this.information = owner.getPropertyInformation().getProperty(this.name);
|
||||
this.propertyDescriptor = propertyDescriptor;
|
||||
this.field = field;
|
||||
this.isTransient = Modifier.isTransient(field.getModifiers()) || field.isAnnotationPresent(Transient.class);
|
||||
if (field.isAnnotationPresent(Value.class)) {
|
||||
this.value = field.getAnnotation(Value.class);
|
||||
// Fields with @Value annotations are considered the same as transient fields
|
||||
this.isTransient = true;
|
||||
}
|
||||
if (field.isAnnotationPresent(Autowired.class)) {
|
||||
this.isTransient = true;
|
||||
}
|
||||
this.association = isAssociation() ? new Association(this, null) : null;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public Object getOwner() {
|
||||
public PersistentEntity<?> getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(Object owner) {
|
||||
if (null != owner && owner.getClass().isAssignableFrom(PersistentEntity.class)) {
|
||||
this.owner = (PersistentEntity<?>) owner;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -100,26 +83,31 @@ public class BasicPersistentProperty implements PersistentProperty {
|
||||
return field;
|
||||
}
|
||||
|
||||
public Value getValueAnnotation() {
|
||||
return value;
|
||||
public String getSpelExpression() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isTransient() {
|
||||
return isTransient;
|
||||
return Modifier.isTransient(field.getModifiers());
|
||||
}
|
||||
|
||||
public boolean isAssociation() {
|
||||
return null != association;
|
||||
if (field.isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
for (Annotation annotation : field.getDeclaredAnnotations()) {
|
||||
if (annotation.annotationType().isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Association getAssociation() {
|
||||
return association;
|
||||
}
|
||||
|
||||
public void setAssociation(Association association) {
|
||||
this.association = association;
|
||||
}
|
||||
|
||||
public boolean isCollection() {
|
||||
return Collection.class.isAssignableFrom(getType()) || isArray();
|
||||
}
|
||||
@@ -157,8 +145,4 @@ public class BasicPersistentProperty implements PersistentProperty {
|
||||
public Class<?> getMapValueType() {
|
||||
return isMap() ? information.getMapValueType().getType() : null;
|
||||
}
|
||||
|
||||
public boolean isIdProperty() {
|
||||
return field.isAnnotationPresent(Id.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mapping;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Reference;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.mapping.model.Association;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.PersistentProperty;
|
||||
|
||||
|
||||
/**
|
||||
* Special {@link PersistentProperty} that takes annotations at a property into account.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class AnnotationBasedPersistentProperty extends AbstractPersistentProperty {
|
||||
|
||||
private final Value value;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AnnotationBasedPersistentProperty}.
|
||||
*
|
||||
* @param field
|
||||
* @param propertyDescriptor
|
||||
* @param owner
|
||||
*/
|
||||
public AnnotationBasedPersistentProperty(Field field,
|
||||
PropertyDescriptor propertyDescriptor, PersistentEntity<?> owner) {
|
||||
|
||||
super(field, propertyDescriptor, owner);
|
||||
this.value = field.getAnnotation(Value.class);
|
||||
field.isAnnotationPresent(Autowired.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects a potentially available {@link Value} annotation at the property and returns the {@link String} value of
|
||||
* it.
|
||||
*
|
||||
* @see org.springframework.data.mapping.AbstractPersistentProperty#getSpelExpression()
|
||||
*/
|
||||
public String getSpelExpression() {
|
||||
return value == null ? null :value.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Considers plain transient fields, fields annotated with {@link Transient}, {@link Value} or {@link Autowired} as
|
||||
* transien.
|
||||
*
|
||||
* @see org.springframework.data.mapping.BasicPersistentProperty#isTransient()
|
||||
*/
|
||||
public boolean isTransient() {
|
||||
|
||||
boolean isTransient = super.isTransient() || field.isAnnotationPresent(Transient.class);
|
||||
|
||||
return isTransient || field.isAnnotationPresent(Value.class) || field.isAnnotationPresent(Autowired.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regards the property as ID if there is an {@link Id} annotation found on it.
|
||||
*/
|
||||
public boolean isIdProperty() {
|
||||
return field.isAnnotationPresent(Id.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Considers the property an {@link Association} if it is annotated with {@link Reference}.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAssociation() {
|
||||
|
||||
if (isTransient()) {
|
||||
return false;
|
||||
}
|
||||
if (field.isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: do we need this? Shouldn't the section above already find that annotation?
|
||||
for (Annotation annotation : field.getDeclaredAnnotations()) {
|
||||
if (annotation.annotationType().isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -13,394 +13,45 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mapping;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
import org.springframework.data.annotation.Reference;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.mapping.event.MappingContextEvent;
|
||||
import org.springframework.data.mapping.model.Association;
|
||||
import org.springframework.data.mapping.model.MappingConfigurationException;
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.PersistentProperty;
|
||||
import org.springframework.data.mapping.model.PreferredConstructor;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.FieldCallback;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link MappingContext} creating {@link BasicPersistentEntity} and
|
||||
* {@link AnnotationBasedPersistentProperty} instances.
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class BasicMappingContext implements MappingContext, InitializingBean, ApplicationEventPublisherAware {
|
||||
public class BasicMappingContext extends AbstractMappingContext<BasicPersistentEntity<?>, PersistentProperty> {
|
||||
|
||||
private static final Set<String> UNMAPPED_FIELDS = new HashSet<String>(Arrays.asList("class", "this$0"));
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected BasicPersistentEntity<?> createPersistentEntity(
|
||||
TypeInformation typeInformation) {
|
||||
|
||||
protected Log log = LogFactory.getLog(getClass());
|
||||
protected ApplicationEventPublisher applicationEventPublisher;
|
||||
protected ConcurrentMap<TypeInformation, PersistentEntity<?>> persistentEntities = new ConcurrentHashMap<TypeInformation, PersistentEntity<?>>();
|
||||
protected ConcurrentMap<PersistentEntity<?>, List<Validator>> validators = new ConcurrentHashMap<PersistentEntity<?>, List<Validator>>();
|
||||
protected final GenericConversionService conversionService;
|
||||
private List<Class<?>> customSimpleTypes = new ArrayList<Class<?>>();
|
||||
private Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
|
||||
return new BasicPersistentEntity(typeInformation);
|
||||
}
|
||||
|
||||
public BasicMappingContext() {
|
||||
this(ConversionServiceFactory.createDefaultConversionService());
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.util.TypeInformation, org.springframework.data.mapping.BasicPersistentEntity)
|
||||
*/
|
||||
@Override
|
||||
protected PersistentProperty createPersistentProperty(Field field,
|
||||
PropertyDescriptor descriptor,
|
||||
BasicPersistentEntity<?> owner) {
|
||||
|
||||
public BasicMappingContext(GenericConversionService conversionService) {
|
||||
Assert.notNull(conversionService);
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param customSimpleTypes the customSimpleTypes to set
|
||||
*/
|
||||
public void setCustomSimpleTypes(List<Class<?>> customSimpleTypes) {
|
||||
this.customSimpleTypes = customSimpleTypes;
|
||||
}
|
||||
|
||||
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
public void setInitialEntitySet(Set<Class<?>> initialEntitySet) {
|
||||
this.initialEntitySet = initialEntitySet;
|
||||
}
|
||||
|
||||
public Collection<? extends PersistentEntity<?>> getPersistentEntities() {
|
||||
return persistentEntities.values();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(java.lang.Class)
|
||||
*/
|
||||
public <T> PersistentEntity<T> getPersistentEntity(Class<T> type) {
|
||||
return getPersistentEntity(new ClassTypeInformation(type));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public <T> PersistentEntity<T> getPersistentEntity(TypeInformation type) {
|
||||
return (PersistentEntity<T>) persistentEntities.get(type);
|
||||
}
|
||||
|
||||
public <T> PersistentEntity<T> addPersistentEntity(Class<T> type) {
|
||||
return addPersistentEntity(new ClassTypeInformation(type));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> PersistentEntity<T> addPersistentEntity(TypeInformation typeInformation) {
|
||||
|
||||
PersistentEntity<?> persistentEntity = persistentEntities.get(typeInformation);
|
||||
|
||||
if (persistentEntity != null) {
|
||||
return (PersistentEntity<T>) persistentEntity;
|
||||
}
|
||||
|
||||
Class<T> type = (Class<T>) typeInformation.getType();
|
||||
|
||||
try {
|
||||
final BasicPersistentEntity<T> entity = createPersistentEntity(typeInformation, this);
|
||||
BeanInfo info = Introspector.getBeanInfo(type);
|
||||
|
||||
final Map<String, PropertyDescriptor> descriptors = new HashMap<String, PropertyDescriptor>();
|
||||
for (PropertyDescriptor descriptor : info.getPropertyDescriptors()) {
|
||||
descriptors.put(descriptor.getName(), descriptor);
|
||||
}
|
||||
|
||||
ReflectionUtils.doWithFields(type, new FieldCallback() {
|
||||
|
||||
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||
try {
|
||||
PropertyDescriptor descriptor = descriptors.get(field.getName());
|
||||
if (isPersistentProperty(field, descriptor)) {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
BasicPersistentProperty property = createPersistentProperty(field, descriptor, entity.getPropertyInformation());
|
||||
property.setOwner(entity);
|
||||
entity.addPersistentProperty(property);
|
||||
if (isAssociation(field, descriptor)) {
|
||||
Association association = createAssociation(property);
|
||||
entity.addAssociation(association);
|
||||
}
|
||||
|
||||
if (property.isIdProperty()) {
|
||||
entity.setIdProperty(property);
|
||||
}
|
||||
|
||||
TypeInformation nestedType = getNestedTypeToAdd(property);
|
||||
if (nestedType != null) {
|
||||
addPersistentEntity(nestedType);
|
||||
}
|
||||
}
|
||||
} catch (MappingConfigurationException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}, new ReflectionUtils.FieldFilter() {
|
||||
public boolean matches(Field field) {
|
||||
return !Modifier.isStatic(field.getModifiers());
|
||||
}
|
||||
});
|
||||
|
||||
entity.setPreferredConstructor(getPreferredConstructor(type));
|
||||
|
||||
// Inform listeners
|
||||
if (null != applicationEventPublisher) {
|
||||
applicationEventPublisher.publishEvent(new MappingContextEvent(entity, typeInformation));
|
||||
}
|
||||
|
||||
// Cache
|
||||
persistentEntities.put(entity.getPropertyInformation(), entity);
|
||||
|
||||
return entity;
|
||||
} catch (MappingConfigurationException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} catch (IntrospectionException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(PersistentProperty property) {
|
||||
|
||||
TypeInformation typeInformation = property.getTypeInformation();
|
||||
|
||||
if (customSimpleTypes.contains(typeInformation.getType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (property.isEntity()) {
|
||||
return typeInformation;
|
||||
}
|
||||
|
||||
if (property.isCollection()) {
|
||||
return getTypeInformationIfNotSimpleType(typeInformation.getComponentType());
|
||||
}
|
||||
|
||||
if (property.isMap()) {
|
||||
return getTypeInformationIfNotSimpleType(typeInformation.getMapValueType());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private TypeInformation getTypeInformationIfNotSimpleType(TypeInformation information) {
|
||||
return information == null || MappingBeanHelper.isSimpleType(information.getType()) ? null : information;
|
||||
}
|
||||
|
||||
public List<Validator> getEntityValidators(PersistentEntity<?> entity) {
|
||||
return validators.get(entity);
|
||||
}
|
||||
|
||||
public boolean isPersistentEntity(Object value) {
|
||||
if (null != value) {
|
||||
Class<?> clazz;
|
||||
if (value instanceof Class) {
|
||||
clazz = ((Class<?>) value);
|
||||
} else {
|
||||
clazz = value.getClass();
|
||||
}
|
||||
return isPersistentEntity(clazz);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isPersistentEntity(Class<?> type) {
|
||||
if (type.isAnnotationPresent(Persistent.class)) {
|
||||
return true;
|
||||
} else {
|
||||
for (Annotation annotation : type.getDeclaredAnnotations()) {
|
||||
if (annotation.annotationType().isAnnotationPresent(Persistent.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (Field field : type.getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(Id.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected <T> BasicPersistentEntity<T> createPersistentEntity(TypeInformation typeInformation, MappingContext mappingContext)
|
||||
throws MappingConfigurationException {
|
||||
return new BasicPersistentEntity<T>(mappingContext, typeInformation);
|
||||
}
|
||||
|
||||
protected BasicPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
|
||||
TypeInformation information) throws MappingConfigurationException {
|
||||
return new BasicPersistentProperty(field, descriptor, information);
|
||||
}
|
||||
|
||||
public boolean isPersistentProperty(Field field, PropertyDescriptor descriptor) throws MappingConfigurationException {
|
||||
if (UNMAPPED_FIELDS.contains(field.getName()) || isTransient(field)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public <T> PreferredConstructor<T> getPreferredConstructor(Class<T> type) throws MappingConfigurationException {
|
||||
// Find the right constructor
|
||||
PreferredConstructor<T> preferredConstructor = null;
|
||||
|
||||
for (Constructor<?> constructor : type.getDeclaredConstructors()) {
|
||||
if (constructor.getParameterTypes().length != 0) {
|
||||
// Non-no-arg constructor
|
||||
if (null == preferredConstructor || constructor.isAnnotationPresent(PersistenceConstructor.class)) {
|
||||
preferredConstructor = new PreferredConstructor<T>((Constructor<T>) constructor);
|
||||
|
||||
String[] paramNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(constructor);
|
||||
Type[] paramTypes = constructor.getGenericParameterTypes();
|
||||
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
Class<?> targetType = Object.class;
|
||||
Class<?> rawType = constructor.getParameterTypes()[i];
|
||||
if (paramTypes[i] instanceof ParameterizedType) {
|
||||
ParameterizedType ptype = (ParameterizedType) paramTypes[i];
|
||||
targetType = getTargetType(ptype);
|
||||
} else {
|
||||
if (paramTypes[i] instanceof TypeVariable) {
|
||||
targetType = getTargetType((TypeVariable) paramTypes[i]);
|
||||
} else if (paramTypes[i] instanceof Class<?>) {
|
||||
targetType = (Class<?>) paramTypes[i];
|
||||
}
|
||||
}
|
||||
String paramName = (null != paramNames ? paramNames[i] : "param" + i);
|
||||
preferredConstructor.addParameter(paramName, targetType, rawType, targetType.getDeclaredAnnotations());
|
||||
}
|
||||
|
||||
if (constructor.isAnnotationPresent(PersistenceConstructor.class)) {
|
||||
// We're done
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return preferredConstructor;
|
||||
}
|
||||
|
||||
public boolean isAssociation(Field field, PropertyDescriptor descriptor) throws MappingConfigurationException {
|
||||
if (!isTransient(field)) {
|
||||
if (field.isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
for (Annotation annotation : field.getDeclaredAnnotations()) {
|
||||
if (annotation.annotationType().isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Association createAssociation(BasicPersistentProperty property) {
|
||||
// Only support uni-directional associations in the Basic configuration
|
||||
Association association = new Association(property, null);
|
||||
property.setAssociation(association);
|
||||
|
||||
return association;
|
||||
}
|
||||
|
||||
protected boolean isTransient(Field field) {
|
||||
if (Modifier.isTransient(field.getModifiers())
|
||||
|| null != field.getAnnotation(Transient.class)
|
||||
|| null != field.getAnnotation(Value.class)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected Class<?> getTargetType(TypeVariable<?> tv) {
|
||||
Class<?> targetType = Object.class;
|
||||
Type[] bounds = tv.getBounds();
|
||||
if (bounds.length > 0) {
|
||||
if (bounds[0] instanceof ParameterizedType) {
|
||||
return getTargetType((ParameterizedType) bounds[0]);
|
||||
} else if (bounds[0] instanceof TypeVariable) {
|
||||
return getTargetType((TypeVariable<?>) bounds[0]);
|
||||
} else {
|
||||
targetType = (Class<?>) bounds[0];
|
||||
}
|
||||
}
|
||||
return targetType;
|
||||
}
|
||||
|
||||
protected Class<?> getTargetType(ParameterizedType ptype) {
|
||||
Class<?> targetType = Object.class;
|
||||
Type[] types = ptype.getActualTypeArguments();
|
||||
if (types.length == 1) {
|
||||
if (types[0] instanceof TypeVariable) {
|
||||
// Placeholder type
|
||||
targetType = Object.class;
|
||||
} else {
|
||||
if (types[0] instanceof ParameterizedType) {
|
||||
return getTargetType((ParameterizedType) types[0]);
|
||||
} else {
|
||||
targetType = (Class<?>) types[0];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
targetType = (Class<?>) ptype.getRawType();
|
||||
}
|
||||
return targetType;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
for (Class<?> initialEntity : initialEntitySet) {
|
||||
addPersistentEntity(initialEntity);
|
||||
}
|
||||
}
|
||||
return new AnnotationBasedPersistentProperty(field, descriptor, owner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,50 +13,132 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mapping;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.mapping.model.Association;
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.PersistentProperty;
|
||||
import org.springframework.data.mapping.model.PreferredConstructor;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* Simple value object to capture information of {@link PersistentEntity}s.
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class BasicPersistentEntity<T> implements PersistentEntity<T> {
|
||||
|
||||
protected final Class<T> type;
|
||||
protected PreferredConstructor<T> preferredConstructor;
|
||||
protected PersistentProperty idProperty;
|
||||
protected Map<String, PersistentProperty> persistentProperties = new HashMap<String, PersistentProperty>();
|
||||
protected Map<String, Association> associations = new HashMap<String, Association>();
|
||||
protected final PreferredConstructor<T> preferredConstructor;
|
||||
protected final TypeInformation information;
|
||||
|
||||
protected MappingContext mappingContext;
|
||||
protected final Map<String, PersistentProperty> persistentProperties = new HashMap<String, PersistentProperty>();
|
||||
protected final Map<String, Association> associations = new HashMap<String, Association>();
|
||||
protected PersistentProperty idProperty;
|
||||
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public BasicPersistentEntity(MappingContext mappingContext, TypeInformation information) {
|
||||
this.mappingContext = mappingContext;
|
||||
/**
|
||||
* Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}.
|
||||
*
|
||||
* @param information
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public BasicPersistentEntity(TypeInformation information) {
|
||||
this.type = (Class<T>) information.getType();
|
||||
this.information = information;
|
||||
this.preferredConstructor = getPreferredConstructor(type);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private final PreferredConstructor<T> getPreferredConstructor(Class<T> type) {
|
||||
// Find the right constructor
|
||||
PreferredConstructor<T> preferredConstructor = null;
|
||||
|
||||
for (Constructor<?> constructor : type.getDeclaredConstructors()) {
|
||||
if (constructor.getParameterTypes().length != 0) {
|
||||
// Non-no-arg constructor
|
||||
if (null == preferredConstructor || constructor.isAnnotationPresent(PersistenceConstructor.class)) {
|
||||
preferredConstructor = new PreferredConstructor<T>((Constructor<T>) constructor);
|
||||
|
||||
String[] paramNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(constructor);
|
||||
Type[] paramTypes = constructor.getGenericParameterTypes();
|
||||
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
Class<?> targetType = Object.class;
|
||||
Class<?> rawType = constructor.getParameterTypes()[i];
|
||||
if (paramTypes[i] instanceof ParameterizedType) {
|
||||
ParameterizedType ptype = (ParameterizedType) paramTypes[i];
|
||||
targetType = getTargetType(ptype);
|
||||
} else {
|
||||
if (paramTypes[i] instanceof TypeVariable) {
|
||||
targetType = getTargetType((TypeVariable) paramTypes[i]);
|
||||
} else if (paramTypes[i] instanceof Class<?>) {
|
||||
targetType = (Class<?>) paramTypes[i];
|
||||
}
|
||||
}
|
||||
String paramName = (null != paramNames ? paramNames[i] : "param" + i);
|
||||
preferredConstructor.addParameter(paramName, targetType, rawType, targetType.getDeclaredAnnotations());
|
||||
}
|
||||
|
||||
if (constructor.isAnnotationPresent(PersistenceConstructor.class)) {
|
||||
// We're done
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return preferredConstructor;
|
||||
}
|
||||
|
||||
private Class<?> getTargetType(TypeVariable<?> tv) {
|
||||
Class<?> targetType = Object.class;
|
||||
Type[] bounds = tv.getBounds();
|
||||
if (bounds.length > 0) {
|
||||
if (bounds[0] instanceof ParameterizedType) {
|
||||
return getTargetType((ParameterizedType) bounds[0]);
|
||||
} else if (bounds[0] instanceof TypeVariable) {
|
||||
return getTargetType((TypeVariable<?>) bounds[0]);
|
||||
} else {
|
||||
targetType = (Class<?>) bounds[0];
|
||||
}
|
||||
}
|
||||
return targetType;
|
||||
}
|
||||
|
||||
private Class<?> getTargetType(ParameterizedType ptype) {
|
||||
Class<?> targetType = Object.class;
|
||||
Type[] types = ptype.getActualTypeArguments();
|
||||
if (types.length == 1) {
|
||||
if (types[0] instanceof TypeVariable) {
|
||||
// Placeholder type
|
||||
targetType = Object.class;
|
||||
} else {
|
||||
if (types[0] instanceof ParameterizedType) {
|
||||
return getTargetType((ParameterizedType) types[0]);
|
||||
} else {
|
||||
targetType = (Class<?>) types[0];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
targetType = (Class<?>) ptype.getRawType();
|
||||
}
|
||||
return targetType;
|
||||
}
|
||||
|
||||
public PreferredConstructor<T> getPreferredConstructor() {
|
||||
return preferredConstructor;
|
||||
}
|
||||
|
||||
public void setPreferredConstructor(PreferredConstructor<T> constructor) {
|
||||
this.preferredConstructor = constructor;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return type.getName();
|
||||
}
|
||||
@@ -101,14 +183,6 @@ public class BasicPersistentEntity<T> implements PersistentEntity<T> {
|
||||
return persistentProperties.keySet();
|
||||
}
|
||||
|
||||
public MappingContext getMappingContext() {
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
public void doWithProperties(PropertyHandler handler) {
|
||||
for (PersistentProperty property : persistentProperties.values()) {
|
||||
if (!property.isTransient() && !property.isAssociation() && !property.isIdProperty()) {
|
||||
@@ -122,4 +196,12 @@ public class BasicPersistentEntity<T> implements PersistentEntity<T> {
|
||||
handler.doWithAssociation(association);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method to trigger validation of the {@link PersistentEntity}. As {@link BasicPersistentEntity} is not
|
||||
* immutable there might be some verification steps necessary after the object has reached is final state.
|
||||
*/
|
||||
public void verify() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,15 +106,12 @@ public abstract class MappingBeanHelper {
|
||||
return type.isEnum();
|
||||
}
|
||||
|
||||
public static <T> T constructInstance(PersistentEntity<T> entity,
|
||||
PreferredConstructor.ParameterValueProvider provider) {
|
||||
public static <T> T constructInstance(PersistentEntity<T> entity, PreferredConstructor.ParameterValueProvider provider) {
|
||||
return constructInstance(entity, provider, new StandardEvaluationContext());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public static <T> T constructInstance(PersistentEntity<T> entity,
|
||||
PreferredConstructor.ParameterValueProvider provider,
|
||||
EvaluationContext spelCtx) {
|
||||
public static <T> T constructInstance(PersistentEntity<T> entity, PreferredConstructor.ParameterValueProvider provider, EvaluationContext spelCtx) {
|
||||
|
||||
PreferredConstructor<T> constructor = entity.getPreferredConstructor();
|
||||
if (null == constructor) {
|
||||
|
||||
@@ -30,6 +30,6 @@ public interface MappingContextAware {
|
||||
*
|
||||
* @param mappingContext
|
||||
*/
|
||||
void setMappingContext(MappingContext mappingContext);
|
||||
void setMappingContext(MappingContext<?> mappingContext);
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.data.mapping.model;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
@@ -36,41 +35,25 @@ import org.springframework.validation.Validator;
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface MappingContext extends InitializingBean {
|
||||
public interface MappingContext<E extends PersistentEntity<?>> {
|
||||
|
||||
/**
|
||||
* Adds a PersistentEntity instance
|
||||
*
|
||||
* @param type The Java class representing the entity
|
||||
* @return The PersistentEntity instance
|
||||
*/
|
||||
<T> PersistentEntity<T> addPersistentEntity(Class<T> type);
|
||||
|
||||
/**
|
||||
* Obtains a list of PersistentEntity instances
|
||||
*
|
||||
* @return A list of PersistentEntity instances
|
||||
*/
|
||||
Collection<? extends PersistentEntity<?>> getPersistentEntities();
|
||||
Collection<E> getPersistentEntities();
|
||||
|
||||
<T> PersistentEntity<T> getPersistentEntity(Class<T> type);
|
||||
E getPersistentEntity(Class<?> type);
|
||||
|
||||
<T> PersistentEntity<T> getPersistentEntity(TypeInformation type);
|
||||
E getPersistentEntity(TypeInformation type);
|
||||
|
||||
/**
|
||||
* Obtains a validator for the given entity
|
||||
* TODO: Why do we need validators at the {@link MappingContext}?
|
||||
*
|
||||
* @param entity The entity
|
||||
* @return A validator or null if none exists for the given entity
|
||||
*/
|
||||
List<Validator> getEntityValidators(PersistentEntity<?> entity);
|
||||
|
||||
/**
|
||||
* Returns whether the specified value is a persistent entity
|
||||
*
|
||||
* @param value The value to check
|
||||
* @return True if it is
|
||||
*/
|
||||
boolean isPersistentEntity(Object value);
|
||||
|
||||
List<Validator> getEntityValidators(E entity);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
@@ -15,7 +14,7 @@ import java.util.Collection;
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface PersistentEntity<T> extends InitializingBean {
|
||||
public interface PersistentEntity<T> {
|
||||
|
||||
/**
|
||||
* The entity name including any package prefix
|
||||
@@ -26,11 +25,14 @@ public interface PersistentEntity<T> extends InitializingBean {
|
||||
|
||||
PreferredConstructor<T> getPreferredConstructor();
|
||||
|
||||
/**
|
||||
* Returns the identity of the instance
|
||||
*
|
||||
* @return The identity
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the id property of the {@link PersistentEntity}. Must never
|
||||
* return {@literal null} as a {@link PersistentEntity} instance must not be
|
||||
* created if there is no id property.
|
||||
*
|
||||
* @return the id property of the {@link PersistentEntity}.
|
||||
*/
|
||||
PersistentProperty getIdProperty();
|
||||
|
||||
/**
|
||||
@@ -69,15 +71,7 @@ public interface PersistentEntity<T> extends InitializingBean {
|
||||
*/
|
||||
Collection<String> getPersistentPropertyNames();
|
||||
|
||||
/**
|
||||
* Obtains the MappingContext where this PersistentEntity is defined
|
||||
*
|
||||
* @return The MappingContext instance
|
||||
*/
|
||||
MappingContext getMappingContext();
|
||||
|
||||
void doWithProperties(PropertyHandler handler);
|
||||
|
||||
void doWithAssociations(AssociationHandler handler);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.springframework.data.util.TypeInformation;
|
||||
*/
|
||||
public interface PersistentProperty {
|
||||
|
||||
Object getOwner();
|
||||
PersistentEntity<?> getOwner();
|
||||
|
||||
/**
|
||||
* The name of the property
|
||||
@@ -37,7 +37,7 @@ public interface PersistentProperty {
|
||||
|
||||
Field getField();
|
||||
|
||||
Value getValueAnnotation();
|
||||
String getSpelExpression();
|
||||
|
||||
boolean isTransient();
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Property information for a plain {@link Class}.
|
||||
*
|
||||
@@ -50,12 +52,18 @@ public class ClassTypeInformation extends TypeDiscoverer {
|
||||
public TypeInformation getComponentType() {
|
||||
|
||||
if (type.isArray()) {
|
||||
return createInfo(type.getComponentType());
|
||||
return createInfo(resolveArrayType(type));
|
||||
}
|
||||
|
||||
TypeVariable<?>[] typeParameters = type.getTypeParameters();
|
||||
return typeParameters.length > 0 ? new TypeVariableTypeInformation(typeParameters[0], this.getType(), this) : null;
|
||||
}
|
||||
|
||||
private static Type resolveArrayType(Class<?> type) {
|
||||
Assert.isTrue(type.isArray());
|
||||
Class<?> componentType = type.getComponentType();
|
||||
return componentType.isArray() ? resolveArrayType(componentType) : componentType;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#equals(java.lang.Object)
|
||||
|
||||
@@ -62,6 +62,10 @@ class TypeDiscoverer implements TypeInformation {
|
||||
* @return
|
||||
*/
|
||||
protected TypeInformation createInfo(Type fieldType) {
|
||||
|
||||
if (fieldType.equals(this.type)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (fieldType instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) fieldType;
|
||||
|
||||
@@ -19,12 +19,14 @@ package org.springframework.data.mapping;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.model.Association;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
@@ -40,43 +42,21 @@ public class MappingMetadataTests {
|
||||
ctx = new BasicMappingContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesRecognizePersistentEntities() {
|
||||
boolean persistentEntity = ctx.isPersistentEntity(PersonNoId.class);
|
||||
assertFalse(persistentEntity);
|
||||
|
||||
persistentEntity = ctx.isPersistentEntity(PersonPersistent.class);
|
||||
assertTrue(persistentEntity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPojoWithId() {
|
||||
PersistentEntity<PersonWithId> person = ctx.addPersistentEntity(PersonWithId.class);
|
||||
PersistentEntity<?> person = ctx.addPersistentEntity(PersonWithId.class);
|
||||
assertNotNull(person.getIdProperty());
|
||||
assertEquals(String.class, person.getIdProperty().getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPojoWithoutId() {
|
||||
PersistentEntity<PersonNoId> person = ctx.addPersistentEntity(PersonNoId.class);
|
||||
assertNull(person.getIdProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssociations() {
|
||||
PersistentEntity<PersonWithChildren> person = ctx.addPersistentEntity(PersonWithChildren.class);
|
||||
PersistentEntity<?> person = ctx.addPersistentEntity(PersonWithChildren.class);
|
||||
assertNotNull(person.getAssociations());
|
||||
|
||||
for (Association association : person.getAssociations()) {
|
||||
assertEquals(Child.class, association.getInverse().getComponentType());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesRecognizeDocumentEntities() {
|
||||
// PersistentEntity<PersonDocument> person = ctx.addPersistentEntity(PersonDocument.class);
|
||||
boolean persistentEntity = ctx.isPersistentEntity(PersonDocument.class);
|
||||
assertTrue(persistentEntity);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package org.springframework.data.mapping;
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Document
|
||||
public class PersonNoId extends Person {
|
||||
|
||||
public PersonNoId(Integer ssn, String firstName, String lastName) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.data.mapping;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Reference;
|
||||
|
||||
import java.util.List;
|
||||
@@ -25,6 +26,9 @@ import java.util.List;
|
||||
*/
|
||||
public class PersonWithChildren extends Person {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@Reference
|
||||
private List<Child> children;
|
||||
|
||||
|
||||
@@ -152,7 +152,6 @@ public class DomainClassPropertyEditorUnitTests {
|
||||
*
|
||||
* @see org.springframework.data.domain.Persistable#getId()
|
||||
*/
|
||||
@Override
|
||||
public Integer getId() {
|
||||
|
||||
return id;
|
||||
@@ -164,7 +163,6 @@ public class DomainClassPropertyEditorUnitTests {
|
||||
*
|
||||
* @see org.springframework.data.domain.Persistable#isNew()
|
||||
*/
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
|
||||
return getId() != null;
|
||||
|
||||
@@ -7,6 +7,7 @@ Changes in version 1.0.1
|
||||
General
|
||||
* Extracted Querydsl support code from JPA and Mongo modules (DATACMNS-32)
|
||||
* Implementations of Page, Pageable and Sort are now serializable (DATACMNS-30)
|
||||
* Refactored mapping subsystem (DATADOC-109)
|
||||
|
||||
Changes in version 1.0.0 (2011-04-18)
|
||||
----------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user