Fix id field target type conversion for document references.

This commit fixes an issue where a defined custom target type conversion for the id field was not properly considered when writing a document reference.  Previously an eg. String was not being converted into an ObjectId correctly causing lookup queries to return empty results.
Converting the id property value on write solves the issue.
Includes a minor polish in the mapping centralizing pointer creation within the DocumentPointerFactory.

Closes: #3782
Original pull request: #3785.
This commit is contained in:
Christoph Strobl
2021-08-24 07:31:25 +02:00
committed by Mark Paluch
parent f662d7ca0d
commit bf86f39b2d
3 changed files with 114 additions and 18 deletions

View File

@@ -83,7 +83,16 @@ class DocumentPointerFactory {
.getRequiredPersistentEntity(property.getAssociationTargetType());
if (usesDefaultLookup(property)) {
return () -> persistentEntity.getIdentifierAccessor(value).getIdentifier();
MongoPersistentProperty idProperty = persistentEntity.getIdProperty();
Object idValue = persistentEntity.getIdentifierAccessor(value).getIdentifier();
if (idProperty.hasExplicitWriteTarget()
&& conversionService.canConvert(idValue.getClass(), idProperty.getFieldType())) {
return () -> conversionService.convert(idValue, idProperty.getFieldType());
}
return () -> idValue;
}
MongoPersistentEntity<?> valueEntity = mappingContext.getPersistentEntity(value.getClass());

View File

@@ -869,15 +869,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (!property.isDbReference()) {
if (property.isAssociation()) {
return writeCollectionInternal(collection.stream().map(it -> {
if (conversionService.canConvert(it.getClass(), DocumentPointer.class)) {
return conversionService.convert(it, DocumentPointer.class).getPointer();
} else {
// just take the id as a reference
return mappingContext.getPersistentEntity(property.getAssociationTargetType()).getIdentifierAccessor(it)
.getIdentifier();
}
}).collect(Collectors.toList()), ClassTypeInformation.from(DocumentPointer.class), new ArrayList<>());
List<Object> targetCollection = collection.stream().map(it -> {
return documentPointerFactory.computePointer(mappingContext, property, it, property.getActualType()).getPointer();
}).collect(Collectors.toList());
return writeCollectionInternal(targetCollection, ClassTypeInformation.from(DocumentPointer.class), new ArrayList<>());
}
if (property.hasExplicitWriteTarget()) {
@@ -930,13 +927,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (property.isDbReference()) {
document.put(simpleKey, value != null ? createDBRef(value, property) : null);
} else {
if (conversionService.canConvert(value.getClass(), DocumentPointer.class)) {
document.put(simpleKey, conversionService.convert(value, DocumentPointer.class).getPointer());
} else {
// just take the id as a reference
document.put(simpleKey, mappingContext.getPersistentEntity(property.getAssociationTargetType())
.getIdentifierAccessor(value).getIdentifier());
}
document.put(simpleKey, documentPointerFactory.computePointer(mappingContext, property, value, property.getActualType()).getPointer());
}
} else {

View File

@@ -32,6 +32,7 @@ import java.util.List;
import java.util.Map;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -44,6 +45,8 @@ import org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils;
import org.springframework.data.mongodb.core.mapping.DocumentPointer;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.FieldType;
import org.springframework.data.mongodb.core.mapping.MongoId;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.test.util.Client;
import org.springframework.data.mongodb.test.util.MongoClientExtension;
@@ -106,6 +109,26 @@ public class MongoTemplateDocumentReferenceTests {
assertThat(target.get("simpleValueRef")).isEqualTo("ref-1");
}
@Test // GH-3782
void writeTypeReferenceHavingCustomizedIdTargetType() {
ObjectId expectedIdValue = new ObjectId();
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
SingleRefRoot source = new SingleRefRoot();
source.id = "root-1";
source.customIdTargetRef = new ObjectRefHavingCustomizedIdTargetType(expectedIdValue.toString(),
"me-the-referenced-object");
template.save(source);
Document target = template.execute(db -> {
return db.getCollection(rootCollectionName).find(Filters.eq("_id", "root-1")).first();
});
assertThat(target.get("customIdTargetRef")).isEqualTo(expectedIdValue);
}
@Test // GH-3602
void writeMapTypeReference() {
@@ -126,6 +149,26 @@ public class MongoTemplateDocumentReferenceTests {
assertThat(target.get("mapValueRef", Map.class)).containsEntry("frodo", "ref-1").containsEntry("bilbo", "ref-2");
}
@Test // GH-3782
void writeMapOfTypeReferenceHavingCustomizedIdTargetType() {
ObjectId expectedIdValue = new ObjectId();
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
CollectionRefRoot source = new CollectionRefRoot();
source.id = "root-1";
source.customIdTargetRefMap = Collections.singletonMap("frodo",
new ObjectRefHavingCustomizedIdTargetType(expectedIdValue.toString(), "me-the-referenced-object"));
template.save(source);
Document target = template.execute(db -> {
return db.getCollection(rootCollectionName).find(Filters.eq("_id", "root-1")).first();
});
assertThat(target.get("customIdTargetRefMap", Map.class)).containsEntry("frodo", expectedIdValue);
}
@Test // GH-3602
void writeCollectionOfSimpleTypeReference() {
@@ -145,6 +188,26 @@ public class MongoTemplateDocumentReferenceTests {
assertThat(target.get("simpleValueRef", List.class)).containsExactly("ref-1", "ref-2");
}
@Test // GH-3782
void writeListOfTypeReferenceHavingCustomizedIdTargetType() {
ObjectId expectedIdValue = new ObjectId();
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
CollectionRefRoot source = new CollectionRefRoot();
source.id = "root-1";
source.customIdTargetRefList = Collections.singletonList(
new ObjectRefHavingCustomizedIdTargetType(expectedIdValue.toString(), "me-the-referenced-object"));
template.save(source);
Document target = template.execute(db -> {
return db.getCollection(rootCollectionName).find(Filters.eq("_id", "root-1")).first();
});
assertThat(target.get("customIdTargetRefList", List.class)).containsExactly(expectedIdValue);
}
@Test // GH-3602
void writeObjectTypeReference() {
@@ -739,6 +802,26 @@ public class MongoTemplateDocumentReferenceTests {
assertThat(target).containsEntry("toB", "b");
}
@Test // GH-3782
void updateReferenceHavingCustomizedIdTargetType() {
ObjectId expectedIdValue = new ObjectId();
String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
SingleRefRoot root = new SingleRefRoot();
root.id = "root-1";
template.save(root);
template.update(SingleRefRoot.class).apply(new Update().set("customIdTargetRef",
new ObjectRefHavingCustomizedIdTargetType(expectedIdValue.toString(), "b"))).first();
Document target = template.execute(db -> {
return db.getCollection(rootCollectionName).find(Filters.eq("_id", "root-1")).first();
});
assertThat(target).containsEntry("customIdTargetRef", expectedIdValue);
}
@Test // GH-3602
void updateReferenceCollectionWithEntity() {
@@ -998,6 +1081,8 @@ public class MongoTemplateDocumentReferenceTests {
@DocumentReference(lookup = "{ 'refKey1' : '?#{refKey1}', 'refKey2' : '?#{refKey2}' }", lazy = true) //
ObjectRefOnNonIdField lazyObjectValueRefOnNonIdFields;
@DocumentReference ObjectRefHavingCustomizedIdTargetType customIdTargetRef;
}
@Data
@@ -1027,6 +1112,10 @@ public class MongoTemplateDocumentReferenceTests {
@DocumentReference(lookup = "{ 'refKey1' : '?#{refKey1}', 'refKey2' : '?#{refKey2}' }") //
List<ObjectRefOnNonIdField> objectValueRefOnNonIdFields;
@DocumentReference List<ObjectRefHavingCustomizedIdTargetType> customIdTargetRefList;
@DocumentReference Map<String, ObjectRefHavingCustomizedIdTargetType> customIdTargetRefMap;
}
@FunctionalInterface
@@ -1094,6 +1183,14 @@ public class MongoTemplateDocumentReferenceTests {
}
}
@Data
@AllArgsConstructor
static class ObjectRefHavingCustomizedIdTargetType {
@MongoId(targetType = FieldType.OBJECT_ID) String id;
String name;
}
static class ReferencableConverter implements Converter<ReferenceAble, DocumentPointer<Object>> {
@Nullable
@@ -1196,5 +1293,4 @@ public class MongoTemplateDocumentReferenceTests {
@Reference //
Publisher publisher;
}
}