DATAMONGO-383 - Adapt new EntityInstantiator API of Spring Data Commons.
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
<org.springframework.version.30>3.0.7.RELEASE</org.springframework.version.30>
|
||||
<org.springframework.version.40>4.0.0.RELEASE</org.springframework.version.40>
|
||||
<org.springframework.version.range>[${org.springframework.version.30}, ${org.springframework.version.40})</org.springframework.version.range>
|
||||
<data.commons.version>1.2.0.RELEASE</data.commons.version>
|
||||
<data.commons.version>1.3.0.BUILD-SNAPSHOT</data.commons.version>
|
||||
<aspectj.version>1.6.11.RELEASE</aspectj.version>
|
||||
</properties>
|
||||
<profiles>
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.convert.EntityInstantiators;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToObjectIdConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToBigIntegerConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToStringConverter;
|
||||
@@ -39,6 +40,7 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
|
||||
|
||||
protected final GenericConversionService conversionService;
|
||||
protected CustomConversions conversions = new CustomConversions();
|
||||
protected EntityInstantiators instantiators = new EntityInstantiators();
|
||||
|
||||
/**
|
||||
* Creates a new {@link AbstractMongoConverter} using the given {@link GenericConversionService}.
|
||||
@@ -60,6 +62,15 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
|
||||
this.conversions = conversions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers {@link EntityInstantiators} to customize entity instantiation.
|
||||
*
|
||||
* @param instantiators
|
||||
*/
|
||||
public void setInstantiators(EntityInstantiators instantiators) {
|
||||
this.instantiators = instantiators == null ? new EntityInstantiators() : instantiators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers additional converters that will be available when using the {@link ConversionService} directly (e.g. for
|
||||
* id conversion). These converters are not custom conversions as they'd introduce unwanted conversions (e.g.
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* 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.mongodb.core.convert;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PreferredConstructor;
|
||||
import org.springframework.data.mapping.PreferredConstructor.Parameter;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstraction for a {@link PreferredConstructor} alongside mapping information.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class MappedConstructor {
|
||||
|
||||
private final Set<MappedConstructor.MappedParameter> parameters;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappedConstructor} from the given {@link MongoPersistentEntity} and {@link MappingContext}.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param context must not be {@literal null}.
|
||||
*/
|
||||
public MappedConstructor(MongoPersistentEntity<?> entity,
|
||||
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context) {
|
||||
|
||||
Assert.notNull(entity);
|
||||
Assert.notNull(context);
|
||||
|
||||
this.parameters = new HashSet<MappedConstructor.MappedParameter>();
|
||||
|
||||
for (Parameter<?> parameter : entity.getPreferredConstructor().getParameters()) {
|
||||
parameters.add(new MappedParameter(parameter, entity, context));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link PersistentProperty} is referenced in a constructor argument of the
|
||||
* {@link PersistentEntity} backing this {@link MappedConstructor}.
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean isConstructorParameter(PersistentProperty<?> property) {
|
||||
|
||||
Assert.notNull(property);
|
||||
|
||||
for (MappedConstructor.MappedParameter parameter : parameters) {
|
||||
if (parameter.maps(property)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link MappedParameter} for the given {@link Parameter}.
|
||||
*
|
||||
* @param parameter must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public MappedParameter getFor(Parameter<?> parameter) {
|
||||
|
||||
for (MappedParameter mappedParameter : parameters) {
|
||||
if (mappedParameter.parameter.equals(parameter)) {
|
||||
return mappedParameter;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format("Didn't find a MappedParameter for %s!", parameter.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction of a {@link Parameter} alongside mapping information.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class MappedParameter {
|
||||
|
||||
private final MongoPersistentProperty property;
|
||||
private final Parameter<?> parameter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappedParameter} for the given {@link Parameter}, {@link MongoPersistentProperty} and
|
||||
* {@link MappingContext}.
|
||||
*
|
||||
* @param parameter must not be {@literal null}.
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param context must not be {@literal null}.
|
||||
*/
|
||||
public MappedParameter(Parameter<?> parameter, MongoPersistentEntity<?> entity,
|
||||
MappingContext<? extends MongoPersistentEntity<?>, ? extends MongoPersistentProperty> context) {
|
||||
|
||||
Assert.notNull(parameter);
|
||||
Assert.notNull(entity);
|
||||
Assert.notNull(context);
|
||||
|
||||
this.parameter = parameter;
|
||||
|
||||
PropertyPath propertyPath = PropertyPath.from(parameter.getName(), entity.getType());
|
||||
PersistentPropertyPath<? extends MongoPersistentProperty> path = context.getPersistentPropertyPath(propertyPath);
|
||||
this.property = path == null ? null : path.getLeafProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether there is a SpEL expression configured for this parameter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasSpELExpression() {
|
||||
return parameter.getKey() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field name to be used to lookup the value which in turn shall be converted into the constructor
|
||||
* parameter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getFieldName() {
|
||||
return property.getFieldName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the property backing the {@link Parameter}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public TypeInformation<?> getPropertyTypeInformation() {
|
||||
return property.getTypeInformation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link PersistentProperty} is mapped by the parameter.
|
||||
*
|
||||
* @param property
|
||||
* @return
|
||||
*/
|
||||
public boolean maps(PersistentProperty<?> property) {
|
||||
return this.property.equals(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -30,29 +29,30 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.data.convert.EntityInstantiator;
|
||||
import org.springframework.data.convert.TypeMapper;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PreferredConstructor.Parameter;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
import org.springframework.data.mapping.model.SpELAwareParameterValueProvider;
|
||||
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
|
||||
import org.springframework.data.mapping.model.PropertyValueProvider;
|
||||
import org.springframework.data.mapping.model.SpELContext;
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -82,6 +82,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
protected boolean useFieldAccessOnly = true;
|
||||
protected MongoTypeMapper typeMapper;
|
||||
|
||||
private SpELContext spELContext;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappingMongoConverter} given the new {@link MongoDbFactory} and {@link MappingContext}.
|
||||
*
|
||||
@@ -101,6 +103,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
this.mappingContext = mappingContext;
|
||||
this.typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, mappingContext);
|
||||
this.idMapper = new QueryMapper(this);
|
||||
|
||||
this.spELContext = new SpELContext(DBObjectPropertyAccessor.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,7 +144,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
|
||||
this.applicationContext = applicationContext;
|
||||
this.spELContext = new SpELContext(this.spELContext, applicationContext);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -187,34 +193,39 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return read(persistentEntity, dbo);
|
||||
}
|
||||
|
||||
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo) {
|
||||
private ParameterValueProvider<MongoPersistentProperty> getParameterProvider(MongoPersistentEntity<?> entity,
|
||||
DBObject source, DefaultSpELExpressionEvaluator evaluator) {
|
||||
|
||||
final StandardEvaluationContext spelCtx = new StandardEvaluationContext(dbo);
|
||||
spelCtx.addPropertyAccessor(DBObjectPropertyAccessor.INSTANCE);
|
||||
MongoDbPropertyValueProvider provider = new MongoDbPropertyValueProvider(source, evaluator);
|
||||
PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<MongoPersistentProperty>(
|
||||
entity, provider);
|
||||
parameterProvider.setSpELEvaluator(evaluator);
|
||||
|
||||
if (applicationContext != null) {
|
||||
spelCtx.setBeanResolver(new BeanFactoryResolver(applicationContext));
|
||||
return parameterProvider;
|
||||
}
|
||||
|
||||
final MappedConstructor constructor = new MappedConstructor(entity, mappingContext);
|
||||
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo) {
|
||||
|
||||
SpELAwareParameterValueProvider delegate = new SpELAwareParameterValueProvider(spelExpressionParser, spelCtx);
|
||||
ParameterValueProvider provider = new DelegatingParameterValueProvider(constructor, dbo, delegate);
|
||||
final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(dbo, spELContext);
|
||||
|
||||
final BeanWrapper<MongoPersistentEntity<S>, S> wrapper = BeanWrapper.create(entity, provider, conversionService);
|
||||
ParameterValueProvider<MongoPersistentProperty> provider = getParameterProvider(entity, dbo, evaluator);
|
||||
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
|
||||
S instance = instantiator.createInstance(entity, provider);
|
||||
|
||||
final BeanWrapper<MongoPersistentEntity<S>, S> wrapper = BeanWrapper.create(instance, conversionService);
|
||||
|
||||
// Set properties not already set in the constructor
|
||||
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
|
||||
public void doWithPersistentProperty(MongoPersistentProperty prop) {
|
||||
|
||||
boolean isConstructorProperty = constructor.isConstructorParameter(prop);
|
||||
boolean isConstructorProperty = entity.isConstructorArgument(prop);
|
||||
boolean hasValueForProperty = dbo.containsField(prop.getFieldName());
|
||||
|
||||
if (!hasValueForProperty || isConstructorProperty) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object obj = getValueInternal(prop, dbo, spelCtx, prop.getSpelExpression());
|
||||
Object obj = getValueInternal(prop, dbo, evaluator);
|
||||
wrapper.setProperty(prop, obj, useFieldAccessOnly);
|
||||
}
|
||||
});
|
||||
@@ -223,7 +234,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
entity.doWithAssociations(new AssociationHandler<MongoPersistentProperty>() {
|
||||
public void doWithAssociation(Association<MongoPersistentProperty> association) {
|
||||
MongoPersistentProperty inverseProp = association.getInverse();
|
||||
Object obj = getValueInternal(inverseProp, dbo, spelCtx, inverseProp.getSpelExpression());
|
||||
Object obj = getValueInternal(inverseProp, dbo, evaluator);
|
||||
try {
|
||||
wrapper.setProperty(inverseProp, obj);
|
||||
} catch (IllegalAccessException e) {
|
||||
@@ -621,58 +632,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return new DBRef(db, collection, idMapper.convertId(id));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, StandardEvaluationContext ctx,
|
||||
String spelExpr) {
|
||||
protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, SpELExpressionEvaluator eval) {
|
||||
|
||||
Object o;
|
||||
if (null != spelExpr) {
|
||||
Expression x = spelExpressionParser.parseExpression(spelExpr);
|
||||
o = x.getValue(ctx);
|
||||
} else {
|
||||
|
||||
Object sourceValue = dbo.get(prop.getFieldName());
|
||||
|
||||
if (sourceValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> propertyType = prop.getType();
|
||||
|
||||
if (conversions.hasCustomReadTarget(sourceValue.getClass(), propertyType)) {
|
||||
return conversionService.convert(sourceValue, propertyType);
|
||||
}
|
||||
|
||||
if (sourceValue instanceof DBRef) {
|
||||
sourceValue = ((DBRef) sourceValue).fetch();
|
||||
}
|
||||
if (sourceValue instanceof DBObject) {
|
||||
if (prop.isMap()) {
|
||||
return readMap(prop.getTypeInformation(), (DBObject) sourceValue);
|
||||
} else if (prop.isArray() && sourceValue instanceof BasicDBObject
|
||||
&& ((DBObject) sourceValue).keySet().size() == 0) {
|
||||
// It's empty
|
||||
return Array.newInstance(prop.getComponentType(), 0);
|
||||
} else if (prop.isCollectionLike() && sourceValue instanceof BasicDBList) {
|
||||
return readCollectionOrArray((TypeInformation<? extends Collection<?>>) prop.getTypeInformation(),
|
||||
(BasicDBList) sourceValue);
|
||||
}
|
||||
|
||||
TypeInformation<?> toType = typeMapper.readType((DBObject) sourceValue);
|
||||
|
||||
// It's a complex object, have to read it in
|
||||
if (toType != null) {
|
||||
// TODO: why do we remove the type?
|
||||
// dbo.removeField(CUSTOM_TYPE_KEY);
|
||||
o = read(toType, (DBObject) sourceValue);
|
||||
} else {
|
||||
o = read(mappingContext.getPersistentEntity(prop.getTypeInformation()), (DBObject) sourceValue);
|
||||
}
|
||||
} else {
|
||||
o = sourceValue;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
MongoDbPropertyValueProvider provider = new MongoDbPropertyValueProvider(dbo, spELContext);
|
||||
return provider.getPropertyValue(prop);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -858,46 +821,42 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return dbObject;
|
||||
}
|
||||
|
||||
private class DelegatingParameterValueProvider implements ParameterValueProvider {
|
||||
private class MongoDbPropertyValueProvider implements PropertyValueProvider<MongoPersistentProperty> {
|
||||
|
||||
private final DBObject source;
|
||||
private final ParameterValueProvider delegate;
|
||||
private final MappedConstructor constructor;
|
||||
private final SpELExpressionEvaluator evaluator;
|
||||
|
||||
/**
|
||||
* {@link ParameterValueProvider} to delegate source object lookup to a {@link SpELAwareParameterValueProvider} in
|
||||
* case a MappCon
|
||||
*
|
||||
* @param constructor must not be {@literal null}.
|
||||
* @param source must not be {@literal null}.
|
||||
* @param delegate must not be {@literal null}.
|
||||
*/
|
||||
public DelegatingParameterValueProvider(MappedConstructor constructor, DBObject source,
|
||||
SpELAwareParameterValueProvider delegate) {
|
||||
public MongoDbPropertyValueProvider(DBObject source, SpELContext factory) {
|
||||
this(source, new DefaultSpELExpressionEvaluator(source, factory));
|
||||
}
|
||||
|
||||
public MongoDbPropertyValueProvider(DBObject source, DefaultSpELExpressionEvaluator evaluator) {
|
||||
|
||||
Assert.notNull(constructor);
|
||||
Assert.notNull(source);
|
||||
Assert.notNull(delegate);
|
||||
Assert.notNull(evaluator);
|
||||
|
||||
this.constructor = constructor;
|
||||
this.source = source;
|
||||
this.delegate = delegate;
|
||||
this.evaluator = evaluator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
|
||||
* @see org.springframework.data.convert.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getParameterValue(Parameter<T> parameter) {
|
||||
public <T> T getPropertyValue(MongoPersistentProperty property) {
|
||||
|
||||
MappedConstructor.MappedParameter mappedParameter = constructor.getFor(parameter);
|
||||
Object value = mappedParameter.hasSpELExpression() ? delegate.getParameterValue(parameter) : source
|
||||
.get(mappedParameter.getFieldName());
|
||||
String expression = property.getSpelExpression();
|
||||
TypeInformation<?> type = property.getTypeInformation();
|
||||
Object value = expression != null ? evaluator.evaluate(expression) : source.get(property.getFieldName());
|
||||
|
||||
TypeInformation<?> type = mappedParameter.getPropertyTypeInformation();
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value instanceof DBRef) {
|
||||
if (conversions.hasCustomReadTarget(value.getClass(), type.getType())) {
|
||||
return (T) conversionService.convert(value, type.getType());
|
||||
} else if (value instanceof DBRef) {
|
||||
return (T) read(type, ((DBRef) value).fetch());
|
||||
} else if (value instanceof BasicDBList) {
|
||||
return (T) getPotentiallyConvertedSimpleRead(readCollectionOrArray(type, (BasicDBList) value), type.getType());
|
||||
|
||||
Reference in New Issue
Block a user