Skip to content

Commit f186dc4

Browse files
Do not resolve unresolved LazyLoadingProxy instances when saving back the entity.
1 parent 04daef2 commit f186dc4

File tree

4 files changed

+104
-28
lines changed

4 files changed

+104
-28
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,15 @@ public interface LazyLoadingProxy {
4646
*/
4747
@Nullable
4848
DBRef toDBRef();
49+
50+
/**
51+
* Returns the raw {@literal source} object that defines the reference.
52+
*
53+
* @return can be {@literal null}.
54+
* @since 3.3
55+
*/
56+
@Nullable
57+
default Object getSource() {
58+
return toDBRef();
59+
}
4960
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyGenerator.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ public LazyLoadingProxyGenerator(ReferenceReader referenceReader) {
5050
this.objenesis = new ObjenesisStd(true);
5151
}
5252

53-
public Object createLazyLoadingProxy(MongoPersistentProperty property, Object source,
54-
LookupFunction lookupFunction,
53+
public Object createLazyLoadingProxy(MongoPersistentProperty property, Object source, LookupFunction lookupFunction,
5554
ResultConversionFunction resultConversionFunction) {
5655

5756
Class<?> propertyType = property.getType();
58-
LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor(property, source, referenceReader, lookupFunction, resultConversionFunction);
57+
LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor(property, source, referenceReader, lookupFunction,
58+
resultConversionFunction);
5959

6060
if (!propertyType.isInterface()) {
6161

@@ -105,21 +105,21 @@ public static class LazyLoadingInterceptor
105105
private LookupFunction lookupFunction;
106106
private ResultConversionFunction resultConversionFunction;
107107

108-
private final Method INITIALIZE_METHOD, TO_DBREF_METHOD, FINALIZE_METHOD;
108+
private final Method INITIALIZE_METHOD, TO_DBREF_METHOD, FINALIZE_METHOD, GET_SOURCE_METHOD;
109109

110110
{
111111
try {
112112
INITIALIZE_METHOD = LazyLoadingProxy.class.getMethod("getTarget");
113113
TO_DBREF_METHOD = LazyLoadingProxy.class.getMethod("toDBRef");
114114
FINALIZE_METHOD = Object.class.getDeclaredMethod("finalize");
115+
GET_SOURCE_METHOD = LazyLoadingProxy.class.getMethod("getSource");
115116
} catch (Exception e) {
116117
throw new RuntimeException(e);
117118
}
118119
}
119120

120121
public LazyLoadingInterceptor(MongoPersistentProperty property, Object source, ReferenceReader reader,
121-
LookupFunction lookupFunction,
122-
ResultConversionFunction resultConversionFunction) {
122+
LookupFunction lookupFunction, ResultConversionFunction resultConversionFunction) {
123123

124124
this.property = property;
125125
this.source = source;
@@ -145,6 +145,10 @@ public Object intercept(Object o, Method method, Object[] args, MethodProxy prox
145145
return null;
146146
}
147147

148+
if (GET_SOURCE_METHOD.equals(method)) {
149+
return source;
150+
}
151+
148152
if (isObjectMethod(method) && Object.class.equals(method.getDeclaringClass())) {
149153

150154
if (ReflectionUtils.isToStringMethod(method)) {

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,13 @@ protected void writePropertyInternal(@Nullable Object obj, DocumentAccessor acce
763763

764764
if (prop.isAssociation()) {
765765

766+
if (obj instanceof LazyLoadingProxy) {
767+
768+
Object refSource = ((LazyLoadingProxy) obj).getSource();
769+
accessor.put(prop, refSource);
770+
return;
771+
}
772+
766773
if (conversionService.canConvert(valueType.getType(), DocumentPointer.class)) {
767774
accessor.put(prop, conversionService.convert(obj, DocumentPointer.class).getPointer());
768775
} else {

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public void setUp() {
8484
template.flushDatabase();
8585
}
8686

87-
@Test
87+
@Test // GH-3602
8888
void writeSimpleTypeReference() {
8989

9090
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
@@ -102,7 +102,7 @@ void writeSimpleTypeReference() {
102102
assertThat(target.get("simpleValueRef")).isEqualTo("ref-1");
103103
}
104104

105-
@Test
105+
@Test // GH-3602
106106
void writeMapTypeReference() {
107107

108108
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
@@ -122,7 +122,7 @@ void writeMapTypeReference() {
122122
assertThat(target.get("mapValueRef", Map.class)).containsEntry("frodo", "ref-1").containsEntry("bilbo", "ref-2");
123123
}
124124

125-
@Test
125+
@Test // GH-3602
126126
void writeCollectionOfSimpleTypeReference() {
127127

128128
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
@@ -141,7 +141,7 @@ void writeCollectionOfSimpleTypeReference() {
141141
assertThat(target.get("simpleValueRef", List.class)).containsExactly("ref-1", "ref-2");
142142
}
143143

144-
@Test
144+
@Test // GH-3602
145145
void writeObjectTypeReference() {
146146

147147
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
@@ -159,7 +159,7 @@ void writeObjectTypeReference() {
159159
assertThat(target.get("objectValueRef")).isEqualTo(source.getObjectValueRef().toReference());
160160
}
161161

162-
@Test
162+
@Test // GH-3602
163163
void writeCollectionOfObjectTypeReference() {
164164

165165
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
@@ -179,7 +179,7 @@ void writeCollectionOfObjectTypeReference() {
179179
source.getObjectValueRef().get(0).toReference(), source.getObjectValueRef().get(1).toReference());
180180
}
181181

182-
@Test
182+
@Test // GH-3602
183183
void readSimpleTypeObjectReference() {
184184

185185
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
@@ -198,7 +198,7 @@ void readSimpleTypeObjectReference() {
198198
assertThat(result.getSimpleValueRef()).isEqualTo(new SimpleObjectRef("ref-1", "me-the-referenced-object"));
199199
}
200200

201-
@Test
201+
@Test // GH-3602
202202
void readCollectionOfSimpleTypeObjectReference() {
203203

204204
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
@@ -218,7 +218,7 @@ void readCollectionOfSimpleTypeObjectReference() {
218218
assertThat(result.getSimpleValueRef()).containsExactly(new SimpleObjectRef("ref-1", "me-the-referenced-object"));
219219
}
220220

221-
@Test
221+
@Test // GH-3602
222222
void readLazySimpleTypeObjectReference() {
223223

224224
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
@@ -243,7 +243,7 @@ void readLazySimpleTypeObjectReference() {
243243
assertThat(result.getSimpleLazyValueRef()).isEqualTo(new SimpleObjectRef("ref-1", "me-the-referenced-object"));
244244
}
245245

246-
@Test
246+
@Test // GH-3602
247247
void readSimpleTypeObjectReferenceFromFieldWithCustomName() {
248248

249249
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
@@ -264,7 +264,7 @@ void readSimpleTypeObjectReferenceFromFieldWithCustomName() {
264264
.isEqualTo(new SimpleObjectRef("ref-1", "me-the-referenced-object"));
265265
}
266266

267-
@Test
267+
@Test // GH-3602
268268
void readCollectionTypeObjectReferenceFromFieldWithCustomName() {
269269

270270
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
@@ -285,7 +285,7 @@ void readCollectionTypeObjectReferenceFromFieldWithCustomName() {
285285
.containsExactly(new SimpleObjectRef("ref-1", "me-the-referenced-object"));
286286
}
287287

288-
@Test
288+
@Test // GH-3602
289289
void readObjectReferenceFromDocumentType() {
290290

291291
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
@@ -305,7 +305,7 @@ void readObjectReferenceFromDocumentType() {
305305
assertThat(result.getObjectValueRef()).isEqualTo(new ObjectRefOfDocument("ref-1", "me-the-referenced-object"));
306306
}
307307

308-
@Test
308+
@Test // GH-3602
309309
void readCollectionObjectReferenceFromDocumentType() {
310310

311311
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
@@ -326,7 +326,7 @@ void readCollectionObjectReferenceFromDocumentType() {
326326
.containsExactly(new ObjectRefOfDocument("ref-1", "me-the-referenced-object"));
327327
}
328328

329-
@Test
329+
@Test // GH-3602
330330
void readObjectReferenceFromDocumentDeclaringCollectionName() {
331331

332332
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
@@ -349,7 +349,7 @@ void readObjectReferenceFromDocumentDeclaringCollectionName() {
349349
.isEqualTo(new ObjectRefOfDocumentWithEmbeddedCollectionName("ref-1", "me-the-referenced-object"));
350350
}
351351

352-
@Test
352+
@Test // GH-3602
353353
void readCollectionObjectReferenceFromDocumentDeclaringCollectionName() {
354354

355355
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
@@ -377,7 +377,7 @@ void readCollectionObjectReferenceFromDocumentDeclaringCollectionName() {
377377
new ObjectRefOfDocumentWithEmbeddedCollectionName("ref-1", "me-the-1-referenced-object"));
378378
}
379379

380-
@Test
380+
@Test // GH-3602
381381
void readObjectReferenceFromDocumentNotRelatingToTheIdProperty() {
382382

383383
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
@@ -399,7 +399,7 @@ void readObjectReferenceFromDocumentNotRelatingToTheIdProperty() {
399399
.isEqualTo(new ObjectRefOnNonIdField("ref-1", "me-the-referenced-object", "ref-key-1", "ref-key-2"));
400400
}
401401

402-
@Test
402+
@Test // GH-3602
403403
void readLazyObjectReferenceFromDocumentNotRelatingToTheIdProperty() {
404404

405405
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
@@ -427,7 +427,7 @@ void readLazyObjectReferenceFromDocumentNotRelatingToTheIdProperty() {
427427
.isEqualTo(new ObjectRefOnNonIdField("ref-1", "me-the-referenced-object", "ref-key-1", "ref-key-2"));
428428
}
429429

430-
@Test
430+
@Test // GH-3602
431431
void readCollectionObjectReferenceFromDocumentNotRelatingToTheIdProperty() {
432432

433433
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
@@ -450,7 +450,7 @@ void readCollectionObjectReferenceFromDocumentNotRelatingToTheIdProperty() {
450450
.containsExactly(new ObjectRefOnNonIdField("ref-1", "me-the-referenced-object", "ref-key-1", "ref-key-2"));
451451
}
452452

453-
@Test
453+
@Test // GH-3602
454454
void readMapOfReferences() {
455455

456456
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
@@ -483,8 +483,8 @@ void readMapOfReferences() {
483483
.containsEntry("bilbo", new SimpleObjectRef("ref-2", "me-the-2-referenced-object"));
484484
}
485485

486-
@Test
487-
void testLazyCyclic() {
486+
@Test // GH-3602
487+
void loadLazyCyclicReference() {
488488

489489
WithRefA a = new WithRefA();
490490
a.id = "a";
@@ -508,8 +508,8 @@ void testLazyCyclic() {
508508
});
509509
}
510510

511-
@Test
512-
void testEagerCyclic() {
511+
@Test // GH-3602
512+
void loadEagerCyclicReference() {
513513

514514
WithRefA a = new WithRefA();
515515
a.id = "a";
@@ -530,6 +530,60 @@ void testEagerCyclic() {
530530
assertThat(loadedA.getToB().eagerToA).isSameAs(loadedA);
531531
}
532532

533+
@Test // GH-3602
534+
void loadAndStoreUnresolvedLazyDoesNotResolveTheProxy() {
535+
536+
String collectionB = template.getCollectionName(WithRefB.class);
537+
538+
WithRefA a = new WithRefA();
539+
a.id = "a";
540+
541+
WithRefB b = new WithRefB();
542+
b.id = "b";
543+
544+
a.toB = b;
545+
b.lazyToA = a;
546+
547+
template.save(a);
548+
template.save(b);
549+
550+
WithRefA loadedA = template.query(WithRefA.class).matching(where("id").is(a.id)).firstValue();
551+
template.save(loadedA.getToB());
552+
553+
LazyLoadingTestUtils.assertProxy(loadedA.getToB().lazyToA, (proxy) -> {
554+
555+
assertThat(proxy.isResolved()).isFalse();
556+
assertThat(proxy.currentValue()).isNull();
557+
});
558+
559+
Document target = template.execute(db -> {
560+
return db.getCollection(collectionB).find(Filters.eq("_id", "b")).first();
561+
});
562+
assertThat(target.get("lazyToA", Object.class)).isEqualTo("a");
563+
}
564+
565+
@Test // GH-3602
566+
void loadCollectionReferenceWithMissingRefs() {
567+
568+
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
569+
String refCollectionName = template.getCollectionName(SimpleObjectRef.class);
570+
571+
// ref-1 is missing.
572+
Document refSource = new Document("_id", "ref-2").append("value", "me-the-2-referenced-object");
573+
Document source = new Document("_id", "id-1").append("value", "v1").append("simpleValueRef",
574+
Arrays.asList("ref-1", "ref-2"));
575+
576+
template.execute(db -> {
577+
578+
db.getCollection(refCollectionName).insertOne(refSource);
579+
db.getCollection(rootCollectionName).insertOne(source);
580+
return null;
581+
});
582+
583+
CollectionRefRoot result = template.findOne(query(where("id").is("id-1")), CollectionRefRoot.class);
584+
assertThat(result.getSimpleValueRef()).containsExactly(new SimpleObjectRef("ref-2", "me-the-2-referenced-object"));
585+
}
586+
533587
@Data
534588
static class SingleRefRoot {
535589

0 commit comments

Comments
 (0)