Skip to content

Commit 760d7d6

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 29a6688 commit 760d7d6

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
@@ -265,7 +265,7 @@ private <S extends Object> S read(final MongoPersistentEntity<S> entity, final D
265265
// make sure id property is set before all other properties
266266
Object idValue = null;
267267

268-
if (idProperty != null) {
268+
if (idProperty != null && new DBObjectAccessor(dbo).hasValue(idProperty)) {
269269
idValue = getValueInternal(idProperty, dbo, evaluator, path);
270270
accessor.setProperty(idProperty, idValue);
271271
}

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)