DATAMONGO-592 - Fix handling of SpEL evaluation values.
During constructor parameter value resolution the value to be used can result from evaluating a SpEL expression on the root document. This value has to be converted recursively if it is a nested document. Adapted to the altered API in SD Commons and override potentiallyConvertSpelValue(…) to re-invoke converter.
This commit is contained in:
@@ -31,11 +31,13 @@ import org.springframework.context.ApplicationContext;
|
|||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
import org.springframework.core.CollectionFactory;
|
import org.springframework.core.CollectionFactory;
|
||||||
import org.springframework.core.convert.ConversionException;
|
import org.springframework.core.convert.ConversionException;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||||
import org.springframework.data.convert.EntityInstantiator;
|
import org.springframework.data.convert.EntityInstantiator;
|
||||||
import org.springframework.data.convert.TypeMapper;
|
import org.springframework.data.convert.TypeMapper;
|
||||||
import org.springframework.data.mapping.Association;
|
import org.springframework.data.mapping.Association;
|
||||||
import org.springframework.data.mapping.AssociationHandler;
|
import org.springframework.data.mapping.AssociationHandler;
|
||||||
|
import org.springframework.data.mapping.PreferredConstructor.Parameter;
|
||||||
import org.springframework.data.mapping.PropertyHandler;
|
import org.springframework.data.mapping.PropertyHandler;
|
||||||
import org.springframework.data.mapping.context.MappingContext;
|
import org.springframework.data.mapping.context.MappingContext;
|
||||||
import org.springframework.data.mapping.model.BeanWrapper;
|
import org.springframework.data.mapping.model.BeanWrapper;
|
||||||
@@ -46,6 +48,7 @@ import org.springframework.data.mapping.model.PersistentEntityParameterValueProv
|
|||||||
import org.springframework.data.mapping.model.PropertyValueProvider;
|
import org.springframework.data.mapping.model.PropertyValueProvider;
|
||||||
import org.springframework.data.mapping.model.SpELContext;
|
import org.springframework.data.mapping.model.SpELContext;
|
||||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||||
|
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
|
||||||
import org.springframework.data.mongodb.MongoDbFactory;
|
import org.springframework.data.mongodb.MongoDbFactory;
|
||||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||||
@@ -215,9 +218,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
|||||||
MongoDbPropertyValueProvider provider = new MongoDbPropertyValueProvider(source, evaluator, parent);
|
MongoDbPropertyValueProvider provider = new MongoDbPropertyValueProvider(source, evaluator, parent);
|
||||||
PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<MongoPersistentProperty>(
|
PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<MongoPersistentProperty>(
|
||||||
entity, provider, parent);
|
entity, provider, parent);
|
||||||
parameterProvider.setSpELEvaluator(evaluator);
|
|
||||||
|
|
||||||
return parameterProvider;
|
return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider,
|
||||||
|
parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo, Object parent) {
|
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo, Object parent) {
|
||||||
@@ -933,7 +936,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
|||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see org.springframework.data.convert.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
|
* @see org.springframework.data.convert.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T getPropertyValue(MongoPersistentProperty property) {
|
public <T> T getPropertyValue(MongoPersistentProperty property) {
|
||||||
|
|
||||||
String expression = property.getSpelExpression();
|
String expression = property.getSpelExpression();
|
||||||
@@ -943,7 +945,48 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeInformation<?> type = property.getTypeInformation();
|
return readValue(value, property.getTypeInformation(), parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of {@link SpELExpressionParameterValueProvider} to recursively trigger value conversion on the raw
|
||||||
|
* resolved SpEL value.
|
||||||
|
*
|
||||||
|
* @author Oliver Gierke
|
||||||
|
*/
|
||||||
|
private class ConverterAwareSpELExpressionParameterValueProvider extends
|
||||||
|
SpELExpressionParameterValueProvider<MongoPersistentProperty> {
|
||||||
|
|
||||||
|
private final Object parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ConverterAwareSpELExpressionParameterValueProvider}.
|
||||||
|
*
|
||||||
|
* @param evaluator must not be {@literal null}.
|
||||||
|
* @param conversionService must not be {@literal null}.
|
||||||
|
* @param delegate must not be {@literal null}.
|
||||||
|
*/
|
||||||
|
public ConverterAwareSpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator,
|
||||||
|
ConversionService conversionService, ParameterValueProvider<MongoPersistentProperty> delegate, Object parent) {
|
||||||
|
|
||||||
|
super(evaluator, conversionService, delegate);
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see org.springframework.data.mapping.model.SpELExpressionParameterValueProvider#potentiallyConvertSpelValue(java.lang.Object, org.springframework.data.mapping.PreferredConstructor.Parameter)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, MongoPersistentProperty> parameter) {
|
||||||
|
return readValue(object, parameter.getType(), parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> T readValue(Object value, TypeInformation<?> type, Object parent) {
|
||||||
|
|
||||||
Class<?> rawType = type.getType();
|
Class<?> rawType = type.getType();
|
||||||
|
|
||||||
if (conversions.hasCustomReadTarget(value.getClass(), rawType)) {
|
if (conversions.hasCustomReadTarget(value.getClass(), rawType)) {
|
||||||
@@ -958,5 +1001,4 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
|||||||
return (T) getPotentiallyConvertedSimpleRead(value, rawType);
|
return (T) getPotentiallyConvertedSimpleRead(value, rawType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import org.springframework.data.mapping.PropertyPath;
|
|||||||
import org.springframework.data.mapping.model.MappingException;
|
import org.springframework.data.mapping.model.MappingException;
|
||||||
import org.springframework.data.mapping.model.MappingInstantiationException;
|
import org.springframework.data.mapping.model.MappingInstantiationException;
|
||||||
import org.springframework.data.mongodb.MongoDbFactory;
|
import org.springframework.data.mongodb.MongoDbFactory;
|
||||||
|
import org.springframework.data.mongodb.core.mapping.Document;
|
||||||
import org.springframework.data.mongodb.core.mapping.Field;
|
import org.springframework.data.mongodb.core.mapping.Field;
|
||||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||||
@@ -65,6 +66,7 @@ import com.mongodb.BasicDBObject;
|
|||||||
import com.mongodb.DB;
|
import com.mongodb.DB;
|
||||||
import com.mongodb.DBObject;
|
import com.mongodb.DBObject;
|
||||||
import com.mongodb.DBRef;
|
import com.mongodb.DBRef;
|
||||||
|
import com.mongodb.util.JSON;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link MappingMongoConverter}.
|
* Unit tests for {@link MappingMongoConverter}.
|
||||||
@@ -1323,6 +1325,18 @@ public class MappingMongoConverterUnitTests {
|
|||||||
converter.write(wrapper, dbObject);
|
converter.write(wrapper, dbObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see DATAMONGO-592
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void recursivelyConvertsSpELReadValue() {
|
||||||
|
|
||||||
|
DBObject input = (DBObject) JSON
|
||||||
|
.parse("{ \"_id\" : { \"$oid\" : \"50ca271c4566a2b08f2d667a\" }, \"_class\" : \"com.recorder.TestRecorder2$ObjectContainer\", \"property\" : { \"property\" : 100 } }");
|
||||||
|
|
||||||
|
converter.read(ObjectContainer.class, input);
|
||||||
|
}
|
||||||
|
|
||||||
static class GenericType<T> {
|
static class GenericType<T> {
|
||||||
T content;
|
T content;
|
||||||
}
|
}
|
||||||
@@ -1516,6 +1530,38 @@ public class MappingMongoConverterUnitTests {
|
|||||||
Throwable throwable;
|
Throwable throwable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Document
|
||||||
|
static class PrimitiveContainer {
|
||||||
|
|
||||||
|
@Field("property")
|
||||||
|
private final int m_property;
|
||||||
|
|
||||||
|
@PersistenceConstructor
|
||||||
|
public PrimitiveContainer(@Value("#root.property") int a_property) {
|
||||||
|
m_property = a_property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int property() {
|
||||||
|
return m_property;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Document
|
||||||
|
static class ObjectContainer {
|
||||||
|
|
||||||
|
@Field("property")
|
||||||
|
private final PrimitiveContainer m_property;
|
||||||
|
|
||||||
|
@PersistenceConstructor
|
||||||
|
public ObjectContainer(@Value("#root.property") PrimitiveContainer a_property) {
|
||||||
|
m_property = a_property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrimitiveContainer property() {
|
||||||
|
return m_property;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class LocalDateToDateConverter implements Converter<LocalDate, Date> {
|
private class LocalDateToDateConverter implements Converter<LocalDate, Date> {
|
||||||
|
|
||||||
public Date convert(LocalDate source) {
|
public Date convert(LocalDate source) {
|
||||||
|
|||||||
Reference in New Issue
Block a user