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.
This commit is contained in:
Mark Paluch
2021-06-14 09:30:08 +02:00
parent b1020d19ba
commit c217618d9d
11 changed files with 242 additions and 102 deletions

View File

@@ -103,6 +103,7 @@ import com.mongodb.DBRef;
* @author Mark Paluch * @author Mark Paluch
* @author Roman Puchkovskiy * @author Roman Puchkovskiy
* @author Heesu Jung * @author Heesu Jung
* @author Divya Srivastava
*/ */
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware { public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware {
@@ -737,6 +738,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
continue; continue;
} }
if (prop.isAssociation()) { if (prop.isAssociation()) {
writeAssociation(prop.getRequiredAssociation(), accessor, dbObjectAccessor); writeAssociation(prop.getRequiredAssociation(), accessor, dbObjectAccessor);
continue; continue;
} }
@@ -744,13 +746,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Object value = accessor.getProperty(prop); Object value = accessor.getProperty(prop);
if (value == null) { if (value == null) {
if(!prop.isPropertyOmittableOnNull()) { if (prop.writeNullValues()) {
writeSimpleInternal(value, bson , prop); dbObjectAccessor.put(prop, null);
} }
continue; } else if (!conversions.isSimpleType(value.getClass())) {
}
if (!conversions.isSimpleType(value.getClass())) {
writePropertyInternal(value, dbObjectAccessor, prop); writePropertyInternal(value, dbObjectAccessor, prop);
} else { } else {
writeSimpleInternal(value, bson, prop); writeSimpleInternal(value, bson, prop);
@@ -763,7 +762,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
MongoPersistentProperty inverseProp = association.getInverse(); 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" }) @SuppressWarnings({ "unchecked" })

View File

@@ -41,6 +41,7 @@ import org.springframework.util.StringUtils;
* @author Thomas Darimont * @author Thomas Darimont
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch * @author Mark Paluch
* @author Divya Srivastava
*/ */
public class BasicMongoPersistentProperty extends AnnotationBasedPersistentProperty<MongoPersistentProperty> public class BasicMongoPersistentProperty extends AnnotationBasedPersistentProperty<MongoPersistentProperty>
implements MongoPersistentProperty { implements MongoPersistentProperty {
@@ -214,6 +215,19 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
return annotation != null ? annotation.order() : Integer.MAX_VALUE; 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) * (non-Javadoc)
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation() * @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation()
@@ -286,17 +300,4 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
return isAnnotationPresent(TextScore.class); 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;
}
} }

View File

@@ -33,6 +33,7 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty
private boolean dbRefResolved; private boolean dbRefResolved;
private @Nullable DBRef dbref; private @Nullable DBRef dbref;
private @Nullable String fieldName; private @Nullable String fieldName;
private @Nullable Boolean writeNullValues;
private @Nullable Class<?> fieldType; private @Nullable Class<?> fieldType;
private @Nullable Boolean usePropertyAccess; private @Nullable Boolean usePropertyAccess;
private @Nullable Boolean isTransient; private @Nullable Boolean isTransient;
@@ -90,6 +91,20 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty
return this.fieldName; 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) * (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty#getFieldType() * @see org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty#getFieldType()

View File

@@ -28,27 +28,13 @@ import org.springframework.core.annotation.AliasFor;
* *
* @author Oliver Gierke * @author Oliver Gierke
* @author Christoph Strobl * @author Christoph Strobl
* @author Divya Srivastava
*/ */
@Documented @Documented
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface Field { 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()}. * 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; FieldType targetType() default FieldType.IMPLICIT;
/** /**
* If set to {@link Write#NON_NULL} {@literal null} values will be omitted. * Write rules when to include a property value upon conversion. If set to {@link Write#NON_NULL} (default)
* Setting the value to {@link Write#ALWAYS} explicitly adds an entry for the given field * {@literal null} values are not written to the target {@code Document}. Setting the value to {@link Write#ALWAYS}
* holding {@literal null} as a value {@code 'fieldName' : null }. * explicitly adds an entry for the given field holding {@literal null} as a value {@code 'fieldName' : null }.
* <p /> * <p />
* <strong>NOTE</strong> Setting the value to {@link Write#ALWAYS} may lead to increased document size. * <strong>NOTE</strong>Setting the value to {@link Write#ALWAYS} may lead to increased document size.
*
* @return {@link Write#NON_NULL} by default. * @return {@link Write#NON_NULL} by default.
* @since 3.3
*/ */
Write write() default Write.NON_NULL; 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
}
} }

View File

@@ -28,6 +28,7 @@ import org.springframework.lang.Nullable;
* @author Patryk Wasik * @author Patryk Wasik
* @author Thomas Darimont * @author Thomas Darimont
* @author Christoph Strobl * @author Christoph Strobl
* @author Divya Srivastava
*/ */
public interface MongoPersistentProperty extends PersistentProperty<MongoPersistentProperty> { public interface MongoPersistentProperty extends PersistentProperty<MongoPersistentProperty> {
@@ -54,6 +55,15 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
*/ */
int getFieldOrder(); int getFieldOrder();
/**
* Returns whether the property should be written to the database if its value is {@literal null}.
*
* @return
* @since 3.3
* @see Field.Write
*/
boolean writeNullValues();
/** /**
* Returns whether the property is a {@link com.mongodb.DBRef}. If this returns {@literal true} you can expect * Returns whether the property is a {@link com.mongodb.DBRef}. If this returns {@literal true} you can expect
* {@link #getDBRef()} to return an non-{@literal null} value. * {@link #getDBRef()} to return an non-{@literal null} value.
@@ -105,24 +115,6 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
*/ */
boolean isTextScoreProperty(); boolean isTextScoreProperty();
/**
* Returns whether the property is to be written to the document if the value is null <br/>
* 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 <br/>
* It's annotated with {@link omitNull}.
*
* @return
* @since 1.6
*/
boolean isOmitNullProperty();
/** /**
* Returns the {@link DBRef} if the property is a reference. * Returns the {@link DBRef} if the property is a reference.
* *

View File

@@ -63,6 +63,11 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty {
return delegate.getFieldOrder(); return delegate.getFieldOrder();
} }
@Override
public boolean writeNullValues() {
return delegate.writeNullValues();
}
@Override @Override
public boolean isDbReference() { public boolean isDbReference() {
return delegate.isDbReference(); return delegate.isDbReference();
@@ -93,11 +98,6 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty {
return delegate.isTextScoreProperty(); return delegate.isTextScoreProperty();
} }
@Override
public boolean isOmitNullProperty() {
return delegate.isOmitNullProperty();
}
@Override @Override
@Nullable @Nullable
public DBRef getDBRef() { public DBRef getDBRef() {
@@ -321,8 +321,4 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty {
return delegate.getAccessorForOwner(owner); return delegate.getAccessorForOwner(owner);
} }
@Override
public boolean isPropertyOmittableOnNull() {
return delegate.isPropertyOmittableOnNull();
}
} }

View File

@@ -24,6 +24,7 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Value; import lombok.Value;
import lombok.With; import lombok.With;
@@ -1692,7 +1693,7 @@ public class MongoTemplateTests {
assertThat(template.count(query, collectionName)).isEqualTo(1L); assertThat(template.count(query, collectionName)).isEqualTo(1L);
} }
@Test // DATAMONGO-571 @Test // DATAMONGO-571, GH-3407
public void nullsPropertiesForVersionObjectUpdates() { public void nullsPropertiesForVersionObjectUpdates() {
VersionedPerson person = new VersionedPerson(); VersionedPerson person = new VersionedPerson();
@@ -1702,11 +1703,17 @@ public class MongoTemplateTests {
template.save(person); template.save(person);
assertThat(person.id).isNotNull(); assertThat(person.id).isNotNull();
person.firstname = null;
person.lastname = null; person.lastname = null;
template.save(person); template.save(person);
person = template.findOne(query(where("id").is(person.id)), VersionedPerson.class); person = template.findOne(query(where("id").is(person.id)), VersionedPerson.class);
assertThat(person.firstname).isNull();
assertThat(person.lastname).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 @Test // DATAMONGO-571
@@ -3703,6 +3710,64 @@ public class MongoTemplateTests {
assertThat(template.find(new BasicQuery("{}").with(Sort.by("id")), WithIdAndFieldAnnotation.class)).isNotEmpty(); 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<ImmutableVersioned> createAfterSaveReference() { private AtomicReference<ImmutableVersioned> createAfterSaveReference() {
AtomicReference<ImmutableVersioned> saved = new AtomicReference<>(); AtomicReference<ImmutableVersioned> saved = new AtomicReference<>();
@@ -4020,7 +4085,8 @@ public class MongoTemplateTests {
static class VersionedPerson { static class VersionedPerson {
@Version Long version; @Version Long version;
String id, firstname, lastname; String id, firstname;
@Field(write = Field.Write.ALWAYS) String lastname;
} }
static class TypeWithFieldAnnotation { static class TypeWithFieldAnnotation {
@@ -4247,4 +4313,22 @@ public class MongoTemplateTests {
String value; 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;
}
} }

View File

@@ -1139,7 +1139,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
.containsEntry("$geoNear.near.coordinates.[1]", 2D); .containsEntry("$geoNear.near.coordinates.[1]", 2D);
} }
@Test // DATAMONGO-2155 @Test // DATAMONGO-2155, GH-3407
void saveVersionedEntityShouldCallUpdateCorrectly() { void saveVersionedEntityShouldCallUpdateCorrectly() {
when(updateResult.getModifiedCount()).thenReturn(1L); 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(queryCaptor.getValue()).isEqualTo(new Document("_id", 1).append("version", 10));
assertThat(updateCaptor.getValue()) 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 @Test // DATAMONGO-1783
@@ -2273,6 +2273,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
@Id Integer id; @Id Integer id;
@Version Integer version; @Version Integer version;
@Field(write = Field.Write.ALWAYS) String name;
} }
enum MyConverter implements Converter<AutogenerateableId, String> { enum MyConverter implements Converter<AutogenerateableId, String> {

View File

@@ -2520,6 +2520,18 @@ class MappingMongoConverterUnitTests {
assertThat(target.typeImplementingMap).isEqualTo(new TypeImplementingMap("one", 2)); 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> { static class GenericType<T> {
T content; T content;
} }
@@ -3165,4 +3177,20 @@ class MappingMongoConverterUnitTests {
return null; 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;
}
} }

View File

@@ -48,25 +48,26 @@ import org.springframework.util.ReflectionUtils;
* @author Oliver Gierke * @author Oliver Gierke
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch * @author Mark Paluch
* @author Divya Srivastava
*/ */
public class BasicMongoPersistentPropertyUnitTests { public class BasicMongoPersistentPropertyUnitTests {
MongoPersistentEntity<Person> entity; private MongoPersistentEntity<Person> entity;
@BeforeEach @BeforeEach
public void setup() { void setup() {
entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Person.class)); entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Person.class));
} }
@Test @Test
public void usesAnnotatedFieldName() { void usesAnnotatedFieldName() {
Field field = ReflectionUtils.findField(Person.class, "firstname"); Field field = ReflectionUtils.findField(Person.class, "firstname");
assertThat(getPropertyFor(field).getFieldName()).isEqualTo("foo"); assertThat(getPropertyFor(field).getFieldName()).isEqualTo("foo");
} }
@Test @Test
public void returns_IdForIdProperty() { void returns_IdForIdProperty() {
Field field = ReflectionUtils.findField(Person.class, "id"); Field field = ReflectionUtils.findField(Person.class, "id");
MongoPersistentProperty property = getPropertyFor(field); MongoPersistentProperty property = getPropertyFor(field);
assertThat(property.isIdProperty()).isTrue(); assertThat(property.isIdProperty()).isTrue();
@@ -74,19 +75,19 @@ public class BasicMongoPersistentPropertyUnitTests {
} }
@Test @Test
public void returnsPropertyNameForUnannotatedProperties() { void returnsPropertyNameForUnannotatedProperties() {
Field field = ReflectionUtils.findField(Person.class, "lastname"); Field field = ReflectionUtils.findField(Person.class, "lastname");
assertThat(getPropertyFor(field).getFieldName()).isEqualTo("lastname"); assertThat(getPropertyFor(field).getFieldName()).isEqualTo("lastname");
} }
@Test @Test
public void preventsNegativeOrder() { void preventsNegativeOrder() {
getPropertyFor(ReflectionUtils.findField(Person.class, "ssn")); getPropertyFor(ReflectionUtils.findField(Person.class, "ssn"));
} }
@Test // DATAMONGO-553 @Test // DATAMONGO-553
public void usesPropertyAccessForThrowableCause() { void usesPropertyAccessForThrowableCause() {
BasicMongoPersistentEntity<Throwable> entity = new BasicMongoPersistentEntity<>( BasicMongoPersistentEntity<Throwable> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(Throwable.class)); ClassTypeInformation.from(Throwable.class));
@@ -96,7 +97,7 @@ public class BasicMongoPersistentPropertyUnitTests {
} }
@Test // DATAMONGO-607 @Test // DATAMONGO-607
public void usesCustomFieldNamingStrategyByDefault() throws Exception { void usesCustomFieldNamingStrategyByDefault() throws Exception {
ClassTypeInformation<Person> type = ClassTypeInformation.from(Person.class); ClassTypeInformation<Person> type = ClassTypeInformation.from(Person.class);
Field field = ReflectionUtils.findField(Person.class, "lastname"); Field field = ReflectionUtils.findField(Person.class, "lastname");
@@ -113,7 +114,7 @@ public class BasicMongoPersistentPropertyUnitTests {
} }
@Test // DATAMONGO-607 @Test // DATAMONGO-607
public void rejectsInvalidValueReturnedByFieldNamingStrategy() { void rejectsInvalidValueReturnedByFieldNamingStrategy() {
ClassTypeInformation<Person> type = ClassTypeInformation.from(Person.class); ClassTypeInformation<Person> type = ClassTypeInformation.from(Person.class);
Field field = ReflectionUtils.findField(Person.class, "lastname"); Field field = ReflectionUtils.findField(Person.class, "lastname");
@@ -126,49 +127,42 @@ public class BasicMongoPersistentPropertyUnitTests {
} }
@Test // DATAMONGO-937 @Test // DATAMONGO-937
public void shouldDetectAnnotatedLanguagePropertyCorrectly() { void shouldDetectAnnotatedLanguagePropertyCorrectly() {
MongoPersistentProperty property = getPropertyFor(DocumentWithLanguageProperty.class, "lang"); MongoPersistentProperty property = getPropertyFor(DocumentWithLanguageProperty.class, "lang");
assertThat(property.isLanguageProperty()).isTrue(); assertThat(property.isLanguageProperty()).isTrue();
} }
@Test // DATAMONGO-937 @Test // DATAMONGO-937
public void shouldDetectImplicitLanguagePropertyCorrectly() { void shouldDetectImplicitLanguagePropertyCorrectly() {
MongoPersistentProperty property = getPropertyFor(DocumentWithImplicitLanguageProperty.class, "language"); MongoPersistentProperty property = getPropertyFor(DocumentWithImplicitLanguageProperty.class, "language");
assertThat(property.isLanguageProperty()).isTrue(); assertThat(property.isLanguageProperty()).isTrue();
} }
@Test // DATAMONGO-976 @Test // DATAMONGO-976
public void shouldDetectTextScorePropertyCorrectly() { void shouldDetectTextScorePropertyCorrectly() {
MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score"); MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score");
assertThat(property.isTextScoreProperty()).isTrue(); assertThat(property.isTextScoreProperty()).isTrue();
} }
@Test // DATAMONGO-2551
public void shouldDetectOmittableOnNullPropertyCorrectly() {
MongoPersistentProperty property = getPropertyFor(DocumentWithOmittableOnNullProperty.class, "write");
assertThat(property.isPropertyOmittableOnNull()).isTrue();
}
@Test // DATAMONGO-976 @Test // DATAMONGO-976
public void shouldDetectTextScoreAsReadOnlyProperty() { void shouldDetectTextScoreAsReadOnlyProperty() {
MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score"); MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score");
assertThat(property.isWritable()).isFalse(); assertThat(property.isWritable()).isFalse();
} }
@Test // DATAMONGO-1050 @Test // DATAMONGO-1050
public void shouldNotConsiderExplicitlyNameFieldAsIdProperty() { void shouldNotConsiderExplicitlyNameFieldAsIdProperty() {
MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdProperty.class, "id"); MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdProperty.class, "id");
assertThat(property.isIdProperty()).isFalse(); assertThat(property.isIdProperty()).isFalse();
} }
@Test // DATAMONGO-1050 @Test // DATAMONGO-1050
public void shouldConsiderPropertyAsIdWhenExplicitlyAnnotatedWithIdEvenWhenExplicitlyNamePresent() { void shouldConsiderPropertyAsIdWhenExplicitlyAnnotatedWithIdEvenWhenExplicitlyNamePresent() {
MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation.class, MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation.class,
"id"); "id");
@@ -176,7 +170,7 @@ public class BasicMongoPersistentPropertyUnitTests {
} }
@Test // DATAMONGO-1373 @Test // DATAMONGO-1373
public void shouldConsiderComposedAnnotationsForIdField() { void shouldConsiderComposedAnnotationsForIdField() {
MongoPersistentProperty property = getPropertyFor(DocumentWithComposedAnnotations.class, "myId"); MongoPersistentProperty property = getPropertyFor(DocumentWithComposedAnnotations.class, "myId");
assertThat(property.isIdProperty()).isTrue(); assertThat(property.isIdProperty()).isTrue();
@@ -184,14 +178,14 @@ public class BasicMongoPersistentPropertyUnitTests {
} }
@Test // DATAMONGO-1373 @Test // DATAMONGO-1373
public void shouldConsiderComposedAnnotationsForFields() { void shouldConsiderComposedAnnotationsForFields() {
MongoPersistentProperty property = getPropertyFor(DocumentWithComposedAnnotations.class, "myField"); MongoPersistentProperty property = getPropertyFor(DocumentWithComposedAnnotations.class, "myField");
assertThat(property.getFieldName()).isEqualTo("myField"); assertThat(property.getFieldName()).isEqualTo("myField");
} }
@Test // DATAMONGO-1737 @Test // DATAMONGO-1737
public void honorsFieldOrderWhenIteratingOverProperties() { void honorsFieldOrderWhenIteratingOverProperties() {
MongoMappingContext context = new MongoMappingContext(); MongoMappingContext context = new MongoMappingContext();
MongoPersistentEntity<?> entity = context.getPersistentEntity(Sample.class); MongoPersistentEntity<?> entity = context.getPersistentEntity(Sample.class);
@@ -203,36 +197,45 @@ public class BasicMongoPersistentPropertyUnitTests {
assertThat(properties).containsExactly("first", "second", "third"); 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 @Test // DATAMONGO-1798
public void fieldTypeShouldReturnActualTypeForNonIdProperties() { void fieldTypeShouldReturnActualTypeForNonIdProperties() {
MongoPersistentProperty property = getPropertyFor(Person.class, "lastname"); MongoPersistentProperty property = getPropertyFor(Person.class, "lastname");
assertThat(property.getFieldType()).isEqualTo(String.class); assertThat(property.getFieldType()).isEqualTo(String.class);
} }
@Test // DATAMONGO-1798 @Test // DATAMONGO-1798
public void fieldTypeShouldBeObjectIdForPropertiesAnnotatedWithCommonsId() { void fieldTypeShouldBeObjectIdForPropertiesAnnotatedWithCommonsId() {
MongoPersistentProperty property = getPropertyFor(Person.class, "id"); MongoPersistentProperty property = getPropertyFor(Person.class, "id");
assertThat(property.getFieldType()).isEqualTo(ObjectId.class); assertThat(property.getFieldType()).isEqualTo(ObjectId.class);
} }
@Test // DATAMONGO-1798 @Test // DATAMONGO-1798
public void fieldTypeShouldBeImplicitForPropertiesAnnotatedWithMongoId() { void fieldTypeShouldBeImplicitForPropertiesAnnotatedWithMongoId() {
MongoPersistentProperty property = getPropertyFor(WithStringMongoId.class, "id"); MongoPersistentProperty property = getPropertyFor(WithStringMongoId.class, "id");
assertThat(property.getFieldType()).isEqualTo(String.class); assertThat(property.getFieldType()).isEqualTo(String.class);
} }
@Test // DATAMONGO-1798 @Test // DATAMONGO-1798
public void fieldTypeShouldBeObjectIdForPropertiesAnnotatedWithMongoIdAndTargetTypeObjectId() { void fieldTypeShouldBeObjectIdForPropertiesAnnotatedWithMongoIdAndTargetTypeObjectId() {
MongoPersistentProperty property = getPropertyFor(WithStringMongoIdMappedToObjectId.class, "id"); MongoPersistentProperty property = getPropertyFor(WithStringMongoIdMappedToObjectId.class, "id");
assertThat(property.getFieldType()).isEqualTo(ObjectId.class); assertThat(property.getFieldType()).isEqualTo(ObjectId.class);
} }
@Test // DATAMONGO-2460 @Test // DATAMONGO-2460
public void fieldTypeShouldBeDocumentForPropertiesAnnotatedIdWhenAComplexTypeAndFieldTypeImplicit() { void fieldTypeShouldBeDocumentForPropertiesAnnotatedIdWhenAComplexTypeAndFieldTypeImplicit() {
MongoPersistentProperty property = getPropertyFor(WithComplexId.class, "id"); MongoPersistentProperty property = getPropertyFor(WithComplexId.class, "id");
assertThat(property.getFieldType()).isEqualTo(Document.class); assertThat(property.getFieldType()).isEqualTo(Document.class);
@@ -304,9 +307,15 @@ public class BasicMongoPersistentPropertyUnitTests {
@TextScore Float score; @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;
} }

View File

@@ -5,6 +5,7 @@
== What's New in Spring Data MongoDB 3.3 == What's New in Spring Data MongoDB 3.3
* Extended support for <<mapping-usage.document-references, referencing>> entities. * Extended support for <<mapping-usage.document-references, referencing>> entities.
* Include/exclude `null` properties on write to `Document` through `@Field(write=…)`.
[[new-features.3.2]] [[new-features.3.2]]
== What's New in Spring Data MongoDB 3.2 == What's New in Spring Data MongoDB 3.2