Skip to content

Commit be549b3

Browse files
committed
DATAMONGO-1471 - Converter only applies identifier values if actually available.
Setting the value for the identifier property is an explicit step in MappingMongoConverter and always executed if the type to be created has an identifier property. If the source document doesn't contain an _id field (e.g. because it has been excluded explicitly) that previously caused null to be set on the identifier. This caused an exception if the identifier property is a primitive type. We now explicitly check whether the field backing the identifier property is actually present in the source document and only explicitly set the value if so.
1 parent 16bb07d commit be549b3

File tree

4 files changed

+57
-1
lines changed

4 files changed

+57
-1
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DBObjectAccessor.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,40 @@ public Object get(MongoPersistentProperty property) {
114114
return result;
115115
}
116116

117+
/**
118+
* Returns whether the underlying {@link DBObject} has a value ({@literal null} or non-{@literal null}) for the given
119+
* {@link MongoPersistentProperty}.
120+
*
121+
* @param property must not be {@literal null}.
122+
* @return
123+
*/
124+
public boolean hasValue(MongoPersistentProperty property) {
125+
126+
Assert.notNull(property, "Property must not be null!");
127+
128+
String fieldName = property.getFieldName();
129+
130+
if (!fieldName.contains(".")) {
131+
return this.dbObject.containsField(fieldName);
132+
}
133+
134+
String[] parts = fieldName.split("\\.");
135+
Map<String, Object> source = this.dbObject;
136+
Object result = null;
137+
138+
for (int i = 1; i < parts.length; i++) {
139+
140+
result = source.get(parts[i - 1]);
141+
source = getAsMap(result);
142+
143+
if (source == null) {
144+
return false;
145+
}
146+
}
147+
148+
return source.containsKey(parts[parts.length - 1]);
149+
}
150+
117151
/**
118152
* Returns the given source object as map, i.e. {@link BasicDBObject}s and maps as is or {@literal null} otherwise.
119153
*

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ private <S extends Object> S read(final MongoPersistentEntity<S> entity, final D
260260
// make sure id property is set before all other properties
261261
Object idValue = null;
262262

263-
if (idProperty != null) {
263+
if (idProperty != null && new DBObjectAccessor(dbo).hasValue(idProperty)) {
264264
idValue = getValueInternal(idProperty, dbo, evaluator, path);
265265
accessor.setProperty(idProperty, idValue);
266266
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,20 @@ public void writesAllNestingsCorrectly() {
101101
assertThat(nestedA.get("c"), is((Object) "c"));
102102
}
103103

104+
/**
105+
* @see DATAMONGO-1471
106+
*/
107+
@Test
108+
public void exposesAvailabilityOfFields() {
109+
110+
DBObjectAccessor accessor = new DBObjectAccessor(new BasicDBObject("a", new BasicDBObject("c", "d")));
111+
MongoPersistentEntity<?> entity = context.getPersistentEntity(ProjectingType.class);
112+
113+
assertThat(accessor.hasValue(entity.getPersistentProperty("foo")), is(false));
114+
assertThat(accessor.hasValue(entity.getPersistentProperty("a")), is(true));
115+
assertThat(accessor.hasValue(entity.getPersistentProperty("name")), is(false));
116+
}
117+
104118
static class ProjectingType {
105119

106120
String name;

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,6 +2066,14 @@ public void readsMapKeyUsingCustomConverter() {
20662066
assertThat(target.map.get(FooBarEnum.FOO), is("spring"));
20672067
}
20682068

2069+
/**
2070+
* @see DATAMONGO-1471
2071+
*/
2072+
@Test
2073+
public void readsDocumentWithPrimitiveIdButNoValue() {
2074+
assertThat(converter.read(ClassWithIntId.class, new BasicDBObject()), is(notNullValue()));
2075+
}
2076+
20692077
static class GenericType<T> {
20702078
T content;
20712079
}

0 commit comments

Comments
 (0)