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:
@@ -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" })
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user