diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java index f0153a1b2..183c1b963 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java @@ -171,6 +171,7 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper implements M this.typeKey = typeKey; } + @Override public Alias readAliasFrom(Bson source) { if (source instanceof List) { 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 0c4d396cd..2135a7685 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 @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.BiPredicate; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -130,6 +131,23 @@ public class MappingMongoConverter extends AbstractMongoConverter 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"; + private static final BiPredicate, MongoPersistentProperty> PROPERTY_FILTER = (e, + property) -> { + + if (property.isIdProperty()) { + return false; + } + + if (e.isCreatorArgument(property)) { + return false; + } + + if (!property.isReadable()) { + return false; + } + return true; + }; + public static final TypeInformation BSON = TypeInformation.of(Bson.class); protected static final Log LOGGER = LogFactory.getLog(MappingMongoConverter.class); @@ -368,7 +386,7 @@ public class MappingMongoConverter extends AbstractMongoConverter evaluator, spELContext); readProperties(context, entity, convertingAccessor, documentAccessor, valueProvider, evaluator, - Predicates.isTrue()); + (mongoPersistentProperties, mongoPersistentProperty) -> true); return (R) projectionFactory.createProjection(mappedType.getType(), accessor.getBean()); } @@ -518,6 +536,23 @@ public class MappingMongoConverter extends AbstractMongoConverter parameterProvider); } + class EvaluatingDocumentAccessor extends DocumentAccessor implements ValueExpressionEvaluator { + + /** + * Creates a new {@link DocumentAccessor} for the given {@link Document}. + * + * @param document must be a {@link Document} effectively, must not be {@literal null}. + */ + public EvaluatingDocumentAccessor(Bson document) { + super(document); + } + + @Override + public T evaluate(String expression) { + return expressionEvaluatorFactory.create(getDocument()).evaluate(expression); + } + } + private S read(ConversionContext context, MongoPersistentEntity entity, Document bson) { S existing = context.findContextualEntity(entity, bson); @@ -525,19 +560,18 @@ public class MappingMongoConverter extends AbstractMongoConverter return existing; } - ValueExpressionEvaluator evaluator = expressionEvaluatorFactory.create(bson); - DocumentAccessor documentAccessor = new DocumentAccessor(bson); - + EvaluatingDocumentAccessor documentAccessor = new EvaluatingDocumentAccessor(bson); InstanceCreatorMetadata instanceCreatorMetadata = entity.getInstanceCreatorMetadata(); ParameterValueProvider provider = instanceCreatorMetadata != null - && instanceCreatorMetadata.hasParameters() ? getParameterProvider(context, entity, documentAccessor, evaluator) + && instanceCreatorMetadata.hasParameters() + ? getParameterProvider(context, entity, documentAccessor, documentAccessor) : NoOpParameterValueProvider.INSTANCE; EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); S instance = instantiator.createInstance(entity, provider); - return populateProperties(context, entity, documentAccessor, evaluator, instance); + return populateProperties(context, entity, documentAccessor, documentAccessor, instance); } private S populateProperties(ConversionContext context, MongoPersistentEntity entity, @@ -559,9 +593,7 @@ public class MappingMongoConverter extends AbstractMongoConverter MongoDbPropertyValueProvider valueProvider = new MongoDbPropertyValueProvider(contextToUse, documentAccessor, evaluator, spELContext); - Predicate propertyFilter = isIdentifier(entity).or(isConstructorArgument(entity)) - .or(Predicates.negate(PersistentProperty::isReadable)).negate(); - readProperties(contextToUse, entity, accessor, documentAccessor, valueProvider, evaluator, propertyFilter); + readProperties(contextToUse, entity, accessor, documentAccessor, valueProvider, evaluator, PROPERTY_FILTER); return accessor.getBean(); } @@ -606,13 +638,13 @@ public class MappingMongoConverter extends AbstractMongoConverter private void readProperties(ConversionContext context, MongoPersistentEntity entity, PersistentPropertyAccessor accessor, DocumentAccessor documentAccessor, MongoDbPropertyValueProvider valueProvider, ValueExpressionEvaluator evaluator, - Predicate propertyFilter) { + BiPredicate, MongoPersistentProperty> propertyFilter) { DbRefResolverCallback callback = null; for (MongoPersistentProperty prop : entity) { - if (!propertyFilter.test(prop)) { + if (!propertyFilter.test(entity, prop)) { continue; } @@ -1943,10 +1975,6 @@ public class MappingMongoConverter extends AbstractMongoConverter MongoDbPropertyValueProvider(ConversionContext context, DocumentAccessor accessor, ValueExpressionEvaluator evaluator, SpELContext spELContext) { - Assert.notNull(context, "ConversionContext must no be null"); - Assert.notNull(accessor, "DocumentAccessor must no be null"); - Assert.notNull(evaluator, "ValueExpressionEvaluator must not be null"); - this.context = context; this.accessor = accessor; this.evaluator = evaluator; @@ -2359,9 +2387,6 @@ public class MappingMongoConverter extends AbstractMongoConverter public S convert(Object source, TypeInformation typeHint, ConversionContext context) { - Assert.notNull(source, "Source must not be null"); - Assert.notNull(typeHint, "TypeInformation must not be null"); - if (conversions.hasCustomReadTarget(source.getClass(), typeHint.getType())) { return (S) elementConverter.convert(source, typeHint); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java index fce1fbfb8..4eaae5371 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java @@ -30,6 +30,8 @@ import org.springframework.lang.Nullable; */ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty { + private final Lazy isEntity = Lazy.of(super::isEntity); + private final Lazy isUnwrapped = Lazy.of(super::isUnwrapped); private final Lazy isIdProperty = Lazy.of(super::isIdProperty); private final Lazy isAssociation = Lazy.of(super::isAssociation); private final Lazy dbref = Lazy.of(super::getDBRef); @@ -58,6 +60,16 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty super(property, owner, simpleTypeHolder, fieldNamingStrategy); } + @Override + public boolean isEntity() { + return isEntity.get(); + } + + @Override + public boolean isUnwrapped() { + return isUnwrapped.get(); + } + @Override public boolean isIdProperty() { return isIdProperty.get(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldName.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldName.java index a6d2c1cff..6269a024e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldName.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldName.java @@ -25,7 +25,11 @@ import org.springframework.util.ObjectUtils; * @author Christoph Strobl * @since 4.2 */ -public record FieldName(String name, Type type) { +public record FieldName(String name, Type type, String[] parts) { + + public FieldName(String name, Type type) { + this(name, type, name.split("\\.")); + } private static final String ID_KEY = "_id"; @@ -65,7 +69,7 @@ public record FieldName(String name, Type type) { return new String[] { name }; } - return name.split("\\."); + return parts; } /**