diff --git a/pom.xml b/pom.xml
index b6410e6887..619a86e88c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-2860-SNAPSHOT
pom
Spring Data MongoDB
@@ -26,7 +26,7 @@
multi
spring-data-mongodb
- 2.7.0-SNAPSHOT
+ 2.7.0-GH-2420-SNAPSHOT
4.4.0
${mongo}
1.19
diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml
index e2704a6753..5acd398513 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
- 3.4.0-SNAPSHOT
+ 3.4.0-2860-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index b75f8bf624..4380676a50 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-2860-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index ca96626cc9..b0bdd93cf4 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-2860-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java
index 3bba17aaef..b5af067589 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java
@@ -23,13 +23,17 @@
import org.bson.Document;
import org.springframework.core.convert.ConversionService;
import org.springframework.dao.InvalidDataAccessApiUsageException;
+import org.springframework.data.convert.CustomConversions;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentPropertyAccessor;
+import org.springframework.data.mapping.context.EntityProjection;
+import org.springframework.data.mapping.context.EntityProjectionIntrospector;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mongodb.core.CollectionOptions.TimeSeriesOptions;
+import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.MongoWriter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
@@ -39,6 +43,7 @@
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.timeseries.Granularity;
+import org.springframework.data.projection.ProjectionFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -63,8 +68,19 @@ class EntityOperations {
private final MappingContext extends MongoPersistentEntity>, MongoPersistentProperty> context;
- EntityOperations(MappingContext extends MongoPersistentEntity>, MongoPersistentProperty> context) {
+ private final EntityProjectionIntrospector introspector;
+
+ EntityOperations(MongoConverter converter) {
+ this(converter.getMappingContext(), converter.getCustomConversions(), converter.getProjectionFactory());
+ }
+
+ EntityOperations(MappingContext extends MongoPersistentEntity>, MongoPersistentProperty> context,
+ CustomConversions conversions, ProjectionFactory projectionFactory) {
this.context = context;
+ this.introspector = EntityProjectionIntrospector.create(projectionFactory,
+ EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy()
+ .and(((target, underlyingType) -> !conversions.isSimpleType(target))),
+ context);
}
/**
@@ -229,6 +245,11 @@ public TypedOperations forType(@Nullable Class entityClass) {
return UntypedOperations.instance();
}
+ public EntityProjection introspectProjection(Class resultType,
+ Class entityType) {
+ return introspector.introspect(resultType, entityType);
+ }
+
/**
* A representation of information about an entity.
*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
index 895097dd47..15b6b5b790 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
@@ -49,6 +49,7 @@
import org.springframework.data.geo.Metric;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.callback.EntityCallbacks;
+import org.springframework.data.mapping.context.EntityProjection;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoDatabaseUtils;
@@ -102,7 +103,6 @@
import org.springframework.data.mongodb.core.timeseries.Granularity;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.util.CloseableIterator;
import org.springframework.data.util.Optionals;
import org.springframework.lang.Nullable;
@@ -173,7 +173,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private final QueryMapper queryMapper;
private final UpdateMapper updateMapper;
private final JsonSchemaMapper schemaMapper;
- private final SpelAwareProxyProjectionFactory projectionFactory;
private final EntityOperations operations;
private final PropertyOperations propertyOperations;
private final QueryOperations queryOperations;
@@ -225,8 +224,7 @@ public MongoTemplate(MongoDatabaseFactory mongoDbFactory, @Nullable MongoConvert
this.queryMapper = new QueryMapper(this.mongoConverter);
this.updateMapper = new UpdateMapper(this.mongoConverter);
this.schemaMapper = new MongoJsonSchemaMapper(this.mongoConverter);
- this.projectionFactory = new SpelAwareProxyProjectionFactory();
- this.operations = new EntityOperations(this.mongoConverter.getMappingContext());
+ this.operations = new EntityOperations(this.mongoConverter);
this.propertyOperations = new PropertyOperations(this.mongoConverter.getMappingContext());
this.queryOperations = new QueryOperations(queryMapper, updateMapper, operations, propertyOperations,
mongoDbFactory);
@@ -264,7 +262,6 @@ private MongoTemplate(MongoDatabaseFactory dbFactory, MongoTemplate that) {
this.queryMapper = that.queryMapper;
this.updateMapper = that.updateMapper;
this.schemaMapper = that.schemaMapper;
- this.projectionFactory = that.projectionFactory;
this.mappingContext = that.mappingContext;
this.operations = that.operations;
this.propertyOperations = that.propertyOperations;
@@ -330,9 +327,6 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
}
resourceLoader = applicationContext;
-
- projectionFactory.setBeanFactory(applicationContext);
- projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
}
/**
@@ -416,15 +410,17 @@ protected CloseableIterator doStream(Query query, Class> entityType, St
MongoPersistentEntity> persistentEntity = mappingContext.getPersistentEntity(entityType);
QueryContext queryContext = queryOperations.createQueryContext(query);
+ EntityProjection projection = operations.introspectProjection(returnType,
+ entityType);
Document mappedQuery = queryContext.getMappedQuery(persistentEntity);
- Document mappedFields = queryContext.getMappedFields(persistentEntity, returnType, projectionFactory);
+ Document mappedFields = queryContext.getMappedFields(persistentEntity, projection);
FindIterable cursor = new QueryCursorPreparer(query, entityType).initiateFind(collection,
col -> col.find(mappedQuery, Document.class).projection(mappedFields));
return new CloseableIterableCursorAdapter<>(cursor, exceptionTranslator,
- new ProjectingReadCallback<>(mongoConverter, entityType, returnType, collectionName));
+ new ProjectingReadCallback<>(mongoConverter, projection, collectionName));
});
}
@@ -964,9 +960,11 @@ public GeoResults geoNear(NearQuery near, Class> domainType, String col
.withOptions(AggregationOptions.builder().collation(near.getCollation()).build());
AggregationResults results = aggregate($geoNear, collection, Document.class);
+ EntityProjection projection = operations.introspectProjection(returnType,
+ domainType);
DocumentCallback> callback = new GeoNearResultDocumentCallback<>(distanceField,
- new ProjectingReadCallback<>(mongoConverter, domainType, returnType, collection), near.getMetric());
+ new ProjectingReadCallback<>(mongoConverter, projection, collection), near.getMetric());
List> result = new ArrayList<>();
@@ -1050,8 +1048,10 @@ public T findAndReplace(Query query, S replacement, FindAndReplaceOptions
MongoPersistentEntity> entity = mappingContext.getPersistentEntity(entityType);
QueryContext queryContext = queryOperations.createQueryContext(query);
+ EntityProjection projection = operations.introspectProjection(resultType,
+ entityType);
Document mappedQuery = queryContext.getMappedQuery(entity);
- Document mappedFields = queryContext.getMappedFields(entity, resultType, projectionFactory);
+ Document mappedFields = queryContext.getMappedFields(entity, projection);
Document mappedSort = queryContext.getMappedSort(entity);
replacement = maybeCallBeforeConvert(replacement, collectionName);
@@ -1061,7 +1061,8 @@ public T findAndReplace(Query query, S replacement, FindAndReplaceOptions
maybeCallBeforeSave(replacement, mappedReplacement, collectionName);
T saved = doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort,
- queryContext.getCollation(entityType).orElse(null), entityType, mappedReplacement, options, resultType);
+ queryContext.getCollation(entityType).orElse(null), entityType, mappedReplacement, options,
+ projection);
if (saved != null) {
maybeEmitEvent(new AfterSaveEvent<>(saved, mappedReplacement, collectionName));
@@ -2499,7 +2500,8 @@ protected T doFindOne(String collectionName, Document query, Document fields
MongoPersistentEntity> entity = mappingContext.getPersistentEntity(entityClass);
QueryContext queryContext = queryOperations.createQueryContext(new BasicQuery(query, fields));
- Document mappedFields = queryContext.getMappedFields(entity, entityClass, projectionFactory);
+ Document mappedFields = queryContext.getMappedFields(entity,
+ EntityProjection.nonProjecting(entityClass));
Document mappedQuery = queryContext.getMappedQuery(entity);
if (LOGGER.isDebugEnabled()) {
@@ -2551,7 +2553,8 @@ protected List doFind(String collectionName, Document query, Document
MongoPersistentEntity> entity = mappingContext.getPersistentEntity(entityClass);
QueryContext queryContext = queryOperations.createQueryContext(new BasicQuery(query, fields));
- Document mappedFields = queryContext.getMappedFields(entity, entityClass, projectionFactory);
+ Document mappedFields = queryContext.getMappedFields(entity,
+ EntityProjection.nonProjecting(entityClass));
Document mappedQuery = queryContext.getMappedQuery(entity);
if (LOGGER.isDebugEnabled()) {
@@ -2573,9 +2576,11 @@ List doFind(String collectionName, Document query, Document fields, Cl
Class targetClass, CursorPreparer preparer) {
MongoPersistentEntity> entity = mappingContext.getPersistentEntity(sourceClass);
+ EntityProjection projection = operations.introspectProjection(targetClass,
+ sourceClass);
QueryContext queryContext = queryOperations.createQueryContext(new BasicQuery(query, fields));
- Document mappedFields = queryContext.getMappedFields(entity, targetClass, projectionFactory);
+ Document mappedFields = queryContext.getMappedFields(entity, projection);
Document mappedQuery = queryContext.getMappedQuery(entity);
if (LOGGER.isDebugEnabled()) {
@@ -2584,9 +2589,10 @@ List doFind(String collectionName, Document query, Document fields, Cl
}
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields, null), preparer,
- new ProjectingReadCallback<>(mongoConverter, sourceClass, targetClass, collectionName), collectionName);
+ new ProjectingReadCallback<>(mongoConverter, projection, collectionName), collectionName);
}
+
/**
* Convert given {@link CollectionOptions} to a document and take the domain type information into account when
* creating a mapped schema for validation.
@@ -2745,6 +2751,35 @@ protected T doFindAndReplace(String collectionName, Document mappedQuery, Do
Document mappedSort, @Nullable com.mongodb.client.model.Collation collation, Class> entityType,
Document replacement, FindAndReplaceOptions options, Class resultType) {
+ EntityProjection projection = operations.introspectProjection(resultType,
+ entityType);
+
+ return doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort, collation, entityType, replacement,
+ options, projection);
+ }
+
+ /**
+ * Customize this part for findAndReplace.
+ *
+ * @param collectionName The name of the collection to perform the operation in.
+ * @param mappedQuery the query to look up documents.
+ * @param mappedFields the fields to project the result to.
+ * @param mappedSort the sort to be applied when executing the query.
+ * @param collation collation settings for the query. Can be {@literal null}.
+ * @param entityType the source domain type.
+ * @param replacement the replacement {@link Document}.
+ * @param options applicable options.
+ * @param projection the projection descriptor.
+ * @return {@literal null} if object does not exist, {@link FindAndReplaceOptions#isReturnNew() return new} is
+ * {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
+ * @since 3.4
+ */
+ @Nullable
+ private T doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
+ Document mappedSort, @Nullable com.mongodb.client.model.Collation collation, Class> entityType,
+ Document replacement, FindAndReplaceOptions options,
+ EntityProjection projection) {
+
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format(
"findAndReplace using query: %s fields: %s sort: %s for class: %s and replacement: %s " + "in collection: %s",
@@ -2754,7 +2789,7 @@ protected T doFindAndReplace(String collectionName, Document mappedQuery, Do
return executeFindOneInternal(
new FindAndReplaceCallback(mappedQuery, mappedFields, mappedSort, replacement, collation, options),
- new ProjectingReadCallback<>(mongoConverter, entityType, resultType, collectionName), collectionName);
+ new ProjectingReadCallback<>(mongoConverter, projection, collectionName), collectionName);
}
/**
@@ -3205,17 +3240,15 @@ public T doWith(Document document) {
*/
private class ProjectingReadCallback implements DocumentCallback {
- private final EntityReader