Skip to content

Commit 97433d0

Browse files
christophstroblmp911de
authored andcommitted
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
1 parent 6f6b61d commit 97433d0

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ public static boolean hasValue(Bson bson, FieldName fieldName) {
593593

594594
Map<String, Object> source = asMap(bson);
595595
if (fieldName.isKey()) {
596-
return source.get(fieldName.name()) != null;
596+
return source.containsKey(fieldName.name());
597597
}
598598

599599
String[] parts = fieldName.parts();

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

+78
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import java.time.LocalDateTime;
2828
import java.time.temporal.ChronoUnit;
2929
import java.util.*;
30+
import java.util.function.Function;
31+
import java.util.stream.Stream;
3032

3133
import org.bson.BsonUndefined;
3234
import org.bson.types.Binary;
@@ -38,6 +40,8 @@
3840
import org.junit.jupiter.api.Test;
3941
import org.junit.jupiter.api.extension.ExtendWith;
4042
import org.junit.jupiter.params.ParameterizedTest;
43+
import org.junit.jupiter.params.provider.Arguments;
44+
import org.junit.jupiter.params.provider.MethodSource;
4145
import org.junit.jupiter.params.provider.ValueSource;
4246
import org.mockito.Mock;
4347
import org.mockito.Mockito;
@@ -673,6 +677,15 @@ void readsListOfMapsCorrectly() {
673677
assertThat(wrapper.listOfMaps.get(0).get("Foo")).isEqualTo(Locale.ENGLISH);
674678
}
675679

680+
@ParameterizedTest // GH-4571
681+
@MethodSource("listMapSetReadingSource")
682+
<T> void initializesListMapSetPropertiesIfRequiredOnRead(org.bson.Document source, Class<T> type,
683+
Function<T, Object> valueFunction, Object expectedValue) {
684+
685+
T target = converter.read(type, source);
686+
assertThat(target).extracting(valueFunction).isEqualTo(expectedValue);
687+
}
688+
676689
@Test // DATAMONGO-259
677690
void writesPlainMapOfCollectionsCorrectly() {
678691

@@ -2986,6 +2999,49 @@ org.bson.Document write(Object source) {
29862999
return target;
29873000
}
29883001

3002+
private static Stream<Arguments> listMapSetReadingSource() {
3003+
3004+
Function<CollectionWrapper, Object> contacts = CollectionWrapper::getContacts;
3005+
Function<CollectionWrapper, Object> contactsSet = CollectionWrapper::getContactsSet;
3006+
Function<CollectionWrapper, Object> autoInitList = CollectionWrapper::getAutoInitList;
3007+
Function<ClassWithMapProperty, Object> map = ClassWithMapProperty::getMap;
3008+
Function<ClassWithMapProperty, Object> autoInitMap = ClassWithMapProperty::getAutoInitMap;
3009+
3010+
return Stream.of( //
3011+
3012+
// List
3013+
Arguments.of(new org.bson.Document("contacts", Collections.emptyList()), CollectionWrapper.class, contacts,
3014+
Collections.emptyList()),
3015+
Arguments.of(new org.bson.Document("contacts", null), CollectionWrapper.class, contacts, null),
3016+
Arguments.of(new org.bson.Document(), CollectionWrapper.class, contacts, null),
3017+
3018+
// ctor initialized List
3019+
Arguments.of(new org.bson.Document("autoInitList", Collections.emptyList()), CollectionWrapper.class,
3020+
autoInitList, Collections.emptyList()),
3021+
Arguments.of(new org.bson.Document("autoInitList", null), CollectionWrapper.class, autoInitList, null),
3022+
Arguments.of(new org.bson.Document(), CollectionWrapper.class, autoInitList,
3023+
Collections.singletonList("spring")),
3024+
3025+
// Set
3026+
Arguments.of(new org.bson.Document("contactsSet", Collections.emptyList()), CollectionWrapper.class,
3027+
contactsSet, Collections.emptySet()),
3028+
Arguments.of(new org.bson.Document("contactsSet", null), CollectionWrapper.class, contactsSet, null),
3029+
Arguments.of(new org.bson.Document(), CollectionWrapper.class, contactsSet, null),
3030+
3031+
// Map
3032+
Arguments.of(new org.bson.Document("map", new org.bson.Document()), ClassWithMapProperty.class, map,
3033+
Collections.emptyMap()),
3034+
Arguments.of(new org.bson.Document("map", null), ClassWithMapProperty.class, map, null),
3035+
Arguments.of(new org.bson.Document(), ClassWithMapProperty.class, map, null),
3036+
3037+
// ctor initialized Map
3038+
Arguments.of(new org.bson.Document("autoInitMap", new org.bson.Document()), ClassWithMapProperty.class,
3039+
autoInitMap, Collections.emptyMap()),
3040+
Arguments.of(new org.bson.Document("autoInitMap", null), ClassWithMapProperty.class, autoInitMap, null),
3041+
Arguments.of(new org.bson.Document(), ClassWithMapProperty.class, autoInitMap,
3042+
Collections.singletonMap("spring", "data")));
3043+
}
3044+
29893045
static class GenericType<T> {
29903046
T content;
29913047
}
@@ -3142,11 +3198,20 @@ static class ClassWithSortedMap {
31423198

31433199
static class ClassWithMapProperty {
31443200
Map<Locale, String> map;
3201+
Map<String, String> autoInitMap = Collections.singletonMap("spring", "data");
31453202
Map<String, List<String>> mapOfLists;
31463203
Map<String, Object> mapOfObjects;
31473204
Map<String, String[]> mapOfStrings;
31483205
Map<String, Person> mapOfPersons;
31493206
TreeMap<String, Person> treeMapOfPersons;
3207+
3208+
public Map<Locale, String> getMap() {
3209+
return map;
3210+
}
3211+
3212+
public Map<String, String> getAutoInitMap() {
3213+
return this.autoInitMap;
3214+
}
31503215
}
31513216

31523217
static class ClassWithNestedMaps {
@@ -3168,6 +3233,19 @@ static class CollectionWrapper {
31683233
List<List<String>> strings;
31693234
List<Map<String, Locale>> listOfMaps;
31703235
Set<Contact> contactsSet;
3236+
List<String> autoInitList = Collections.singletonList("spring");
3237+
3238+
public List<Contact> getContacts() {
3239+
return contacts;
3240+
}
3241+
3242+
public Set<Contact> getContactsSet() {
3243+
return contactsSet;
3244+
}
3245+
3246+
public List<String> getAutoInitList() {
3247+
return autoInitList;
3248+
}
31713249
}
31723250

31733251
static class LocaleWrapper {

src/main/antora/modules/ROOT/pages/mongodb/mapping/mapping.adoc

+11
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,17 @@ calling `get()` before the actual conversion
274274
|===
275275
====
276276

277+
.Collection Handling
278+
[NOTE]
279+
====
280+
Collection handing depends on the actual values retrieved from the MongoDB.
281+
282+
* If a document does **not** contain the field mapped to a collection, the mapping will not touch the property.
283+
Which means the value will remain `null`, a java default or any value set during object creation.
284+
* 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.
285+
* 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.
286+
====
287+
277288
[[mapping-configuration]]
278289
== Mapping Configuration
279290

0 commit comments

Comments
 (0)