4949import org .springframework .data .geo .Metric ;
5050import org .springframework .data .mapping .MappingException ;
5151import org .springframework .data .mapping .callback .EntityCallbacks ;
52+ import org .springframework .data .mapping .context .EntityProjection ;
5253import org .springframework .data .mapping .context .MappingContext ;
5354import org .springframework .data .mongodb .MongoDatabaseFactory ;
5455import org .springframework .data .mongodb .MongoDatabaseUtils ;
102103import org .springframework .data .mongodb .core .timeseries .Granularity ;
103104import org .springframework .data .mongodb .core .validation .Validator ;
104105import org .springframework .data .mongodb .util .BsonUtils ;
105- import org .springframework .data .projection .SpelAwareProxyProjectionFactory ;
106106import org .springframework .data .util .CloseableIterator ;
107107import org .springframework .data .util .Optionals ;
108108import org .springframework .lang .Nullable ;
@@ -173,7 +173,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
173173 private final QueryMapper queryMapper ;
174174 private final UpdateMapper updateMapper ;
175175 private final JsonSchemaMapper schemaMapper ;
176- private final SpelAwareProxyProjectionFactory projectionFactory ;
177176 private final EntityOperations operations ;
178177 private final PropertyOperations propertyOperations ;
179178 private final QueryOperations queryOperations ;
@@ -225,8 +224,7 @@ public MongoTemplate(MongoDatabaseFactory mongoDbFactory, @Nullable MongoConvert
225224 this .queryMapper = new QueryMapper (this .mongoConverter );
226225 this .updateMapper = new UpdateMapper (this .mongoConverter );
227226 this .schemaMapper = new MongoJsonSchemaMapper (this .mongoConverter );
228- this .projectionFactory = new SpelAwareProxyProjectionFactory ();
229- this .operations = new EntityOperations (this .mongoConverter .getMappingContext ());
227+ this .operations = new EntityOperations (this .mongoConverter );
230228 this .propertyOperations = new PropertyOperations (this .mongoConverter .getMappingContext ());
231229 this .queryOperations = new QueryOperations (queryMapper , updateMapper , operations , propertyOperations ,
232230 mongoDbFactory );
@@ -264,7 +262,6 @@ private MongoTemplate(MongoDatabaseFactory dbFactory, MongoTemplate that) {
264262 this .queryMapper = that .queryMapper ;
265263 this .updateMapper = that .updateMapper ;
266264 this .schemaMapper = that .schemaMapper ;
267- this .projectionFactory = that .projectionFactory ;
268265 this .mappingContext = that .mappingContext ;
269266 this .operations = that .operations ;
270267 this .propertyOperations = that .propertyOperations ;
@@ -330,9 +327,6 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
330327 }
331328
332329 resourceLoader = applicationContext ;
333-
334- projectionFactory .setBeanFactory (applicationContext );
335- projectionFactory .setBeanClassLoader (applicationContext .getClassLoader ());
336330 }
337331
338332 /**
@@ -416,15 +410,17 @@ protected <T> CloseableIterator<T> doStream(Query query, Class<?> entityType, St
416410 MongoPersistentEntity <?> persistentEntity = mappingContext .getPersistentEntity (entityType );
417411
418412 QueryContext queryContext = queryOperations .createQueryContext (query );
413+ EntityProjection <T , ?> projection = operations .introspectProjection (returnType ,
414+ entityType );
419415
420416 Document mappedQuery = queryContext .getMappedQuery (persistentEntity );
421- Document mappedFields = queryContext .getMappedFields (persistentEntity , returnType , projectionFactory );
417+ Document mappedFields = queryContext .getMappedFields (persistentEntity , projection );
422418
423419 FindIterable <Document > cursor = new QueryCursorPreparer (query , entityType ).initiateFind (collection ,
424420 col -> col .find (mappedQuery , Document .class ).projection (mappedFields ));
425421
426422 return new CloseableIterableCursorAdapter <>(cursor , exceptionTranslator ,
427- new ProjectingReadCallback <>(mongoConverter , entityType , returnType , collectionName ));
423+ new ProjectingReadCallback <>(mongoConverter , projection , collectionName ));
428424 });
429425 }
430426
@@ -964,9 +960,11 @@ public <T> GeoResults<T> geoNear(NearQuery near, Class<?> domainType, String col
964960 .withOptions (AggregationOptions .builder ().collation (near .getCollation ()).build ());
965961
966962 AggregationResults <Document > results = aggregate ($geoNear , collection , Document .class );
963+ EntityProjection <T , ?> projection = operations .introspectProjection (returnType ,
964+ domainType );
967965
968966 DocumentCallback <GeoResult <T >> callback = new GeoNearResultDocumentCallback <>(distanceField ,
969- new ProjectingReadCallback <>(mongoConverter , domainType , returnType , collection ), near .getMetric ());
967+ new ProjectingReadCallback <>(mongoConverter , projection , collection ), near .getMetric ());
970968
971969 List <GeoResult <T >> result = new ArrayList <>();
972970
@@ -1050,8 +1048,10 @@ public <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions
10501048 MongoPersistentEntity <?> entity = mappingContext .getPersistentEntity (entityType );
10511049 QueryContext queryContext = queryOperations .createQueryContext (query );
10521050
1051+ EntityProjection <T , S > projection = operations .introspectProjection (resultType ,
1052+ entityType );
10531053 Document mappedQuery = queryContext .getMappedQuery (entity );
1054- Document mappedFields = queryContext .getMappedFields (entity , resultType , projectionFactory );
1054+ Document mappedFields = queryContext .getMappedFields (entity , projection );
10551055 Document mappedSort = queryContext .getMappedSort (entity );
10561056
10571057 replacement = maybeCallBeforeConvert (replacement , collectionName );
@@ -1061,7 +1061,8 @@ public <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions
10611061 maybeCallBeforeSave (replacement , mappedReplacement , collectionName );
10621062
10631063 T saved = doFindAndReplace (collectionName , mappedQuery , mappedFields , mappedSort ,
1064- queryContext .getCollation (entityType ).orElse (null ), entityType , mappedReplacement , options , resultType );
1064+ queryContext .getCollation (entityType ).orElse (null ), entityType , mappedReplacement , options ,
1065+ projection );
10651066
10661067 if (saved != null ) {
10671068 maybeEmitEvent (new AfterSaveEvent <>(saved , mappedReplacement , collectionName ));
@@ -2499,7 +2500,8 @@ protected <T> T doFindOne(String collectionName, Document query, Document fields
24992500 MongoPersistentEntity <?> entity = mappingContext .getPersistentEntity (entityClass );
25002501
25012502 QueryContext queryContext = queryOperations .createQueryContext (new BasicQuery (query , fields ));
2502- Document mappedFields = queryContext .getMappedFields (entity , entityClass , projectionFactory );
2503+ Document mappedFields = queryContext .getMappedFields (entity ,
2504+ EntityProjection .nonProjecting (entityClass ));
25032505 Document mappedQuery = queryContext .getMappedQuery (entity );
25042506
25052507 if (LOGGER .isDebugEnabled ()) {
@@ -2551,7 +2553,8 @@ protected <S, T> List<T> doFind(String collectionName, Document query, Document
25512553 MongoPersistentEntity <?> entity = mappingContext .getPersistentEntity (entityClass );
25522554
25532555 QueryContext queryContext = queryOperations .createQueryContext (new BasicQuery (query , fields ));
2554- Document mappedFields = queryContext .getMappedFields (entity , entityClass , projectionFactory );
2556+ Document mappedFields = queryContext .getMappedFields (entity ,
2557+ EntityProjection .nonProjecting (entityClass ));
25552558 Document mappedQuery = queryContext .getMappedQuery (entity );
25562559
25572560 if (LOGGER .isDebugEnabled ()) {
@@ -2573,9 +2576,11 @@ <S, T> List<T> doFind(String collectionName, Document query, Document fields, Cl
25732576 Class <T > targetClass , CursorPreparer preparer ) {
25742577
25752578 MongoPersistentEntity <?> entity = mappingContext .getPersistentEntity (sourceClass );
2579+ EntityProjection <T , S > projection = operations .introspectProjection (targetClass ,
2580+ sourceClass );
25762581
25772582 QueryContext queryContext = queryOperations .createQueryContext (new BasicQuery (query , fields ));
2578- Document mappedFields = queryContext .getMappedFields (entity , targetClass , projectionFactory );
2583+ Document mappedFields = queryContext .getMappedFields (entity , projection );
25792584 Document mappedQuery = queryContext .getMappedQuery (entity );
25802585
25812586 if (LOGGER .isDebugEnabled ()) {
@@ -2584,9 +2589,10 @@ <S, T> List<T> doFind(String collectionName, Document query, Document fields, Cl
25842589 }
25852590
25862591 return executeFindMultiInternal (new FindCallback (mappedQuery , mappedFields , null ), preparer ,
2587- new ProjectingReadCallback <>(mongoConverter , sourceClass , targetClass , collectionName ), collectionName );
2592+ new ProjectingReadCallback <>(mongoConverter , projection , collectionName ), collectionName );
25882593 }
25892594
2595+
25902596 /**
25912597 * Convert given {@link CollectionOptions} to a document and take the domain type information into account when
25922598 * creating a mapped schema for validation. <br />
@@ -2745,6 +2751,35 @@ protected <T> T doFindAndReplace(String collectionName, Document mappedQuery, Do
27452751 Document mappedSort , @ Nullable com .mongodb .client .model .Collation collation , Class <?> entityType ,
27462752 Document replacement , FindAndReplaceOptions options , Class <T > resultType ) {
27472753
2754+ EntityProjection <T , ?> projection = operations .introspectProjection (resultType ,
2755+ entityType );
2756+
2757+ return doFindAndReplace (collectionName , mappedQuery , mappedFields , mappedSort , collation , entityType , replacement ,
2758+ options , projection );
2759+ }
2760+
2761+ /**
2762+ * Customize this part for findAndReplace.
2763+ *
2764+ * @param collectionName The name of the collection to perform the operation in.
2765+ * @param mappedQuery the query to look up documents.
2766+ * @param mappedFields the fields to project the result to.
2767+ * @param mappedSort the sort to be applied when executing the query.
2768+ * @param collation collation settings for the query. Can be {@literal null}.
2769+ * @param entityType the source domain type.
2770+ * @param replacement the replacement {@link Document}.
2771+ * @param options applicable options.
2772+ * @param projection the projection descriptor.
2773+ * @return {@literal null} if object does not exist, {@link FindAndReplaceOptions#isReturnNew() return new} is
2774+ * {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
2775+ * @since 3.4
2776+ */
2777+ @ Nullable
2778+ private <T > T doFindAndReplace (String collectionName , Document mappedQuery , Document mappedFields ,
2779+ Document mappedSort , @ Nullable com .mongodb .client .model .Collation collation , Class <?> entityType ,
2780+ Document replacement , FindAndReplaceOptions options ,
2781+ EntityProjection <T , ?> projection ) {
2782+
27482783 if (LOGGER .isDebugEnabled ()) {
27492784 LOGGER .debug (String .format (
27502785 "findAndReplace using query: %s fields: %s sort: %s for class: %s and replacement: %s " + "in collection: %s" ,
@@ -2754,7 +2789,7 @@ protected <T> T doFindAndReplace(String collectionName, Document mappedQuery, Do
27542789
27552790 return executeFindOneInternal (
27562791 new FindAndReplaceCallback (mappedQuery , mappedFields , mappedSort , replacement , collation , options ),
2757- new ProjectingReadCallback <>(mongoConverter , entityType , resultType , collectionName ), collectionName );
2792+ new ProjectingReadCallback <>(mongoConverter , projection , collectionName ), collectionName );
27582793 }
27592794
27602795 /**
@@ -3205,17 +3240,15 @@ public T doWith(Document document) {
32053240 */
32063241 private class ProjectingReadCallback <S , T > implements DocumentCallback <T > {
32073242
3208- private final EntityReader <Object , Bson > reader ;
3209- private final Class <S > entityType ;
3210- private final Class <T > targetType ;
3243+ private final MongoConverter reader ;
3244+ private final EntityProjection <T , S > projection ;
32113245 private final String collectionName ;
32123246
3213- ProjectingReadCallback (EntityReader < Object , Bson > reader , Class < S > entityType , Class < T > targetType ,
3247+ ProjectingReadCallback (MongoConverter reader , EntityProjection < T , S > projection ,
32143248 String collectionName ) {
32153249
32163250 this .reader = reader ;
3217- this .entityType = entityType ;
3218- this .targetType = targetType ;
3251+ this .projection = projection ;
32193252 this .collectionName = collectionName ;
32203253 }
32213254
@@ -3230,21 +3263,16 @@ public T doWith(Document document) {
32303263 return null ;
32313264 }
32323265
3233- Class <?> typeToRead = targetType .isInterface () || targetType .isAssignableFrom (entityType ) ? entityType
3234- : targetType ;
3266+ maybeEmitEvent (new AfterLoadEvent <>(document , projection .getMappedType ().getType (), collectionName ));
32353267
3236- maybeEmitEvent (new AfterLoadEvent <>(document , targetType , collectionName ));
3237-
3238- Object entity = reader .read (typeToRead , document );
3268+ Object entity = reader .project (projection , document );
32393269
32403270 if (entity == null ) {
32413271 throw new MappingException (String .format ("EntityReader %s returned null" , reader ));
32423272 }
32433273
3244- Object result = targetType .isInterface () ? projectionFactory .createProjection (targetType , entity ) : entity ;
3245-
3246- maybeEmitEvent (new AfterConvertEvent <>(document , result , collectionName ));
3247- return (T ) maybeCallAfterConvert (result , document , collectionName );
3274+ maybeEmitEvent (new AfterConvertEvent <>(document , entity , collectionName ));
3275+ return (T ) maybeCallAfterConvert (entity , document , collectionName );
32483276 }
32493277 }
32503278
0 commit comments