diff --git a/pom.xml b/pom.xml index a67ad87168..a8217a7fcb 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 4.3.0-SNAPSHOT + 4.3.0-GH-4634-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index 34d95eb205..7f63fc1748 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-mongodb-parent - 4.3.0-SNAPSHOT + 4.3.0-GH-4634-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 124a6bf5ad..de6fe2d393 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -15,7 +15,7 @@ org.springframework.data spring-data-mongodb-parent - 4.3.0-SNAPSHOT + 4.3.0-GH-4634-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index e7282be0fa..8439475255 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 4.3.0-SNAPSHOT + 4.3.0-GH-4634-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java index 091f965c19..21e1bab2b3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java @@ -15,12 +15,14 @@ */ package org.springframework.data.mongodb.core.convert; +import java.util.function.Function; + import org.bson.Document; + import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator; import org.springframework.data.mapping.model.SpELContext; -import org.springframework.data.mapping.model.SpELExpressionEvaluator; +import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.lang.Nullable; @@ -37,6 +39,7 @@ class DefaultDbRefProxyHandler implements DbRefProxyHandler { private final SpELContext spELContext; private final MappingContext, MongoPersistentProperty> mappingContext; private final ValueResolver resolver; + private final Function evaluatorFactory; /** * @param spELContext must not be {@literal null}. @@ -45,11 +48,12 @@ class DefaultDbRefProxyHandler implements DbRefProxyHandler { */ public DefaultDbRefProxyHandler(SpELContext spELContext, MappingContext, MongoPersistentProperty> mappingContext, - ValueResolver resolver) { + ValueResolver resolver, Function evaluatorFactory) { this.spELContext = spELContext; this.mappingContext = mappingContext; this.resolver = resolver; + this.evaluatorFactory = evaluatorFactory; } @Override @@ -66,7 +70,7 @@ public Object populateId(MongoPersistentProperty property, @Nullable DBRef sourc return proxy; } - SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(proxy, spELContext); + ValueExpressionEvaluator evaluator = evaluatorFactory.apply(proxy); PersistentPropertyAccessor accessor = entity.getPropertyAccessor(proxy); Document object = new Document(idProperty.getFieldName(), source.getId()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java index 7f89ad0e86..0e90835710 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java @@ -17,7 +17,7 @@ import org.bson.Document; import org.bson.conversions.Bson; -import org.springframework.data.mapping.model.SpELExpressionEvaluator; +import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; /** @@ -32,18 +32,18 @@ class DefaultDbRefResolverCallback implements DbRefResolverCallback { private final Bson surroundingObject; private final ObjectPath path; private final ValueResolver resolver; - private final SpELExpressionEvaluator evaluator; + private final ValueExpressionEvaluator evaluator; /** * Creates a new {@link DefaultDbRefResolverCallback} using the given {@link Document}, {@link ObjectPath}, - * {@link ValueResolver} and {@link SpELExpressionEvaluator}. + * {@link ValueResolver} and {@link ValueExpressionEvaluator}. * * @param surroundingObject must not be {@literal null}. * @param path must not be {@literal null}. * @param evaluator must not be {@literal null}. * @param resolver must not be {@literal null}. */ - public DefaultDbRefResolverCallback(Bson surroundingObject, ObjectPath path, SpELExpressionEvaluator evaluator, + public DefaultDbRefResolverCallback(Bson surroundingObject, ObjectPath path, ValueExpressionEvaluator evaluator, ValueResolver resolver) { this.surroundingObject = surroundingObject; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 86d8754850..9f0bc4c107 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -39,6 +39,7 @@ import org.bson.conversions.Bson; import org.bson.json.JsonReader; import org.bson.types.ObjectId; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.context.ApplicationContext; @@ -47,6 +48,9 @@ import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.core.env.Environment; +import org.springframework.core.env.EnvironmentCapable; +import org.springframework.core.env.StandardEnvironment; import org.springframework.data.annotation.Reference; import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.TypeMapper; @@ -59,15 +63,16 @@ import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.callback.EntityCallbacks; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.model.CachingValueExpressionEvaluatorFactory; import org.springframework.data.mapping.model.ConvertingPropertyAccessor; -import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator; import org.springframework.data.mapping.model.EntityInstantiator; import org.springframework.data.mapping.model.ParameterValueProvider; 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.mapping.model.SpELExpressionParameterValueProvider; +import org.springframework.data.mapping.model.ValueExpressionEvaluator; +import org.springframework.data.mapping.model.ValueExpressionParameterValueProvider; import org.springframework.data.mongodb.CodecRegistryProvider; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty; @@ -88,6 +93,7 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.util.Predicates; import org.springframework.data.util.TypeInformation; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -116,7 +122,8 @@ * @author Divya Srivastava * @author Julia Lee */ -public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware { +public class MappingMongoConverter extends AbstractMongoConverter + implements ApplicationContextAware, EnvironmentCapable { private static final String INCOMPATIBLE_TYPES = "Cannot convert %1$s of type %2$s into an instance of %3$s; Implement a custom Converter<%2$s, %3$s> and register it with the CustomConversions; Parent object was: %4$s"; private static final String INVALID_TYPE_TO_READ = "Expected to read Document %s into type %s but didn't find a PersistentEntity for the latter"; @@ -132,6 +139,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App protected final ReferenceLookupDelegate referenceLookupDelegate; protected @Nullable ApplicationContext applicationContext; + protected @Nullable Environment environment; protected MongoTypeMapper typeMapper; protected @Nullable String mapKeyDotReplacement = null; protected @Nullable CodecRegistryProvider codecRegistryProvider; @@ -139,8 +147,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App private MongoTypeMapper defaultTypeMapper; private SpELContext spELContext; private @Nullable EntityCallbacks entityCallbacks; + private final SpelExpressionParser expressionParser = new SpelExpressionParser(); private final DocumentPointerFactory documentPointerFactory; - private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory(); + private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory( + expressionParser); + private final CachingValueExpressionEvaluatorFactory expressionEvaluatorFactory = new CachingValueExpressionEvaluatorFactory( + expressionParser, this, o -> spELContext.getEvaluationContext(o)); /** * Creates a new {@link MappingMongoConverter} given the new {@link DbRefResolver} and {@link MappingContext}. @@ -169,7 +181,7 @@ public MappingMongoConverter(DbRefResolver dbRefResolver, ConversionContext context = getConversionContext(path); return MappingMongoConverter.this.getValueInternal(context, prop, bson, evaluator); - }); + }, expressionEvaluatorFactory::create); this.referenceLookupDelegate = new ReferenceLookupDelegate(mappingContext, spELContext); this.documentPointerFactory = new DocumentPointerFactory(conversionService, mappingContext); @@ -271,9 +283,11 @@ public MappingContext, MongoPersistentPropert return mappingContext; } + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; + this.environment = applicationContext.getEnvironment(); this.spELContext = new SpELContext(this.spELContext, applicationContext); this.projectionFactory.setBeanFactory(applicationContext); this.projectionFactory.setBeanClassLoader(applicationContext.getClassLoader()); @@ -288,6 +302,15 @@ public void setApplicationContext(ApplicationContext applicationContext) throws } } + @Override + public Environment getEnvironment() { + + if (environment == null) { + environment = new StandardEnvironment(); + } + return environment; + } + /** * Set the {@link EntityCallbacks} instance to use when invoking * {@link org.springframework.data.mapping.callback.EntityCallback callbacks} like the {@link AfterConvertCallback}. @@ -328,7 +351,7 @@ private R doReadProjection(ConversionContext context, Bson bson, EntityProje TypeInformation mappedType = projection.getActualMappedType(); MongoPersistentEntity mappedEntity = (MongoPersistentEntity) getMappingContext() .getPersistentEntity(mappedType); - SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(bson, spELContext); + ValueExpressionEvaluator evaluator = expressionEvaluatorFactory.create(bson); boolean isInterfaceProjection = mappedType.getType().isInterface(); if (isInterfaceProjection) { @@ -481,14 +504,14 @@ protected S readDocument(ConversionContext context, Bson bson } private ParameterValueProvider getParameterProvider(ConversionContext context, - MongoPersistentEntity entity, DocumentAccessor source, SpELExpressionEvaluator evaluator) { + MongoPersistentEntity entity, DocumentAccessor source, ValueExpressionEvaluator evaluator) { AssociationAwareMongoDbPropertyValueProvider provider = new AssociationAwareMongoDbPropertyValueProvider(context, source, evaluator); PersistentEntityParameterValueProvider parameterProvider = new PersistentEntityParameterValueProvider<>( entity, provider, context.getPath().getCurrentObject()); - return new ConverterAwareSpELExpressionParameterValueProvider(context, evaluator, conversionService, + return new ConverterAwareValueExpressionParameterValueProvider(context, evaluator, conversionService, parameterProvider); } @@ -499,7 +522,7 @@ private S read(ConversionContext context, MongoPersistentEntity entity, D return existing; } - SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(bson, spELContext); + ValueExpressionEvaluator evaluator = expressionEvaluatorFactory.create(bson); DocumentAccessor documentAccessor = new DocumentAccessor(bson); InstanceCreatorMetadata instanceCreatorMetadata = entity.getInstanceCreatorMetadata(); @@ -515,7 +538,7 @@ private S read(ConversionContext context, MongoPersistentEntity entity, D } private S populateProperties(ConversionContext context, MongoPersistentEntity entity, - DocumentAccessor documentAccessor, SpELExpressionEvaluator evaluator, S instance) { + DocumentAccessor documentAccessor, ValueExpressionEvaluator evaluator, S instance) { if (!entity.requiresPropertyPopulation()) { return instance; @@ -545,7 +568,7 @@ private S populateProperties(ConversionContext context, MongoPersistentEntit */ @Nullable private Object readAndPopulateIdentifier(ConversionContext context, PersistentPropertyAccessor accessor, - DocumentAccessor document, MongoPersistentEntity entity, SpELExpressionEvaluator evaluator) { + DocumentAccessor document, MongoPersistentEntity entity, ValueExpressionEvaluator evaluator) { Object rawId = document.getRawId(entity); @@ -565,7 +588,7 @@ private Object readAndPopulateIdentifier(ConversionContext context, PersistentPr } @Nullable - private Object readIdValue(ConversionContext context, SpELExpressionEvaluator evaluator, + private Object readIdValue(ConversionContext context, ValueExpressionEvaluator evaluator, MongoPersistentProperty idProperty, Object rawId) { String expression = idProperty.getSpelExpression(); @@ -578,7 +601,7 @@ private Object readIdValue(ConversionContext context, SpELExpressionEvaluator ev private void readProperties(ConversionContext context, MongoPersistentEntity entity, PersistentPropertyAccessor accessor, DocumentAccessor documentAccessor, - MongoDbPropertyValueProvider valueProvider, SpELExpressionEvaluator evaluator, + MongoDbPropertyValueProvider valueProvider, ValueExpressionEvaluator evaluator, Predicate propertyFilter) { DbRefResolverCallback callback = null; @@ -622,7 +645,7 @@ private void readProperties(ConversionContext context, MongoPersistentEntity } private DbRefResolverCallback getDbRefResolverCallback(ConversionContext context, DocumentAccessor documentAccessor, - SpELExpressionEvaluator evaluator) { + ValueExpressionEvaluator evaluator) { return new DefaultDbRefResolverCallback(documentAccessor.getDocument(), context.getPath(), evaluator, (prop, bson, e, path) -> MappingMongoConverter.this.getValueInternal(context, prop, bson, e)); @@ -1387,7 +1410,7 @@ protected DBRef createDBRef(Object target, @Nullable MongoPersistentProperty pro @Nullable private Object getValueInternal(ConversionContext context, MongoPersistentProperty prop, Bson bson, - SpELExpressionEvaluator evaluator) { + ValueExpressionEvaluator evaluator) { return new MongoDbPropertyValueProvider(context, bson, evaluator).getPropertyValue(prop); } @@ -1876,23 +1899,23 @@ static class MongoDbPropertyValueProvider implements PropertyValueProvider T getPropertyValue(MongoPersistentProperty property) { * * @author Oliver Gierke */ - private static class ConverterAwareSpELExpressionParameterValueProvider - extends SpELExpressionParameterValueProvider { + private static class ConverterAwareValueExpressionParameterValueProvider + extends ValueExpressionParameterValueProvider { private final ConversionContext context; /** - * Creates a new {@link ConverterAwareSpELExpressionParameterValueProvider}. + * Creates a new {@link ConverterAwareValueExpressionParameterValueProvider}. * * @param context must not be {@literal null}. * @param evaluator must not be {@literal null}. * @param conversionService must not be {@literal null}. * @param delegate must not be {@literal null}. */ - public ConverterAwareSpELExpressionParameterValueProvider(ConversionContext context, - SpELExpressionEvaluator evaluator, ConversionService conversionService, + public ConverterAwareValueExpressionParameterValueProvider(ConversionContext context, + ValueExpressionEvaluator evaluator, ConversionService conversionService, ParameterValueProvider delegate) { super(evaluator, conversionService, delegate); @@ -2025,7 +2048,7 @@ public ConverterAwareSpELExpressionParameterValueProvider(ConversionContext cont } @Override - protected T potentiallyConvertSpelValue(Object object, Parameter parameter) { + protected T potentiallyConvertExpressionValue(Object object, Parameter parameter) { return context.convert(object, parameter.getType()); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java index c1a478fa77..37b7d0a9e8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.core.convert; import org.bson.conversions.Bson; + import org.springframework.data.convert.ValueConversionContext; import org.springframework.data.mapping.model.PropertyValueProvider; import org.springframework.data.mapping.model.SpELContext; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java index 5c4edc55a6..4d421ad05c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java @@ -17,7 +17,8 @@ import org.bson.Document; import org.bson.conversions.Bson; -import org.springframework.data.mapping.model.SpELExpressionEvaluator; + +import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.lang.Nullable; @@ -32,7 +33,7 @@ interface ValueResolver { /** * Resolves the value for the given {@link MongoPersistentProperty} within the given {@link Document} using the given - * {@link SpELExpressionEvaluator} and {@link ObjectPath}. + * {@link ValueExpressionEvaluator} and {@link ObjectPath}. * * @param prop * @param bson @@ -41,5 +42,5 @@ interface ValueResolver { * @return */ @Nullable - Object getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator, ObjectPath path); + Object getValueInternal(MongoPersistentProperty prop, Bson bson, ValueExpressionEvaluator evaluator, ObjectPath path); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java index 026ca64d00..216f28961e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java @@ -26,6 +26,9 @@ import java.util.Map; import org.springframework.data.annotation.Id; +import org.springframework.data.expression.ValueEvaluationContext; +import org.springframework.data.expression.ValueExpression; +import org.springframework.data.expression.ValueExpressionParser; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.AssociationHandler; import org.springframework.data.mapping.MappingException; @@ -38,8 +41,6 @@ import org.springframework.data.util.TypeInformation; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; -import org.springframework.expression.ParserContext; -import org.springframework.expression.common.LiteralExpression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -61,15 +62,15 @@ public class BasicMongoPersistentEntity extends BasicPersistentEntity { private static final String AMBIGUOUS_FIELD_MAPPING = "Ambiguous field mapping detected; Both %s and %s map to the same field name %s; Disambiguate using @Field annotation"; - private static final SpelExpressionParser PARSER = new SpelExpressionParser(); + private static final ValueExpressionParser PARSER = ValueExpressionParser.create(SpelExpressionParser::new); private final String collection; private final String language; - private final @Nullable Expression expression; + private final @Nullable ValueExpression expression; private final @Nullable String collation; - private final @Nullable Expression collationExpression; + private final @Nullable ValueExpression collationExpression; private final ShardKey shardKey; @@ -125,11 +126,12 @@ private ShardKey detectShardKey() { return sharded.immutableKey() ? ShardKey.immutable(shardKey) : shardKey; } + @Override public String getCollection() { return expression == null // ? collection // - : expression.getValue(getEvaluationContext(null), String.class); + : ObjectUtils.nullSafeToString(expression.evaluate(getValueEvaluationContext(null))); } @Override @@ -152,7 +154,7 @@ public boolean hasTextScoreProperty() { public org.springframework.data.mongodb.core.query.Collation getCollation() { Object collationValue = collationExpression != null - ? collationExpression.getValue(getEvaluationContext(null), String.class) + ? collationExpression.evaluate(getValueEvaluationContext(null)) : this.collation; if (collationValue == null) { @@ -196,6 +198,16 @@ public EvaluationContext getEvaluationContext(Object rootObject, ExpressionDepen return super.getEvaluationContext(rootObject, dependencies); } + @Override + public ValueEvaluationContext getValueEvaluationContext(Object rootObject) { + return super.getValueEvaluationContext(rootObject); + } + + @Override + public ValueEvaluationContext getValueEvaluationContext(Object rootObject, ExpressionDependencies dependencies) { + return super.getValueEvaluationContext(rootObject, dependencies); + } + private void verifyFieldUniqueness() { AssertFieldNameUniquenessHandler handler = new AssertFieldNameUniquenessHandler(); @@ -290,21 +302,21 @@ protected MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNul } /** - * Returns a SpEL {@link Expression} if the given {@link String} is actually an expression that does not evaluate to a - * {@link LiteralExpression} (indicating that no subsequent evaluation is necessary). + * Returns a Value {@link Expression} if the given {@link String} is actually an expression that does not evaluate to + * a literal expression (indicating that no subsequent evaluation is necessary). * * @param potentialExpression can be {@literal null} * @return can be {@literal null}. */ @Nullable - private static Expression detectExpression(@Nullable String potentialExpression) { + private static ValueExpression detectExpression(@Nullable String potentialExpression) { if (!StringUtils.hasText(potentialExpression)) { return null; } - Expression expression = PARSER.parseExpression(potentialExpression, ParserContext.TEMPLATE_EXPRESSION); - return expression instanceof LiteralExpression ? null : expression; + ValueExpression expression = PARSER.parse(potentialExpression); + return expression.isLiteral() ? null : expression; } /** @@ -332,8 +344,7 @@ private void assertUniqueness(MongoPersistentProperty property) { MongoPersistentProperty existingProperty = properties.get(fieldName); if (existingProperty != null) { - throw new MappingException( - String.format(AMBIGUOUS_FIELD_MAPPING, property, existingProperty, fieldName)); + throw new MappingException(String.format(AMBIGUOUS_FIELD_MAPPING, property, existingProperty, fieldName)); } properties.put(fieldName, property); @@ -398,9 +409,9 @@ private static void potentiallyAssertDBRefTargetType(MongoPersistentProperty per if (persistentProperty.isDbReference() && persistentProperty.getDBRef().lazy()) { if (persistentProperty.isArray() || Modifier.isFinal(persistentProperty.getActualType().getModifiers())) { - throw new MappingException(String.format( - "Invalid lazy DBRef property for %s; Found %s which must not be an array nor a final class", - persistentProperty.getField(), persistentProperty.getActualType())); + throw new MappingException( + String.format("Invalid lazy DBRef property for %s; Found %s which must not be an array nor a final class", + persistentProperty.getField(), persistentProperty.getActualType())); } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java index 78e0ba4eea..24a630fd49 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java @@ -23,6 +23,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + +import org.springframework.core.env.StandardEnvironment; +import org.springframework.data.expression.ValueEvaluationContext; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; @@ -230,6 +233,25 @@ public EvaluationContext getEvaluationContext(@Nullable Object rootObject) { return rootObject != null ? new StandardEvaluationContext(rootObject) : new StandardEvaluationContext(); } + /** + * Obtain the {@link EvaluationContext} for a specific root object. + * + * @param rootObject can be {@literal null}. + * @return never {@literal null}. + * @since 3.3 + */ + public ValueEvaluationContext getValueEvaluationContext(@Nullable Object rootObject) { + + if (getOwner() instanceof BasicMongoPersistentEntity mongoPersistentEntity) { + return mongoPersistentEntity.getValueEvaluationContext(rootObject); + } + + StandardEvaluationContext standardEvaluationContext = rootObject != null ? new StandardEvaluationContext(rootObject) + : new StandardEvaluationContext(); + + return ValueEvaluationContext.of(new StandardEnvironment(), standardEvaluationContext); + } + @Override public MongoField getMongoField() { return doGetMongoField(); @@ -318,7 +340,7 @@ protected void validate() { String annotatedName = getAnnotatedFieldName(); if (!ID_FIELD_NAME.equals(annotatedName)) { - if(LOG.isWarnEnabled()) { + if (LOG.isWarnEnabled()) { LOG.warn(String.format( "Customizing field name for id property '%s.%s' is not allowed; Custom name ('%s') will not be considered", getOwner().getName(), getName(), annotatedName)); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java index b4221cf7f8..f5697e5631 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java @@ -24,6 +24,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; +import org.springframework.core.env.Environment; import org.springframework.data.mapping.*; import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory; import org.springframework.data.mongodb.core.query.Collation; @@ -326,6 +327,11 @@ public void setEvaluationContextProvider(EvaluationContextProvider provider) { } + @Override + public void setEnvironment(Environment environment) { + + } + @Override public boolean isUnwrapped() { return context.getProperty().isUnwrapped(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java index 0256e20d5e..9759e374f3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java @@ -17,13 +17,11 @@ import java.util.Collections; import java.util.Map; -import java.util.function.Supplier; -import org.springframework.data.mapping.model.SpELExpressionEvaluator; +import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpression; -import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.lang.Nullable; @@ -31,18 +29,15 @@ * @author Christoph Strobl * @since 3.3.5 */ -class EvaluationContextExpressionEvaluator implements SpELExpressionEvaluator { +class EvaluationContextExpressionEvaluator implements ValueExpressionEvaluator { - ValueProvider valueProvider; - ExpressionParser expressionParser; - Supplier evaluationContext; + final ValueProvider valueProvider; + final ExpressionParser expressionParser; - public EvaluationContextExpressionEvaluator(ValueProvider valueProvider, ExpressionParser expressionParser, - Supplier evaluationContext) { + public EvaluationContextExpressionEvaluator(ValueProvider valueProvider, ExpressionParser expressionParser) { this.valueProvider = valueProvider; this.expressionParser = expressionParser; - this.evaluationContext = evaluationContext; } @Nullable @@ -52,19 +47,19 @@ public T evaluate(String expression) { } public EvaluationContext getEvaluationContext(String expressionString) { - return evaluationContext != null ? evaluationContext.get() : new StandardEvaluationContext(); + return new StandardEvaluationContext(); } - public SpelExpression getParsedExpression(String expressionString) { - return (SpelExpression) (expressionParser != null ? expressionParser : new SpelExpressionParser()) - .parseExpression(expressionString); + public Expression getParsedExpression(String expressionString) { + return expressionParser.parseExpression(expressionString); } + @SuppressWarnings("unchecked") public T evaluateExpression(String expressionString, Map variables) { - SpelExpression expression = getParsedExpression(expressionString); + Expression expression = getParsedExpression(expressionString); EvaluationContext ctx = getEvaluationContext(expressionString); - variables.forEach((key, value) -> ctx.setVariable(key, value)); + variables.forEach(ctx::setVariable); Object result = expression.getValue(ctx, Object.class); return (T) result; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java index 4cd933f3d9..1ad1f31a37 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; import org.springframework.data.mapping.model.SpELExpressionEvaluator; +import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.spel.ExpressionDependencies; import org.springframework.data.util.Lazy; import org.springframework.expression.EvaluationContext; @@ -39,13 +40,15 @@ public class ParameterBindingContext { private final ValueProvider valueProvider; - private final SpELExpressionEvaluator expressionEvaluator; + private final ValueExpressionEvaluator expressionEvaluator; /** * @param valueProvider * @param expressionParser * @param evaluationContext + * @deprecated since 4.3, use {@link #ParameterBindingContext(ValueProvider, ExpressionParser, Supplier)} instead. */ + @Deprecated(since = "4.3") public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser, EvaluationContext evaluationContext) { this(valueProvider, expressionParser, () -> evaluationContext); @@ -59,15 +62,31 @@ public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser */ public ParameterBindingContext(ValueProvider valueProvider, ExpressionParser expressionParser, Supplier evaluationContext) { - this(valueProvider, new EvaluationContextExpressionEvaluator(valueProvider, expressionParser, evaluationContext)); + this(valueProvider, new EvaluationContextExpressionEvaluator(valueProvider, expressionParser) { + @Override + public EvaluationContext getEvaluationContext(String expressionString) { + return evaluationContext.get(); + } + }); } /** * @param valueProvider * @param expressionEvaluator * @since 3.1 + * @deprecated since 4.3, use {@link #ParameterBindingContext(ValueProvider, ValueExpressionEvaluator)} instead. */ + @Deprecated(since = "4.3") public ParameterBindingContext(ValueProvider valueProvider, SpELExpressionEvaluator expressionEvaluator) { + this(valueProvider, (ValueExpressionEvaluator) expressionEvaluator); + } + + /** + * @param valueProvider + * @param expressionEvaluator + * @since 4.3 + */ + public ParameterBindingContext(ValueProvider valueProvider, ValueExpressionEvaluator expressionEvaluator) { this.valueProvider = valueProvider; this.expressionEvaluator = expressionEvaluator; } @@ -86,7 +105,7 @@ public static ParameterBindingContext forExpressions(ValueProvider valueProvider Function contextFunction) { return new ParameterBindingContext(valueProvider, - new EvaluationContextExpressionEvaluator(valueProvider, expressionParser, null) { + new EvaluationContextExpressionEvaluator(valueProvider, expressionParser) { @Override public EvaluationContext getEvaluationContext(String expressionString) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java index 4c91aed656..f3cffaa798 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java @@ -40,7 +40,8 @@ import org.bson.codecs.*; import org.bson.codecs.configuration.CodecRegistry; import org.bson.json.JsonParseException; -import org.springframework.data.mapping.model.SpELExpressionEvaluator; + +import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.mapping.FieldName; import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.data.spel.ExpressionDependencies; @@ -384,7 +385,7 @@ private List readList(final BsonReader reader, final DecoderContext deco * @author Christoph Strobl * @since 3.1 */ - static class DependencyCapturingExpressionEvaluator implements SpELExpressionEvaluator { + static class DependencyCapturingExpressionEvaluator implements ValueExpressionEvaluator { private static final Object PLACEHOLDER = new Object(); diff --git a/src/main/antora/modules/ROOT/nav.adoc b/src/main/antora/modules/ROOT/nav.adoc index f308084bf8..3b499b8009 100644 --- a/src/main/antora/modules/ROOT/nav.adoc +++ b/src/main/antora/modules/ROOT/nav.adoc @@ -26,6 +26,7 @@ *** xref:mongodb/mapping/document-references.adoc[Object References] *** xref:mongodb/mapping/mapping-index-management.adoc[] +** xref:mongodb/value-expressions.adoc[] ** xref:mongodb/lifecycle-events.adoc[] ** xref:mongodb/auditing.adoc[] ** xref:mongodb/client-session-transactions.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/mongodb/value-expressions.adoc b/src/main/antora/modules/ROOT/pages/mongodb/value-expressions.adoc new file mode 100644 index 0000000000..6356a46265 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/mongodb/value-expressions.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$value-expressions.adoc[]