From f128e6df152bc559bbae6e07592307d3f3fc402d Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 8 Sep 2021 10:29:02 +0200 Subject: [PATCH] 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. --- .../core/convert/MappingMongoConverter.java | 25 +++-- .../MongoTemplateDocumentReferenceTests.java | 106 ++++++++++++++++++ 2 files changed, 124 insertions(+), 7 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 5a2c3e952..07709df36 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -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 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) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java index c63e7a111..3abd3a3ad 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java @@ -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 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; + } + } }