Document list/map/set initialization on read.

Update the reference documentation about collection initialization on read, add the required tests to make sure it behaves as expected and simplify BeanUtils value presence check.

Closes #4571
Original pull request: #4574
This commit is contained in:
Christoph Strobl
2023-11-30 10:16:04 +01:00
committed by Mark Paluch
parent fa9fbcb2ed
commit e5b9f7d455
3 changed files with 90 additions and 1 deletions

View File

@@ -593,7 +593,7 @@ public class BsonUtils {
Map<String, Object> source = asMap(bson);
if (fieldName.isKey()) {
return source.get(fieldName.name()) != null;
return source.containsKey(fieldName.name());
}
String[] parts = fieldName.parts();

View File

@@ -27,6 +27,8 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
import org.bson.BsonUndefined;
import org.bson.types.Binary;
@@ -38,6 +40,8 @@ import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -673,6 +677,15 @@ class MappingMongoConverterUnitTests {
assertThat(wrapper.listOfMaps.get(0).get("Foo")).isEqualTo(Locale.ENGLISH);
}
@ParameterizedTest // GH-4571
@MethodSource("listMapSetReadingSource")
<T> void initializesListMapSetPropertiesIfRequiredOnRead(org.bson.Document source, Class<T> type,
Function<T, Object> valueFunction, Object expectedValue) {
T target = converter.read(type, source);
assertThat(target).extracting(valueFunction).isEqualTo(expectedValue);
}
@Test // DATAMONGO-259
void writesPlainMapOfCollectionsCorrectly() {
@@ -2986,6 +2999,49 @@ class MappingMongoConverterUnitTests {
return target;
}
private static Stream<Arguments> listMapSetReadingSource() {
Function<CollectionWrapper, Object> contacts = CollectionWrapper::getContacts;
Function<CollectionWrapper, Object> contactsSet = CollectionWrapper::getContactsSet;
Function<CollectionWrapper, Object> autoInitList = CollectionWrapper::getAutoInitList;
Function<ClassWithMapProperty, Object> map = ClassWithMapProperty::getMap;
Function<ClassWithMapProperty, Object> autoInitMap = ClassWithMapProperty::getAutoInitMap;
return Stream.of( //
// List
Arguments.of(new org.bson.Document("contacts", Collections.emptyList()), CollectionWrapper.class, contacts,
Collections.emptyList()),
Arguments.of(new org.bson.Document("contacts", null), CollectionWrapper.class, contacts, null),
Arguments.of(new org.bson.Document(), CollectionWrapper.class, contacts, null),
// ctor initialized List
Arguments.of(new org.bson.Document("autoInitList", Collections.emptyList()), CollectionWrapper.class,
autoInitList, Collections.emptyList()),
Arguments.of(new org.bson.Document("autoInitList", null), CollectionWrapper.class, autoInitList, null),
Arguments.of(new org.bson.Document(), CollectionWrapper.class, autoInitList,
Collections.singletonList("spring")),
// Set
Arguments.of(new org.bson.Document("contactsSet", Collections.emptyList()), CollectionWrapper.class,
contactsSet, Collections.emptySet()),
Arguments.of(new org.bson.Document("contactsSet", null), CollectionWrapper.class, contactsSet, null),
Arguments.of(new org.bson.Document(), CollectionWrapper.class, contactsSet, null),
// Map
Arguments.of(new org.bson.Document("map", new org.bson.Document()), ClassWithMapProperty.class, map,
Collections.emptyMap()),
Arguments.of(new org.bson.Document("map", null), ClassWithMapProperty.class, map, null),
Arguments.of(new org.bson.Document(), ClassWithMapProperty.class, map, null),
// ctor initialized Map
Arguments.of(new org.bson.Document("autoInitMap", new org.bson.Document()), ClassWithMapProperty.class,
autoInitMap, Collections.emptyMap()),
Arguments.of(new org.bson.Document("autoInitMap", null), ClassWithMapProperty.class, autoInitMap, null),
Arguments.of(new org.bson.Document(), ClassWithMapProperty.class, autoInitMap,
Collections.singletonMap("spring", "data")));
}
static class GenericType<T> {
T content;
}
@@ -3142,11 +3198,20 @@ class MappingMongoConverterUnitTests {
static class ClassWithMapProperty {
Map<Locale, String> map;
Map<String, String> autoInitMap = Collections.singletonMap("spring", "data");
Map<String, List<String>> mapOfLists;
Map<String, Object> mapOfObjects;
Map<String, String[]> mapOfStrings;
Map<String, Person> mapOfPersons;
TreeMap<String, Person> treeMapOfPersons;
public Map<Locale, String> getMap() {
return map;
}
public Map<String, String> getAutoInitMap() {
return this.autoInitMap;
}
}
static class ClassWithNestedMaps {
@@ -3168,6 +3233,19 @@ class MappingMongoConverterUnitTests {
List<List<String>> strings;
List<Map<String, Locale>> listOfMaps;
Set<Contact> contactsSet;
List<String> autoInitList = Collections.singletonList("spring");
public List<Contact> getContacts() {
return contacts;
}
public Set<Contact> getContactsSet() {
return contactsSet;
}
public List<String> getAutoInitList() {
return autoInitList;
}
}
static class LocaleWrapper {

View File

@@ -274,6 +274,17 @@ calling `get()` before the actual conversion
|===
====
.Collection Handling
[NOTE]
====
Collection handing depends on the actual values retrieved from the MongoDB.
* If a document does **not** contain the field mapped to a collection, the mapping will not touch the property.
Which means the value will remain `null`, a java default or any value set during object creation.
* If the document contains the field to be mapped, but the field holds a `null` value (like: `{ 'list' : null }`), the property value is set to `null` overriding any default value set during object creation.
* If the document contains the field to be mapped to a collection which is **not** `null` (like: `{ 'list' : [ ... ] }`), the collection is populated with the mapped values overriding any default value set during object creation.
====
[[mapping-configuration]]
== Mapping Configuration