From 149a703ecce3d0cb4ee8dcb8169184a2f1e801f0 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 4 May 2021 13:35:45 +0200 Subject: [PATCH] Fix NPE in QueryMapper when trying to apply target type on null value. Closes #3633 Original pull request: #3643. --- .../data/mongodb/core/convert/QueryMapper.java | 6 +++--- .../mongodb/core/convert/QueryMapperUnitTests.java | 11 +++++++++++ .../AbstractPersonRepositoryIntegrationTests.java | 11 +++++++++++ .../data/mongodb/repository/PersonRepository.java | 3 +++ .../json/ParameterBindingJsonReaderUnitTests.java | 7 +++++++ 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java index 81c1c96dd..65f66ec4c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java @@ -756,12 +756,12 @@ public class QueryMapper { * converted one by one. * * @param documentField the field and its meta data - * @param value the actual value + * @param value the actual value. Can be {@literal null}. * @return the potentially converted target value. */ - private Object applyFieldTargetTypeHintToValue(Field documentField, Object value) { + private Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Object value) { - if (documentField.getProperty() == null || !documentField.getProperty().hasExplicitWriteTarget()) { + if (value == null || documentField.getProperty() == null || !documentField.getProperty().hasExplicitWriteTarget()) { return value; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java index f7b5ec76d..8c23b4a22 100755 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java @@ -1255,6 +1255,17 @@ public class QueryMapperUnitTests { assertThat(document).isEqualTo(new org.bson.Document("double_underscore.renamed", new org.bson.Document("$exists", true))); } + @Test // GH-3633 + void mapsNullValueForFieldWithCustomTargetType() { + + Query query = query(where("stringAsOid").is(null)); + + org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), + context.getPersistentEntity(NonIdFieldWithObjectIdTargetType.class)); + + assertThat(document).isEqualTo(new org.bson.Document("stringAsOid", null)); + } + class WithDeepArrayNesting { List level0; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java index 61caa3056..d57691385 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java @@ -61,7 +61,9 @@ import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.data.mongodb.core.query.BasicQuery; +import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.repository.Person.Sex; import org.springframework.data.mongodb.repository.SampleEvaluationContextExtension.SampleSecurityContextHolder; import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; @@ -1435,6 +1437,15 @@ public abstract class AbstractPersonRepositoryIntegrationTests { assertThat(target.getFirstname()).isEqualTo(alicia.getFirstname().toUpperCase()); } + @Test // GH-3633 + void annotatedQueryWithNullEqualityCheckShouldWork() { + + operations.updateFirst(Query.query(Criteria.where("id").is(dave.getId())), Update.update("age", null), Person.class); + + Person byQueryWithNullEqualityCheck = repository.findByQueryWithNullEqualityCheck(); + assertThat(byQueryWithNullEqualityCheck.getId()).isEqualTo(dave.getId()); + } + @Test // GH-3602 void executesQueryWithDocumentReferenceCorrectly() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java index ca382fa2c..155cf7a7b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java @@ -417,5 +417,8 @@ public interface PersonRepository extends MongoRepository, Query List findByUnwrappedUser(User user); + @Query("{ 'age' : null }") + Person findByQueryWithNullEqualityCheck(); + List findBySpiritAnimal(User user); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java index 72ab2b454..1a684af16 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java @@ -383,6 +383,13 @@ class ParameterBindingJsonReaderUnitTests { .parse("{ 'stores.location' : { $geoWithin: { $centerSphere: [ [ 1.948516, 48.799029 ] , 0.004 ] } } }")); } + @Test // GH-3633 + void parsesNullValue() { + + Document target = parse("{ 'parent' : null }"); + assertThat(target).isEqualTo(new Document("parent", null)); + } + private static Document parse(String json, Object... args) { ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args);