@@ -384,81 +384,6 @@ private Object doReadOrProject(ConversionContext context, Bson source, TypeInfor
384
384
return readDocument (context , source , typeHint );
385
385
}
386
386
387
- static class AssociationConversionContext implements ConversionContext {
388
-
389
- private final ConversionContext delegate ;
390
-
391
- public AssociationConversionContext (ConversionContext delegate ) {
392
- this .delegate = delegate ;
393
- }
394
-
395
- @ Override
396
- public <S > S convert (Object source , TypeInformation <? extends S > typeHint , ConversionContext context ) {
397
- return delegate .convert (source , typeHint , context );
398
- }
399
-
400
- @ Override
401
- public ConversionContext withPath (ObjectPath currentPath ) {
402
- return new AssociationConversionContext (delegate .withPath (currentPath ));
403
- }
404
-
405
- @ Override
406
- public ObjectPath getPath () {
407
- return delegate .getPath ();
408
- }
409
-
410
- @ Override
411
- public CustomConversions getCustomConversions () {
412
- return delegate .getCustomConversions ();
413
- }
414
-
415
- @ Override
416
- public MongoConverter getSourceConverter () {
417
- return delegate .getSourceConverter ();
418
- }
419
-
420
- @ Override
421
- public boolean resolveIdsInContext () {
422
- return true ;
423
- }
424
- }
425
-
426
- class ProjectingConversionContext extends DefaultConversionContext {
427
-
428
- private final EntityProjection <?, ?> returnedTypeDescriptor ;
429
-
430
- ProjectingConversionContext (MongoConverter sourceConverter , CustomConversions customConversions , ObjectPath path ,
431
- ContainerValueConverter <Collection <?>> collectionConverter , ContainerValueConverter <Bson > mapConverter ,
432
- ContainerValueConverter <DBRef > dbRefConverter , ValueConverter <Object > elementConverter ,
433
- EntityProjection <?, ?> projection ) {
434
- super (sourceConverter , customConversions , path ,
435
- (context , source , typeHint ) -> doReadOrProject (context , source , typeHint , projection ),
436
-
437
- collectionConverter , mapConverter , dbRefConverter , elementConverter );
438
- this .returnedTypeDescriptor = projection ;
439
- }
440
-
441
- @ Override
442
- public DefaultConversionContext forProperty (String name ) {
443
-
444
- EntityProjection <?, ?> property = returnedTypeDescriptor .findProperty (name );
445
- if (property == null ) {
446
- return new DefaultConversionContext (sourceConverter , conversions , path ,
447
- MappingMongoConverter .this ::readDocument , collectionConverter , mapConverter , dbRefConverter ,
448
- elementConverter );
449
- }
450
-
451
- return new ProjectingConversionContext (sourceConverter , conversions , path , collectionConverter , mapConverter ,
452
- dbRefConverter , elementConverter , property );
453
- }
454
-
455
- @ Override
456
- public DefaultConversionContext withPath (ObjectPath currentPath ) {
457
- return new ProjectingConversionContext (sourceConverter , conversions , currentPath , collectionConverter ,
458
- mapConverter , dbRefConverter , elementConverter , returnedTypeDescriptor );
459
- }
460
- }
461
-
462
387
static class MapPersistentPropertyAccessor implements PersistentPropertyAccessor <Map <String , Object >> {
463
388
464
389
Map <String , Object > map = new LinkedHashMap <>();
@@ -565,16 +490,14 @@ private ParameterValueProvider<MongoPersistentProperty> getParameterProvider(Con
565
490
566
491
private <S > S read (ConversionContext context , MongoPersistentEntity <S > entity , Document bson ) {
567
492
493
+ S existing = context .findContextualEntity (entity , bson );
494
+ if (existing != null ) {
495
+ return existing ;
496
+ }
497
+
568
498
SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator (bson , spELContext );
569
499
DocumentAccessor documentAccessor = new DocumentAccessor (bson );
570
500
571
- if (context .resolveIdsInContext () && hasIdentifier (bson )) {
572
- S existing = findContextualEntity (context , entity , bson );
573
- if (existing != null ) {
574
- return existing ;
575
- }
576
- }
577
-
578
501
PreferredConstructor <S , MongoPersistentProperty > persistenceConstructor = entity .getPersistenceConstructor ();
579
502
580
503
ParameterValueProvider <MongoPersistentProperty > provider = persistenceConstructor != null
@@ -592,16 +515,6 @@ private <S> S read(ConversionContext context, MongoPersistentEntity<S> entity, D
592
515
return instance ;
593
516
}
594
517
595
- private boolean hasIdentifier (Document bson ) {
596
- return bson .get (BasicMongoPersistentProperty .ID_FIELD_NAME ) != null ;
597
- }
598
-
599
- @ Nullable
600
- private <S > S findContextualEntity (ConversionContext context , MongoPersistentEntity <S > entity , Document bson ) {
601
- return context .getPath ().getPathItem (bson .get (BasicMongoPersistentProperty .ID_FIELD_NAME ), entity .getCollection (),
602
- entity .getType ());
603
- }
604
-
605
518
private <S > S populateProperties (ConversionContext context , MongoPersistentEntity <S > entity ,
606
519
DocumentAccessor documentAccessor , SpELExpressionEvaluator evaluator , S instance ) {
607
520
@@ -2197,40 +2110,130 @@ public TypeDescriptor toTypeDescriptor() {
2197
2110
}
2198
2111
}
2199
2112
2113
+ /**
2114
+ * Conversion context defining an interface for graph-traversal-based conversion of documents. Entrypoint for
2115
+ * recursive conversion of {@link Document} and other types.
2116
+ *
2117
+ * @since 3.4.3
2118
+ */
2200
2119
interface ConversionContext {
2201
2120
2121
+ /**
2122
+ * Converts a source object into {@link TypeInformation target}.
2123
+ *
2124
+ * @param source must not be {@literal null}.
2125
+ * @param typeHint must not be {@literal null}.
2126
+ * @return the converted object.
2127
+ */
2202
2128
default <S extends Object > S convert (Object source , TypeInformation <? extends S > typeHint ) {
2203
2129
return convert (source , typeHint , this );
2204
2130
}
2205
2131
2132
+ /**
2133
+ * Converts a source object into {@link TypeInformation target}.
2134
+ *
2135
+ * @param source must not be {@literal null}.
2136
+ * @param typeHint must not be {@literal null}.
2137
+ * @param context must not be {@literal null}.
2138
+ * @return the converted object.
2139
+ */
2206
2140
<S extends Object > S convert (Object source , TypeInformation <? extends S > typeHint , ConversionContext context );
2207
2141
2142
+ /**
2143
+ * Create a new {@link ConversionContext} with {@link ObjectPath currentPath} applied.
2144
+ *
2145
+ * @param currentPath must not be {@literal null}.
2146
+ * @return a new {@link ConversionContext} with {@link ObjectPath currentPath} applied.
2147
+ */
2208
2148
ConversionContext withPath (ObjectPath currentPath );
2209
2149
2210
- ObjectPath getPath ();
2211
-
2150
+ /**
2151
+ * Obtain a {@link ConversionContext} for the given property {@code name}.
2152
+ *
2153
+ * @param name must not be {@literal null}.
2154
+ * @return the {@link ConversionContext} to be used for conversion of the given property.
2155
+ */
2212
2156
default ConversionContext forProperty (String name ) {
2213
2157
return this ;
2214
2158
}
2215
2159
2216
- default ConversionContext forProperty (@ Nullable PersistentProperty property ) {
2160
+ /**
2161
+ * Obtain a {@link ConversionContext} for the given {@link MongoPersistentProperty}.
2162
+ *
2163
+ * @param property must not be {@literal null}.
2164
+ * @return the {@link ConversionContext} to be used for conversion of the given property.
2165
+ */
2166
+ default ConversionContext forProperty (MongoPersistentProperty property ) {
2217
2167
2218
- if (property != null ) {
2219
- if (property .isAssociation ()) {
2220
- return new AssociationConversionContext (forProperty (property .getName ()));
2221
- }
2222
- return forProperty (property .getName ());
2223
- }
2224
- return this ;
2168
+ return property .isAssociation () ? new AssociationConversionContext (forProperty (property .getName ()))
2169
+ : forProperty (property .getName ());
2225
2170
}
2226
2171
2227
- default boolean resolveIdsInContext () {
2228
- return false ;
2172
+ /**
2173
+ * Lookup a potentially existing entity instance of the given {@link MongoPersistentEntity} and {@link Document}
2174
+ *
2175
+ * @param entity
2176
+ * @param document
2177
+ * @return
2178
+ * @param <S>
2179
+ */
2180
+ @ Nullable
2181
+ default <S > S findContextualEntity (MongoPersistentEntity <S > entity , Document document ) {
2182
+ return null ;
2229
2183
}
2230
2184
2185
+ ObjectPath getPath ();
2186
+
2231
2187
CustomConversions getCustomConversions ();
2232
2188
2233
2189
MongoConverter getSourceConverter ();
2190
+
2191
+ }
2192
+
2193
+ /**
2194
+ * @since 3.4.3
2195
+ */
2196
+ static class AssociationConversionContext implements ConversionContext {
2197
+
2198
+ private final ConversionContext delegate ;
2199
+
2200
+ public AssociationConversionContext (ConversionContext delegate ) {
2201
+ this .delegate = delegate ;
2202
+ }
2203
+
2204
+ @ Override
2205
+ public <S > S convert (Object source , TypeInformation <? extends S > typeHint , ConversionContext context ) {
2206
+ return delegate .convert (source , typeHint , context );
2207
+ }
2208
+
2209
+ @ Override
2210
+ public ConversionContext withPath (ObjectPath currentPath ) {
2211
+ return new AssociationConversionContext (delegate .withPath (currentPath ));
2212
+ }
2213
+
2214
+ @ Override
2215
+ public <S > S findContextualEntity (MongoPersistentEntity <S > entity , Document document ) {
2216
+
2217
+ Object identifier = document .get (BasicMongoPersistentProperty .ID_FIELD_NAME );
2218
+
2219
+ return identifier != null ? getPath ().getPathItem (identifier , entity .getCollection (), entity .getType ()) : null ;
2220
+ }
2221
+
2222
+ @ Override
2223
+ public ObjectPath getPath () {
2224
+ return delegate .getPath ();
2225
+ }
2226
+
2227
+ @ Override
2228
+ public CustomConversions getCustomConversions () {
2229
+ return delegate .getCustomConversions ();
2230
+ }
2231
+
2232
+ @ Override
2233
+ public MongoConverter getSourceConverter () {
2234
+ return delegate .getSourceConverter ();
2235
+ }
2236
+
2234
2237
}
2235
2238
2236
2239
/**
@@ -2266,14 +2269,8 @@ protected static class DefaultConversionContext implements ConversionContext {
2266
2269
this .elementConverter = elementConverter ;
2267
2270
}
2268
2271
2269
- /**
2270
- * Converts a source object into {@link TypeInformation target}.
2271
- *
2272
- * @param source must not be {@literal null}.
2273
- * @param typeHint must not be {@literal null}.
2274
- * @return the converted object.
2275
- */
2276
2272
@ SuppressWarnings ("unchecked" )
2273
+ @ Override
2277
2274
public <S extends Object > S convert (Object source , TypeInformation <? extends S > typeHint ,
2278
2275
ConversionContext context ) {
2279
2276
@@ -2339,28 +2336,20 @@ public MongoConverter getSourceConverter() {
2339
2336
return sourceConverter ;
2340
2337
}
2341
2338
2342
- /**
2343
- * Create a new {@link DefaultConversionContext} with {@link ObjectPath currentPath} applied.
2344
- *
2345
- * @param currentPath must not be {@literal null}.
2346
- * @return a new {@link DefaultConversionContext} with {@link ObjectPath currentPath} applied.
2347
- */
2348
- public DefaultConversionContext withPath (ObjectPath currentPath ) {
2339
+ @ Override
2340
+ public ConversionContext withPath (ObjectPath currentPath ) {
2349
2341
2350
2342
Assert .notNull (currentPath , "ObjectPath must not be null" );
2351
2343
2352
2344
return new DefaultConversionContext (sourceConverter , conversions , currentPath , documentConverter ,
2353
2345
collectionConverter , mapConverter , dbRefConverter , elementConverter );
2354
2346
}
2355
2347
2348
+ @ Override
2356
2349
public ObjectPath getPath () {
2357
2350
return path ;
2358
2351
}
2359
2352
2360
- public DefaultConversionContext forProperty (String name ) {
2361
- return this ;
2362
- }
2363
-
2364
2353
/**
2365
2354
* Converts a simple {@code source} value into {@link TypeInformation the target type}.
2366
2355
*
@@ -2386,6 +2375,45 @@ interface ContainerValueConverter<T> {
2386
2375
2387
2376
}
2388
2377
2378
+ /**
2379
+ * @since 3.4.3
2380
+ */
2381
+ class ProjectingConversionContext extends DefaultConversionContext {
2382
+
2383
+ private final EntityProjection <?, ?> returnedTypeDescriptor ;
2384
+
2385
+ ProjectingConversionContext (MongoConverter sourceConverter , CustomConversions customConversions , ObjectPath path ,
2386
+ ContainerValueConverter <Collection <?>> collectionConverter , ContainerValueConverter <Bson > mapConverter ,
2387
+ ContainerValueConverter <DBRef > dbRefConverter , ValueConverter <Object > elementConverter ,
2388
+ EntityProjection <?, ?> projection ) {
2389
+ super (sourceConverter , customConversions , path ,
2390
+ (context , source , typeHint ) -> doReadOrProject (context , source , typeHint , projection ),
2391
+
2392
+ collectionConverter , mapConverter , dbRefConverter , elementConverter );
2393
+ this .returnedTypeDescriptor = projection ;
2394
+ }
2395
+
2396
+ @ Override
2397
+ public ConversionContext forProperty (String name ) {
2398
+
2399
+ EntityProjection <?, ?> property = returnedTypeDescriptor .findProperty (name );
2400
+ if (property == null ) {
2401
+ return new DefaultConversionContext (sourceConverter , conversions , path ,
2402
+ MappingMongoConverter .this ::readDocument , collectionConverter , mapConverter , dbRefConverter ,
2403
+ elementConverter );
2404
+ }
2405
+
2406
+ return new ProjectingConversionContext (sourceConverter , conversions , path , collectionConverter , mapConverter ,
2407
+ dbRefConverter , elementConverter , property );
2408
+ }
2409
+
2410
+ @ Override
2411
+ public ConversionContext withPath (ObjectPath currentPath ) {
2412
+ return new ProjectingConversionContext (sourceConverter , conversions , currentPath , collectionConverter ,
2413
+ mapConverter , dbRefConverter , elementConverter , returnedTypeDescriptor );
2414
+ }
2415
+ }
2416
+
2389
2417
private static class PropertyTranslatingPropertyAccessor <T > implements PersistentPropertyPathAccessor <T > {
2390
2418
2391
2419
private final PersistentPropertyAccessor <T > delegate ;
0 commit comments