From c217618d9dfb29d1de45e7767844cff2d668d956 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 14 Jun 2021 09:30:08 +0200 Subject: [PATCH] Polishing. Reorder methods and types. Rename MongoPersistentProperty.isOmitNullProperty to writeNullValues. Adapt caching MongoPersistentProperty and add tests. Tweak Javadoc wording, add author and since tags. See #3407 Original pull request: #3646. --- .../core/convert/MappingMongoConverter.java | 20 +++-- .../mapping/BasicMongoPersistentProperty.java | 27 +++--- .../CachingMongoPersistentProperty.java | 15 ++++ .../data/mongodb/core/mapping/Field.java | 44 ++++++---- .../core/mapping/MongoPersistentProperty.java | 28 +++--- .../UnwrappedMongoPersistentProperty.java | 14 ++- .../data/mongodb/core/MongoTemplateTests.java | 88 ++++++++++++++++++- .../mongodb/core/MongoTemplateUnitTests.java | 6 +- .../MappingMongoConverterUnitTests.java | 28 ++++++ ...BasicMongoPersistentPropertyUnitTests.java | 73 ++++++++------- src/main/asciidoc/new-features.adoc | 1 + 11 files changed, 242 insertions(+), 102 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 86be47c55..732e8c9a5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -103,6 +103,7 @@ import com.mongodb.DBRef; * @author Mark Paluch * @author Roman Puchkovskiy * @author Heesu Jung + * @author Divya Srivastava */ public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware { @@ -737,6 +738,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App continue; } if (prop.isAssociation()) { + writeAssociation(prop.getRequiredAssociation(), accessor, dbObjectAccessor); continue; } @@ -744,13 +746,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App Object value = accessor.getProperty(prop); if (value == null) { - if(!prop.isPropertyOmittableOnNull()) { - writeSimpleInternal(value, bson , prop); + if (prop.writeNullValues()) { + dbObjectAccessor.put(prop, null); } - continue; - } - - if (!conversions.isSimpleType(value.getClass())) { + } else if (!conversions.isSimpleType(value.getClass())) { writePropertyInternal(value, dbObjectAccessor, prop); } else { writeSimpleInternal(value, bson, prop); @@ -763,7 +762,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App MongoPersistentProperty inverseProp = association.getInverse(); - writePropertyInternal(accessor.getProperty(inverseProp), dbObjectAccessor, inverseProp); + Object value = accessor.getProperty(inverseProp); + + if (value == null && !inverseProp.isUnwrapped() && inverseProp.writeNullValues()) { + dbObjectAccessor.put(inverseProp, null); + return; + } + + writePropertyInternal(value, dbObjectAccessor, inverseProp); } @SuppressWarnings({ "unchecked" }) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java index 3973f802a..87eb56b73 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java @@ -41,6 +41,7 @@ import org.springframework.util.StringUtils; * @author Thomas Darimont * @author Christoph Strobl * @author Mark Paluch + * @author Divya Srivastava */ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentProperty implements MongoPersistentProperty { @@ -214,6 +215,19 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope return annotation != null ? annotation.order() : Integer.MAX_VALUE; } + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#skipNullValues() + */ + @Override + public boolean writeNullValues() { + + org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation( + org.springframework.data.mongodb.core.mapping.Field.class); + + return annotation != null && annotation.write() == Field.Write.ALWAYS; + } + /* * (non-Javadoc) * @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation() @@ -286,17 +300,4 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope return isAnnotationPresent(TextScore.class); } - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#isPropertyOmittableOnNull() - */ - public boolean isPropertyOmittableOnNull() { - org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation( - org.springframework.data.mongodb.core.mapping.Field.class); - - if ( annotation != null && annotation.write().equals(Field.Write.ALWAYS) ) { - return false; - } - return true; - } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java index 7af678541..53a501e06 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java @@ -33,6 +33,7 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty private boolean dbRefResolved; private @Nullable DBRef dbref; private @Nullable String fieldName; + private @Nullable Boolean writeNullValues; private @Nullable Class fieldType; private @Nullable Boolean usePropertyAccess; private @Nullable Boolean isTransient; @@ -90,6 +91,20 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty return this.fieldName; } + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty#skipNullValues() + */ + @Override + public boolean writeNullValues() { + + if (this.writeNullValues == null) { + this.writeNullValues = super.writeNullValues(); + } + + return this.writeNullValues; + } + /* * (non-Javadoc) * @see org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty#getFieldType() diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Field.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Field.java index b2365f8d6..b606cf15b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Field.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Field.java @@ -28,27 +28,13 @@ import org.springframework.core.annotation.AliasFor; * * @author Oliver Gierke * @author Christoph Strobl + * @author Divya Srivastava */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) public @interface Field { - /** - * Enumeration of write strategies for a field with null value.It decides whether a field with null value has to be - * written to the resulting document to be saved to the database. - */ - enum Write{ - /* - * The field will always be written to the database irrespective of null value. - */ - ALWAYS, - /* - * The field will only be written to the database if it has a non null value. - */ - NON_NULL - } - /** * The key to be used to store the field inside the document. Alias for {@link #name()}. * @@ -82,12 +68,32 @@ public @interface Field { FieldType targetType() default FieldType.IMPLICIT; /** - * If set to {@link Write#NON_NULL} {@literal null} values will be omitted. - * Setting the value to {@link Write#ALWAYS} explicitly adds an entry for the given field - * holding {@literal null} as a value {@code 'fieldName' : null }. + * Write rules when to include a property value upon conversion. If set to {@link Write#NON_NULL} (default) + * {@literal null} values are not written to the target {@code Document}. Setting the value to {@link Write#ALWAYS} + * explicitly adds an entry for the given field holding {@literal null} as a value {@code 'fieldName' : null }. *

- * NOTE Setting the value to {@link Write#ALWAYS} may lead to increased document size. + * NOTESetting the value to {@link Write#ALWAYS} may lead to increased document size. + * * @return {@link Write#NON_NULL} by default. + * @since 3.3 */ Write write() default Write.NON_NULL; + + /** + * Enumeration of write strategies to define when a property is included for write conversion. + * + * @since 3.3 + */ + enum Write { + + /** + * Value that indicates that property is to be always included, independent of value of the property. + */ + ALWAYS, + + /** + * Value that indicates that only properties with non-{@literal null} values are to be included. + */ + NON_NULL + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java index 10f35435d..2bd387d74 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java @@ -28,6 +28,7 @@ import org.springframework.lang.Nullable; * @author Patryk Wasik * @author Thomas Darimont * @author Christoph Strobl + * @author Divya Srivastava */ public interface MongoPersistentProperty extends PersistentProperty { @@ -54,6 +55,15 @@ public interface MongoPersistentProperty extends PersistentProperty - * It's annotated with {@link Field.Write}. - * - * @return - * @since 1.6 - */ - boolean isPropertyOmittableOnNull(); - - /** - * Returns whether the property is to be written to the document if the value is null
- * It's annotated with {@link omitNull}. - * - * @return - * @since 1.6 - */ - boolean isOmitNullProperty(); /** * Returns the {@link DBRef} if the property is a reference. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java index 3d7de5d09..6d4b163d2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java @@ -63,6 +63,11 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { return delegate.getFieldOrder(); } + @Override + public boolean writeNullValues() { + return delegate.writeNullValues(); + } + @Override public boolean isDbReference() { return delegate.isDbReference(); @@ -92,11 +97,6 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { public boolean isTextScoreProperty() { return delegate.isTextScoreProperty(); } - - @Override - public boolean isOmitNullProperty() { - return delegate.isOmitNullProperty(); - } @Override @Nullable @@ -321,8 +321,4 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { return delegate.getAccessorForOwner(owner); } - @Override - public boolean isPropertyOmittableOnNull() { - return delegate.isPropertyOmittableOnNull(); - } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index b18e1066f..f5521008f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -24,6 +24,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; import lombok.Value; import lombok.With; @@ -1692,7 +1693,7 @@ public class MongoTemplateTests { assertThat(template.count(query, collectionName)).isEqualTo(1L); } - @Test // DATAMONGO-571 + @Test // DATAMONGO-571, GH-3407 public void nullsPropertiesForVersionObjectUpdates() { VersionedPerson person = new VersionedPerson(); @@ -1702,11 +1703,17 @@ public class MongoTemplateTests { template.save(person); assertThat(person.id).isNotNull(); + person.firstname = null; person.lastname = null; template.save(person); person = template.findOne(query(where("id").is(person.id)), VersionedPerson.class); + assertThat(person.firstname).isNull(); assertThat(person.lastname).isNull(); + + org.bson.Document document = template.findOne(query(where("_id").is(person.id)), org.bson.Document.class, + "versionedPerson"); + assertThat(document).doesNotContainKey("firstname").containsEntry("lastname", null); } @Test // DATAMONGO-571 @@ -3703,6 +3710,64 @@ public class MongoTemplateTests { assertThat(template.find(new BasicQuery("{}").with(Sort.by("id")), WithIdAndFieldAnnotation.class)).isNotEmpty(); } + @Test // GH-3407 + void shouldWriteSubdocumentWithNullCorrectly() { + + template.dropCollection(WithSubdocument.class); + + WithSubdocument doc = new WithSubdocument(); + SubdocumentWithWriteNull subdoc = new SubdocumentWithWriteNull("Walter", "White"); + doc.subdocument = subdoc; + + template.save(doc); + + org.bson.Document loaded = template.findById(doc.id, org.bson.Document.class, "withSubdocument"); + + assertThat(loaded.get("subdocument", org.bson.Document.class)).hasSize(3).containsEntry("firstname", "Walter") + .containsEntry("nickname", null); + } + + @Test // GH-3407 + void shouldUpdateSubdocumentWithNullCorrectly() { + + template.dropCollection(WithSubdocument.class); + + WithSubdocument doc = new WithSubdocument(); + SubdocumentWithWriteNull subdoc = new SubdocumentWithWriteNull("Walter", "White"); + subdoc.nickname = "Heisenberg"; + doc.subdocument = subdoc; + + template.save(doc); + + String id = doc.id; + + doc.id = null; + subdoc.nickname = null; + template.update(WithSubdocument.class).replaceWith(doc).findAndReplaceValue(); + + org.bson.Document loaded = template.findById(id, org.bson.Document.class, "withSubdocument"); + + assertThat(loaded.get("subdocument", org.bson.Document.class)).hasSize(3).containsEntry("firstname", "Walter") + .containsEntry("nickname", null); + } + + @Test // GH-3407 + void shouldFindSubdocumentWithNullCorrectly() { + + template.dropCollection(WithSubdocument.class); + + WithSubdocument doc = new WithSubdocument(); + SubdocumentWithWriteNull subdoc = new SubdocumentWithWriteNull("Walter", "White"); + doc.subdocument = subdoc; + + template.save(doc); + + org.bson.Document loaded = template.findOne(query(where("subdocument").is(subdoc)), org.bson.Document.class, + "withSubdocument"); + + assertThat(loaded).isNotNull(); + } + private AtomicReference createAfterSaveReference() { AtomicReference saved = new AtomicReference<>(); @@ -4020,7 +4085,8 @@ public class MongoTemplateTests { static class VersionedPerson { @Version Long version; - String id, firstname, lastname; + String id, firstname; + @Field(write = Field.Write.ALWAYS) String lastname; } static class TypeWithFieldAnnotation { @@ -4247,4 +4313,22 @@ public class MongoTemplateTests { String value; } + + @Data + static class WithSubdocument { + + @Id // + @Field(name = "_id") // + String id; + SubdocumentWithWriteNull subdocument; + } + + @Data + @RequiredArgsConstructor + static class SubdocumentWithWriteNull { + + final String firstname, lastname; + + @Field(write = Field.Write.ALWAYS) String nickname; + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java index f8170889b..c3ee9b32f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java @@ -1139,7 +1139,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { .containsEntry("$geoNear.near.coordinates.[1]", 2D); } - @Test // DATAMONGO-2155 + @Test // DATAMONGO-2155, GH-3407 void saveVersionedEntityShouldCallUpdateCorrectly() { when(updateResult.getModifiedCount()).thenReturn(1L); @@ -1157,7 +1157,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { assertThat(queryCaptor.getValue()).isEqualTo(new Document("_id", 1).append("version", 10)); assertThat(updateCaptor.getValue()) - .isEqualTo(new Document("version", 11).append("_class", VersionedEntity.class.getName())); + .isEqualTo(new Document("version", 11).append("_class", VersionedEntity.class.getName()).append("name", null)); } @Test // DATAMONGO-1783 @@ -2273,6 +2273,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { @Id Integer id; @Version Integer version; + + @Field(write = Field.Write.ALWAYS) String name; } enum MyConverter implements Converter { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java index 2009b5314..036157141 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java @@ -2520,6 +2520,18 @@ class MappingMongoConverterUnitTests { assertThat(target.typeImplementingMap).isEqualTo(new TypeImplementingMap("one", 2)); } + @Test // GH-3407 + void shouldWriteNullPropertyCorrectly() { + + WithFieldWrite fieldWrite = new WithFieldWrite(); + + org.bson.Document document = new org.bson.Document(); + converter.write(fieldWrite, document); + + assertThat(document).containsEntry("writeAlways", null).doesNotContainKey("writeNonNull"); + assertThat(document).containsEntry("writeAlwaysPerson", null).doesNotContainKey("writeNonNullPerson"); + } + static class GenericType { T content; } @@ -3165,4 +3177,20 @@ class MappingMongoConverterUnitTests { return null; } } + + static class WithFieldWrite { + + @org.springframework.data.mongodb.core.mapping.Field( + write = org.springframework.data.mongodb.core.mapping.Field.Write.NON_NULL) Integer writeNonNull; + + @org.springframework.data.mongodb.core.mapping.Field( + write = org.springframework.data.mongodb.core.mapping.Field.Write.ALWAYS) Integer writeAlways; + + @org.springframework.data.mongodb.core.mapping.DBRef @org.springframework.data.mongodb.core.mapping.Field( + write = org.springframework.data.mongodb.core.mapping.Field.Write.NON_NULL) Person writeNonNullPerson; + + @org.springframework.data.mongodb.core.mapping.DBRef @org.springframework.data.mongodb.core.mapping.Field( + write = org.springframework.data.mongodb.core.mapping.Field.Write.ALWAYS) Person writeAlwaysPerson; + + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java index 69f9cb661..bbcb8dada 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java @@ -48,25 +48,26 @@ import org.springframework.util.ReflectionUtils; * @author Oliver Gierke * @author Christoph Strobl * @author Mark Paluch + * @author Divya Srivastava */ public class BasicMongoPersistentPropertyUnitTests { - MongoPersistentEntity entity; + private MongoPersistentEntity entity; @BeforeEach - public void setup() { + void setup() { entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Person.class)); } @Test - public void usesAnnotatedFieldName() { + void usesAnnotatedFieldName() { Field field = ReflectionUtils.findField(Person.class, "firstname"); assertThat(getPropertyFor(field).getFieldName()).isEqualTo("foo"); } @Test - public void returns_IdForIdProperty() { + void returns_IdForIdProperty() { Field field = ReflectionUtils.findField(Person.class, "id"); MongoPersistentProperty property = getPropertyFor(field); assertThat(property.isIdProperty()).isTrue(); @@ -74,19 +75,19 @@ public class BasicMongoPersistentPropertyUnitTests { } @Test - public void returnsPropertyNameForUnannotatedProperties() { + void returnsPropertyNameForUnannotatedProperties() { Field field = ReflectionUtils.findField(Person.class, "lastname"); assertThat(getPropertyFor(field).getFieldName()).isEqualTo("lastname"); } @Test - public void preventsNegativeOrder() { + void preventsNegativeOrder() { getPropertyFor(ReflectionUtils.findField(Person.class, "ssn")); } @Test // DATAMONGO-553 - public void usesPropertyAccessForThrowableCause() { + void usesPropertyAccessForThrowableCause() { BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( ClassTypeInformation.from(Throwable.class)); @@ -96,7 +97,7 @@ public class BasicMongoPersistentPropertyUnitTests { } @Test // DATAMONGO-607 - public void usesCustomFieldNamingStrategyByDefault() throws Exception { + void usesCustomFieldNamingStrategyByDefault() throws Exception { ClassTypeInformation type = ClassTypeInformation.from(Person.class); Field field = ReflectionUtils.findField(Person.class, "lastname"); @@ -113,7 +114,7 @@ public class BasicMongoPersistentPropertyUnitTests { } @Test // DATAMONGO-607 - public void rejectsInvalidValueReturnedByFieldNamingStrategy() { + void rejectsInvalidValueReturnedByFieldNamingStrategy() { ClassTypeInformation type = ClassTypeInformation.from(Person.class); Field field = ReflectionUtils.findField(Person.class, "lastname"); @@ -126,49 +127,42 @@ public class BasicMongoPersistentPropertyUnitTests { } @Test // DATAMONGO-937 - public void shouldDetectAnnotatedLanguagePropertyCorrectly() { + void shouldDetectAnnotatedLanguagePropertyCorrectly() { MongoPersistentProperty property = getPropertyFor(DocumentWithLanguageProperty.class, "lang"); assertThat(property.isLanguageProperty()).isTrue(); } @Test // DATAMONGO-937 - public void shouldDetectImplicitLanguagePropertyCorrectly() { + void shouldDetectImplicitLanguagePropertyCorrectly() { MongoPersistentProperty property = getPropertyFor(DocumentWithImplicitLanguageProperty.class, "language"); assertThat(property.isLanguageProperty()).isTrue(); } @Test // DATAMONGO-976 - public void shouldDetectTextScorePropertyCorrectly() { + void shouldDetectTextScorePropertyCorrectly() { MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score"); assertThat(property.isTextScoreProperty()).isTrue(); } - @Test // DATAMONGO-2551 - public void shouldDetectOmittableOnNullPropertyCorrectly() { - - MongoPersistentProperty property = getPropertyFor(DocumentWithOmittableOnNullProperty.class, "write"); - assertThat(property.isPropertyOmittableOnNull()).isTrue(); - } - @Test // DATAMONGO-976 - public void shouldDetectTextScoreAsReadOnlyProperty() { + void shouldDetectTextScoreAsReadOnlyProperty() { MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score"); assertThat(property.isWritable()).isFalse(); } @Test // DATAMONGO-1050 - public void shouldNotConsiderExplicitlyNameFieldAsIdProperty() { + void shouldNotConsiderExplicitlyNameFieldAsIdProperty() { MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdProperty.class, "id"); assertThat(property.isIdProperty()).isFalse(); } @Test // DATAMONGO-1050 - public void shouldConsiderPropertyAsIdWhenExplicitlyAnnotatedWithIdEvenWhenExplicitlyNamePresent() { + void shouldConsiderPropertyAsIdWhenExplicitlyAnnotatedWithIdEvenWhenExplicitlyNamePresent() { MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation.class, "id"); @@ -176,7 +170,7 @@ public class BasicMongoPersistentPropertyUnitTests { } @Test // DATAMONGO-1373 - public void shouldConsiderComposedAnnotationsForIdField() { + void shouldConsiderComposedAnnotationsForIdField() { MongoPersistentProperty property = getPropertyFor(DocumentWithComposedAnnotations.class, "myId"); assertThat(property.isIdProperty()).isTrue(); @@ -184,14 +178,14 @@ public class BasicMongoPersistentPropertyUnitTests { } @Test // DATAMONGO-1373 - public void shouldConsiderComposedAnnotationsForFields() { + void shouldConsiderComposedAnnotationsForFields() { MongoPersistentProperty property = getPropertyFor(DocumentWithComposedAnnotations.class, "myField"); assertThat(property.getFieldName()).isEqualTo("myField"); } @Test // DATAMONGO-1737 - public void honorsFieldOrderWhenIteratingOverProperties() { + void honorsFieldOrderWhenIteratingOverProperties() { MongoMappingContext context = new MongoMappingContext(); MongoPersistentEntity entity = context.getPersistentEntity(Sample.class); @@ -203,36 +197,45 @@ public class BasicMongoPersistentPropertyUnitTests { assertThat(properties).containsExactly("first", "second", "third"); } + @Test // GH-3407 + void shouldDetectWritability() { + + assertThat(getPropertyFor(WithFieldWrite.class, "fieldWithDefaults").writeNullValues()).isFalse(); + assertThat(getPropertyFor(WithFieldWrite.class, "fieldWithField").writeNullValues()).isFalse(); + assertThat(getPropertyFor(WithFieldWrite.class, "writeNonNull").writeNullValues()).isFalse(); + assertThat(getPropertyFor(WithFieldWrite.class, "writeAlways").writeNullValues()).isTrue(); + } + @Test // DATAMONGO-1798 - public void fieldTypeShouldReturnActualTypeForNonIdProperties() { + void fieldTypeShouldReturnActualTypeForNonIdProperties() { MongoPersistentProperty property = getPropertyFor(Person.class, "lastname"); assertThat(property.getFieldType()).isEqualTo(String.class); } @Test // DATAMONGO-1798 - public void fieldTypeShouldBeObjectIdForPropertiesAnnotatedWithCommonsId() { + void fieldTypeShouldBeObjectIdForPropertiesAnnotatedWithCommonsId() { MongoPersistentProperty property = getPropertyFor(Person.class, "id"); assertThat(property.getFieldType()).isEqualTo(ObjectId.class); } @Test // DATAMONGO-1798 - public void fieldTypeShouldBeImplicitForPropertiesAnnotatedWithMongoId() { + void fieldTypeShouldBeImplicitForPropertiesAnnotatedWithMongoId() { MongoPersistentProperty property = getPropertyFor(WithStringMongoId.class, "id"); assertThat(property.getFieldType()).isEqualTo(String.class); } @Test // DATAMONGO-1798 - public void fieldTypeShouldBeObjectIdForPropertiesAnnotatedWithMongoIdAndTargetTypeObjectId() { + void fieldTypeShouldBeObjectIdForPropertiesAnnotatedWithMongoIdAndTargetTypeObjectId() { MongoPersistentProperty property = getPropertyFor(WithStringMongoIdMappedToObjectId.class, "id"); assertThat(property.getFieldType()).isEqualTo(ObjectId.class); } @Test // DATAMONGO-2460 - public void fieldTypeShouldBeDocumentForPropertiesAnnotatedIdWhenAComplexTypeAndFieldTypeImplicit() { + void fieldTypeShouldBeDocumentForPropertiesAnnotatedIdWhenAComplexTypeAndFieldTypeImplicit() { MongoPersistentProperty property = getPropertyFor(WithComplexId.class, "id"); assertThat(property.getFieldType()).isEqualTo(Document.class); @@ -304,9 +307,15 @@ public class BasicMongoPersistentPropertyUnitTests { @TextScore Float score; } - static class DocumentWithOmittableOnNullProperty { + static class WithFieldWrite { - @org.springframework.data.mongodb.core.mapping.Field("write") org.springframework.data.mongodb.core.mapping.Field.Write write; + int fieldWithDefaults; + @org.springframework.data.mongodb.core.mapping.Field int fieldWithField; + @org.springframework.data.mongodb.core.mapping.Field( + write = org.springframework.data.mongodb.core.mapping.Field.Write.NON_NULL) Integer writeNonNull; + + @org.springframework.data.mongodb.core.mapping.Field( + write = org.springframework.data.mongodb.core.mapping.Field.Write.ALWAYS) Integer writeAlways; } diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index eac49f37b..a74594bff 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -5,6 +5,7 @@ == What's New in Spring Data MongoDB 3.3 * Extended support for <> entities. +* Include/exclude `null` properties on write to `Document` through `@Field(write=…)`. [[new-features.3.2]] == What's New in Spring Data MongoDB 3.2