Fix @DocumentReference resolution for properties used in constructor.

This commit fixes an issue that prevented referenced entities from being used as constructor arguments.

Closes: #3806
Original pull request: #3810.
This commit is contained in:
Christoph Strobl
2021-09-08 10:29:02 +02:00
committed by Mark Paluch
parent 270456ed81
commit f128e6df15
2 changed files with 124 additions and 7 deletions

View File

@@ -530,7 +530,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (conversionService.canConvert(DocumentPointer.class, property.getActualType())) {
if(value == null) {
if (value == null) {
return;
}
@@ -541,7 +541,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
} else {
accessor.setProperty(property,
dbRefResolver.resolveReference(property, new DocumentReferenceSource(documentAccessor.getDocument(), documentAccessor.get(property)), referenceLookupDelegate, context::convert));
dbRefResolver.resolveReference(property,
new DocumentReferenceSource(documentAccessor.getDocument(), documentAccessor.get(property)),
referenceLookupDelegate, context::convert));
}
return;
}
@@ -875,10 +877,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (property.isAssociation()) {
List<Object> targetCollection = collection.stream().map(it -> {
return documentPointerFactory.computePointer(mappingContext, property, it, property.getActualType()).getPointer();
return documentPointerFactory.computePointer(mappingContext, property, it, property.getActualType())
.getPointer();
}).collect(Collectors.toList());
return writeCollectionInternal(targetCollection, ClassTypeInformation.from(DocumentPointer.class), new ArrayList<>());
return writeCollectionInternal(targetCollection, ClassTypeInformation.from(DocumentPointer.class),
new ArrayList<>());
}
if (property.hasExplicitWriteTarget()) {
@@ -931,7 +935,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (property.isDbReference()) {
document.put(simpleKey, value != null ? createDBRef(value, property) : null);
} else {
document.put(simpleKey, documentPointerFactory.computePointer(mappingContext, property, value, property.getActualType()).getPointer());
document.put(simpleKey, documentPointerFactory
.computePointer(mappingContext, property, value, property.getActualType()).getPointer());
}
} else {
@@ -1814,6 +1819,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return (T) dbRefResolver.resolveDbRef(property, dbref, callback, dbRefProxyHandler);
}
if (property.isDocumentReference()) {
return (T) dbRefResolver.resolveReference(property, accessor.get(property), referenceLookupDelegate,
context::convert);
}
return super.getPropertyValue(property);
}
}
@@ -2036,7 +2046,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (typeHint.isMap()) {
if(ClassUtils.isAssignable(Document.class, typeHint.getType())) {
if (ClassUtils.isAssignable(Document.class, typeHint.getType())) {
return (S) documentConverter.convert(this, BsonUtils.asBson(source), typeHint);
}
@@ -2044,7 +2054,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return (S) mapConverter.convert(this, BsonUtils.asBson(source), typeHint);
}
throw new IllegalArgumentException(String.format("Expected map like structure but found %s", source.getClass()));
throw new IllegalArgumentException(
String.format("Expected map like structure but found %s", source.getClass()));
}
if (source instanceof DBRef) {

View File

@@ -733,6 +733,52 @@ public class MongoTemplateDocumentReferenceTests {
assertThat(result.simplePreinitializedValueRef).isEmpty();
}
@Test // GH-3806
void resolveReferenceWhenUsedAsCtorArgument() {
Publisher publisher = new Publisher();
publisher.id = "p-111";
publisher.name = "ppp";
template.save(publisher);
WithRequiredArgsCtor source = new WithRequiredArgsCtor("id-1", publisher);
template.save(source);
WithRequiredArgsCtor target = template.findOne(query(where("id").is(source.id)), WithRequiredArgsCtor.class);
assertThat(target.publisher).isNotNull();
}
@Test // GH-3806
void resolveLazyReferenceWhenUsedAsCtorArgument() {
Publisher publisher = new Publisher();
publisher.id = "p-111";
publisher.name = "ppp";
template.save(publisher);
WithLazyRequiredArgsCtor source = new WithLazyRequiredArgsCtor("id-1", publisher);
template.save(source);
WithLazyRequiredArgsCtor target = template.findOne(query(where("id").is(source.id)), WithLazyRequiredArgsCtor.class);
// proxy not yet resolved
LazyLoadingTestUtils.assertProxy(target.publisher, (proxy) -> {
assertThat(proxy.isResolved()).isFalse();
assertThat(proxy.currentValue()).isNull();
});
// resolve the proxy by invoking a method on it
assertThat(target.getPublisher().getName()).isEqualTo("ppp");
LazyLoadingTestUtils.assertProxy(target.publisher, (proxy) -> {
assertThat(proxy.isResolved()).isTrue();
});
}
@Test // GH-3602
void queryForReference() {
@@ -1371,6 +1417,30 @@ public class MongoTemplateDocumentReferenceTests {
String id;
String acronym;
String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAcronym() {
return acronym;
}
public void setAcronym(String acronym) {
this.acronym = acronym;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Data
@@ -1401,4 +1471,40 @@ public class MongoTemplateDocumentReferenceTests {
@DocumentReference(lookup="{'publisherId':?#{#self._id} }")
List<OneToManyStyleBook> books;
}
static class WithRequiredArgsCtor {
final String id;
@DocumentReference
final Publisher publisher;
public WithRequiredArgsCtor(String id, Publisher publisher) {
this.id = id;
this.publisher = publisher;
}
}
static class WithLazyRequiredArgsCtor {
final String id;
@DocumentReference(lazy = true)
final Publisher publisher;
public WithLazyRequiredArgsCtor(String id, Publisher publisher) {
this.id = id;
this.publisher = publisher;
}
public String getId() {
return id;
}
public Publisher getPublisher() {
return publisher;
}
}
}