DATAMONGO-1509 - Write type hint as last element of a Document.
Always add type hint as last property of a Document. This is necessary to assure document equality within MongoDB in cases where the query contains full document comparisons. Unfortunately this also might break existing stuff since the order of properties within a Document is changed with this commit. Original pull request: #411.
This commit is contained in:
committed by
Mark Paluch
parent
070d784be3
commit
3c16b4db7f
@@ -359,20 +359,20 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> entityType = obj.getClass();
|
||||
boolean handledByCustomConverter = conversions.getCustomWriteTarget(entityType, Document.class) != null;
|
||||
Class<?> entityType = ClassUtils.getUserClass(obj.getClass());
|
||||
TypeInformation<? extends Object> type = ClassTypeInformation.from(entityType);
|
||||
|
||||
if (!handledByCustomConverter && !(bson instanceof Collection)) {
|
||||
typeMapper.writeType(type, bson);
|
||||
}
|
||||
|
||||
Object target = obj instanceof LazyLoadingProxy ? ((LazyLoadingProxy) obj).getTarget() : obj;
|
||||
|
||||
writeInternal(target, bson, type);
|
||||
if (asMap(bson).containsKey("_is") && asMap(bson).get("_id") == null) {
|
||||
removeFromMap(bson, "_id");
|
||||
}
|
||||
|
||||
boolean handledByCustomConverter = conversions.getCustomWriteTarget(entityType, Document.class) != null;
|
||||
if (!handledByCustomConverter && !(bson instanceof Collection)) {
|
||||
typeMapper.writeType(type, bson);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -529,12 +529,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
Object existingValue = accessor.get(prop);
|
||||
Document document = existingValue instanceof Document ? (Document) existingValue : new Document();
|
||||
addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, document);
|
||||
|
||||
MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass())
|
||||
? mappingContext.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type);
|
||||
|
||||
writeInternal(obj, document, entity);
|
||||
addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, document);
|
||||
accessor.put(prop, document);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.bson.Document;
|
||||
@@ -80,4 +81,23 @@ public abstract class DocumentTestUtils {
|
||||
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
public static void assertTypeHint(Document document, Class<?> type) {
|
||||
assertTypeHint(document, type.getName());
|
||||
}
|
||||
|
||||
public static void assertTypeHint(Document document, String expectedTypeString) {
|
||||
|
||||
Iterator<String> keyIterator = document.keySet().iterator();
|
||||
while (keyIterator.hasNext()) {
|
||||
String key = keyIterator.next();
|
||||
if (key.equals("_class")) {
|
||||
assertThat((String) document.get(key), is(equalTo(expectedTypeString)));
|
||||
assertThat(keyIterator.hasNext(), is(false));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fail(String.format("Expected to find type info %s in %s.", document, expectedTypeString));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.hamcrest.collection.IsMapContaining;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
@@ -3486,6 +3487,22 @@ public class MongoTemplateTests {
|
||||
assertThat(document.id, is(notNullValue()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1509
|
||||
*/
|
||||
@Test
|
||||
public void findsByGnericNestedListElements() {
|
||||
|
||||
List<Model> modelList = Arrays.<Model>asList(new ModelA("value"));
|
||||
DocumentWithCollection dwc = new DocumentWithCollection(modelList);
|
||||
|
||||
template.insert(dwc);
|
||||
|
||||
Query query = query(where("models").is(modelList));
|
||||
assertThat(template.findOne(query, DocumentWithCollection.class), is(equalTo(dwc)));
|
||||
}
|
||||
|
||||
static class TypeWithNumbers {
|
||||
|
||||
@Id String id;
|
||||
@@ -3565,6 +3582,7 @@ public class MongoTemplateTests {
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) public Map<String, Sample> lazyDbRefAnnotatedMap;
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
static class DocumentWithCollection {
|
||||
|
||||
@Id String id;
|
||||
@@ -3617,6 +3635,7 @@ public class MongoTemplateTests {
|
||||
String id();
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
static class ModelA implements Model {
|
||||
|
||||
@Id String id;
|
||||
|
||||
@@ -526,6 +526,9 @@ public class MappingMongoConverterUnitTests {
|
||||
assertThat(converter.convertToMongoType(null), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1509
|
||||
*/
|
||||
@Test
|
||||
public void writesGenericTypeCorrectly() {
|
||||
|
||||
@@ -537,7 +540,7 @@ public class MappingMongoConverterUnitTests {
|
||||
converter.write(type, result);
|
||||
|
||||
org.bson.Document content = (org.bson.Document) result.get("content");
|
||||
assertThat(content.get("_class"), is(notNullValue()));
|
||||
assertTypeHint(content, Address.class);
|
||||
assertThat(content.get("city"), is(notNullValue()));
|
||||
}
|
||||
|
||||
@@ -1272,6 +1275,7 @@ public class MappingMongoConverterUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-523
|
||||
* @see DATAMONGO-1509
|
||||
*/
|
||||
@Test
|
||||
public void considersTypeAliasAnnotation() {
|
||||
@@ -1282,9 +1286,7 @@ public class MappingMongoConverterUnitTests {
|
||||
org.bson.Document result = new org.bson.Document();
|
||||
converter.write(aliased, result);
|
||||
|
||||
Object type = result.get("_class");
|
||||
assertThat(type, is(notNullValue()));
|
||||
assertThat(type.toString(), is("_"));
|
||||
assertTypeHint(result, "_");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1409,6 +1411,7 @@ public class MappingMongoConverterUnitTests {
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
* @see DATAMONGO-893
|
||||
* @see DATAMONGO-1509
|
||||
*/
|
||||
@Test
|
||||
public void convertsListToBasicDBListAndRetainsTypeInformationForComplexObjects() {
|
||||
@@ -1424,7 +1427,7 @@ public class MappingMongoConverterUnitTests {
|
||||
|
||||
List<Object> dbList = (List<Object>) result;
|
||||
assertThat(dbList, hasSize(1));
|
||||
assertThat(getTypedValue(getAsDocument(dbList, 0), "_class", String.class), equalTo(Address.class.getName()));
|
||||
assertTypeHint(getAsDocument(dbList, 0), Address.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1444,6 +1447,7 @@ public class MappingMongoConverterUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
* @see DATAMONGO-1509
|
||||
*/
|
||||
@Test
|
||||
public void convertsArrayToBasicDBListAndRetainsTypeInformationForComplexObjects() {
|
||||
@@ -1458,7 +1462,7 @@ public class MappingMongoConverterUnitTests {
|
||||
|
||||
List<Object> dbList = (List<Object>) result;
|
||||
assertThat(dbList, hasSize(1));
|
||||
assertThat(getTypedValue(getAsDocument(dbList, 0), "_class", String.class), equalTo(Address.class.getName()));
|
||||
assertTypeHint(getAsDocument(dbList, 0), Address.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1802,6 +1806,7 @@ public class MappingMongoConverterUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1001
|
||||
* @see DATAMONGO-1509
|
||||
*/
|
||||
@Test
|
||||
public void shouldWriteCglibProxiedClassTypeInformationCorrectly() {
|
||||
@@ -1814,7 +1819,7 @@ public class MappingMongoConverterUnitTests {
|
||||
org.bson.Document document = new org.bson.Document();
|
||||
converter.write(proxied, document);
|
||||
|
||||
assertThat(document.get("_class"), is((Object) GenericType.class.getName()));
|
||||
assertTypeHint(document, GenericType.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user