20
20
import java .util .Collection ;
21
21
import java .util .Collections ;
22
22
import java .util .HashSet ;
23
+ import java .util .Iterator ;
23
24
import java .util .List ;
24
25
import java .util .Map ;
25
26
import java .util .Map .Entry ;
@@ -194,6 +195,8 @@ protected <S extends Object> S read(TypeInformation<S> type, DBObject dbo, Objec
194
195
return null ;
195
196
}
196
197
198
+ Object parentObject = ObjectPath .unwrapCurrentFromPotentialPath (parent );
199
+
197
200
TypeInformation <? extends S > typeToUse = typeMapper .readType (dbo , type );
198
201
Class <? extends S > rawType = typeToUse .getType ();
199
202
@@ -206,11 +209,11 @@ protected <S extends Object> S read(TypeInformation<S> type, DBObject dbo, Objec
206
209
}
207
210
208
211
if (typeToUse .isCollectionLike () && dbo instanceof BasicDBList ) {
209
- return (S ) readCollectionOrArray (typeToUse , (BasicDBList ) dbo , parent );
212
+ return (S ) readCollectionOrArray (typeToUse , (BasicDBList ) dbo , parentObject );
210
213
}
211
214
212
215
if (typeToUse .isMap ()) {
213
- return (S ) readMap (typeToUse , dbo , parent );
216
+ return (S ) readMap (typeToUse , dbo , parentObject );
214
217
}
215
218
216
219
// Retrieve persistent entity info
@@ -238,22 +241,37 @@ private <S extends Object> S read(final MongoPersistentEntity<S> entity, final D
238
241
239
242
final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator (dbo , spELContext );
240
243
241
- ParameterValueProvider <MongoPersistentProperty > provider = getParameterProvider (entity , dbo , evaluator , parent );
244
+ final ObjectPath parentPath = ObjectPath .toPath (parent );
245
+
246
+ ParameterValueProvider <MongoPersistentProperty > provider = getParameterProvider (entity , dbo , evaluator ,
247
+ parentPath .getCurrentObject ());
242
248
EntityInstantiator instantiator = instantiators .getInstantiatorFor (entity );
243
249
S instance = instantiator .createInstance (entity , provider );
244
250
245
251
final BeanWrapper <S > wrapper = BeanWrapper .create (instance , conversionService );
246
252
final S result = wrapper .getBean ();
247
253
254
+ // make sure id property is set before all other properties
255
+ if (entity .getIdProperty () != null ) {
256
+ wrapper .setProperty (entity .getIdProperty (), getValueInternal (entity .getIdProperty (), dbo , evaluator , result ));
257
+ }
258
+
259
+ final ObjectPath currentPath = parentPath .push (result );
260
+
248
261
// Set properties not already set in the constructor
249
262
entity .doWithProperties (new PropertyHandler <MongoPersistentProperty >() {
250
263
public void doWithPersistentProperty (MongoPersistentProperty prop ) {
251
264
265
+ // we skip the id property since it was already set
266
+ if (entity .getIdProperty () != null && entity .getIdProperty ().equals (prop )) {
267
+ return ;
268
+ }
269
+
252
270
if (!dbo .containsField (prop .getFieldName ()) || entity .isConstructorArgument (prop )) {
253
271
return ;
254
272
}
255
273
256
- wrapper .setProperty (prop , getValueInternal (prop , dbo , evaluator , result ));
274
+ wrapper .setProperty (prop , getValueInternal (prop , dbo , evaluator , currentPath . getCurrentObject () ));
257
275
}
258
276
});
259
277
@@ -273,7 +291,7 @@ public void doWithAssociation(Association<MongoPersistentProperty> association)
273
291
274
292
@ Override
275
293
public Object resolve (MongoPersistentProperty property ) {
276
- return getValueInternal (property , dbo , evaluator , parent );
294
+ return getValueInternal (property , dbo , evaluator , currentPath );
277
295
}
278
296
}));
279
297
}
@@ -1097,21 +1115,75 @@ protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, MongoPer
1097
1115
@ SuppressWarnings ("unchecked" )
1098
1116
private <T > T readValue (Object value , TypeInformation <?> type , Object parent ) {
1099
1117
1118
+ ObjectPath objectStack = ObjectPath .toPath (parent );
1119
+
1100
1120
Class <?> rawType = type .getType ();
1101
1121
1102
1122
if (conversions .hasCustomReadTarget (value .getClass (), rawType )) {
1103
1123
return (T ) conversionService .convert (value , rawType );
1104
1124
} else if (value instanceof DBRef ) {
1105
- return ( T ) ( rawType . equals ( DBRef . class ) ? value : read ( type , readRef (( DBRef ) value ), parent ) );
1125
+ return potentiallyReadOrResolveDbRef (( DBRef ) value , type , objectStack , rawType );
1106
1126
} else if (value instanceof BasicDBList ) {
1107
- return (T ) readCollectionOrArray (type , (BasicDBList ) value , parent );
1127
+ return (T ) readCollectionOrArray (type , (BasicDBList ) value , objectStack . getCurrentObject () );
1108
1128
} else if (value instanceof DBObject ) {
1109
- return (T ) read (type , (DBObject ) value , parent );
1129
+ return (T ) read (type , (DBObject ) value , objectStack . getCurrentObject () );
1110
1130
} else {
1111
1131
return (T ) getPotentiallyConvertedSimpleRead (value , rawType );
1112
1132
}
1113
1133
}
1114
1134
1135
+ @ SuppressWarnings ("unchecked" )
1136
+ private <T > T potentiallyReadOrResolveDbRef (DBRef dbref , TypeInformation <?> type , ObjectPath path , Class <?> rawType ) {
1137
+
1138
+ if (rawType .equals (DBRef .class )) {
1139
+ return (T ) dbref ;
1140
+ }
1141
+
1142
+ Object object = getObjectFromPathForRefOrNull (path , dbref );
1143
+
1144
+ if (object != null ) {
1145
+ return (T ) object ;
1146
+ }
1147
+
1148
+ return (T ) (object != null ? object : read (type , readRef (dbref ), path .getCurrentObject ()));
1149
+ }
1150
+
1151
+ /**
1152
+ * Returns the object from the given {@link ObjectPath} iff the given {@link DBRef} points to it or {@literal null}.
1153
+ *
1154
+ * @param path
1155
+ * @param dbref
1156
+ * @return
1157
+ */
1158
+ private Object getObjectFromPathForRefOrNull (ObjectPath path , DBRef dbref ) {
1159
+
1160
+ if (path == null || dbref == null ) {
1161
+ return null ;
1162
+ }
1163
+
1164
+ for (Object object : path ) {
1165
+
1166
+ if (object == null ) {
1167
+ continue ;
1168
+ }
1169
+
1170
+ MongoPersistentEntity <?> pe = getMappingContext ().getPersistentEntity (object .getClass ());
1171
+
1172
+ if (pe == null || pe .getIdProperty () == null ) {
1173
+ continue ;
1174
+ }
1175
+
1176
+ BeanWrapper <Object > parentBeanWrapper = BeanWrapper .create (object , conversionService );
1177
+ Object parentId = parentBeanWrapper .getProperty (pe .getIdProperty ());
1178
+
1179
+ if (dbref .getRef ().equals (pe .getCollection ()) && dbref .getId ().equals (parentId )) {
1180
+ return object ;
1181
+ }
1182
+ }
1183
+
1184
+ return null ;
1185
+ }
1186
+
1115
1187
/**
1116
1188
* Performs the fetch operation for the given {@link DBRef}.
1117
1189
*
@@ -1121,4 +1193,70 @@ private <T> T readValue(Object value, TypeInformation<?> type, Object parent) {
1121
1193
DBObject readRef (DBRef ref ) {
1122
1194
return ref .fetch ();
1123
1195
}
1196
+
1197
+ /**
1198
+ * An immutable ordered set of target objects for {@link DBObject} to {@link Object} conversions. Object paths can be
1199
+ * constructed by the {@link #toPath(Object)} method and extended via {@link #push(Object)}.
1200
+ *
1201
+ * @author Thomas Darimont
1202
+ * @since 1.6
1203
+ */
1204
+ static class ObjectPath implements Iterable <Object > {
1205
+
1206
+ private final List <Object > stack ;
1207
+
1208
+ private ObjectPath (Object current ) {
1209
+ this (null , current );
1210
+ }
1211
+
1212
+ private ObjectPath (ObjectPath parent , Object current ) {
1213
+
1214
+ if (parent == null ) {
1215
+ this .stack = Collections .singletonList (current );
1216
+ } else {
1217
+ List <Object > list = new ArrayList <Object >(parent .stack );
1218
+ list .add (current );
1219
+ this .stack = list ;
1220
+ }
1221
+ }
1222
+
1223
+ /**
1224
+ * Returns a copy of the {@link ObjectPath} with the given {@link Object} as current object.
1225
+ *
1226
+ * @param o
1227
+ * @return
1228
+ */
1229
+ public ObjectPath push (Object o ) {
1230
+ return new ObjectPath (this , o );
1231
+ }
1232
+
1233
+ public Object getRootObject () {
1234
+ return stack .get (0 );
1235
+ }
1236
+
1237
+ public Object getCurrentObject () {
1238
+ return stack .get (stack .size () - 1 );
1239
+ }
1240
+
1241
+ @ Override
1242
+ public Iterator <Object > iterator () {
1243
+ return stack .iterator ();
1244
+ }
1245
+
1246
+ /**
1247
+ * Returns the {@link ObjectPath} represented by the given {@link Object} or creates a new {@link ObjectPath}
1248
+ * wrapping the given {@link Object}.
1249
+ *
1250
+ * @param object
1251
+ * @return
1252
+ */
1253
+ public static ObjectPath toPath (Object object ) {
1254
+ return object instanceof ObjectPath ? ((ObjectPath ) object ) : new ObjectPath (object );
1255
+ }
1256
+
1257
+ public static Object unwrapCurrentFromPotentialPath (Object potentialObjectPath ) {
1258
+ return potentialObjectPath instanceof ObjectPath ? ((ObjectPath ) potentialObjectPath ).getCurrentObject ()
1259
+ : potentialObjectPath ;
1260
+ }
1261
+ }
1124
1262
}
0 commit comments