DATAMONGO-1609 - Hacking.

This commit is contained in:
Oliver Gierke
2016-09-28 15:28:04 +02:00
parent ac3f7dbf99
commit 826d00afa7
22 changed files with 472 additions and 474 deletions

View File

@@ -31,6 +31,7 @@
<springdata.commons>2.0.0.BUILD-SNAPSHOT</springdata.commons>
<mongo>3.2.2</mongo>
<mongo.reactivestreams>1.2.0</mongo.reactivestreams>
<mockito>2.2.17</mockito>
</properties>
<developers>

View File

@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
import static org.springframework.data.util.Optionals.*;
import java.io.IOException;
import java.util.*;
@@ -94,6 +95,8 @@ import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.util.MongoClientVersion;
import org.springframework.data.util.CloseableIterator;
import org.springframework.data.util.Optionals;
import org.springframework.data.util.Pair;
import org.springframework.jca.cci.core.ConnectionCallback;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@@ -355,7 +358,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
public CloseableIterator<T> doInCollection(MongoCollection<Document> collection)
throws MongoException, DataAccessException {
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entityType);
MongoPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(entityType);
Document mappedFields = queryMapper.getMappedFields(query.getFieldsObject(), persistentEntity);
Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), persistentEntity);
@@ -441,7 +444,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.notNull(query, "Query must not be null!");
Document queryObject = queryMapper.getMappedObject(query.getQueryObject(), null);
Document queryObject = queryMapper.getMappedObject(query.getQueryObject(), Optional.empty());
Document sortObject = query.getSortObject();
Document fieldsObject = query.getFieldsObject();
@@ -633,9 +636,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
public <T> T findById(Object id, Class<T> entityClass, String collectionName) {
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entityClass);
MongoPersistentProperty idProperty = persistentEntity == null ? null : persistentEntity.getIdProperty();
String idKey = idProperty == null ? ID_FIELD : idProperty.getName();
String idKey = mappingContext.getPersistentEntity(entityClass)//
.flatMap(it -> it.getIdProperty())//
.map(it -> it.getName()).orElse(ID_FIELD);
return doFindOne(collectionName, new Document(idKey, id), null, entityClass);
}
@@ -755,13 +760,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
final Document document = query == null ? null
: queryMapper.getMappedObject(query.getQueryObject(),
entityClass == null ? null : mappingContext.getPersistentEntity(entityClass));
Optional.ofNullable(entityClass).flatMap(it -> mappingContext.getPersistentEntity(entityClass)));
return execute(collectionName, new CollectionCallback<Long>() {
public Long doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
return collection.count(document);
}
});
return execute(collectionName, (CollectionCallback<Long>) collection -> collection.count(document));
}
/*
@@ -878,13 +879,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private void initializeVersionProperty(Object entity) {
MongoPersistentEntity<?> mongoPersistentEntity = getPersistentEntity(entity.getClass());
Optional<? extends MongoPersistentEntity<?>> persistentEntity = getPersistentEntity(entity.getClass());
if (mongoPersistentEntity != null && mongoPersistentEntity.hasVersionProperty()) {
ConvertingPropertyAccessor accessor = new ConvertingPropertyAccessor(
mongoPersistentEntity.getPropertyAccessor(entity), mongoConverter.getConversionService());
accessor.setProperty(mongoPersistentEntity.getVersionProperty(), 0);
}
ifAllPresent(persistentEntity, persistentEntity.flatMap(it -> it.getVersionProperty()), (l, r) -> {
ConvertingPropertyAccessor accessor = new ConvertingPropertyAccessor(l.getPropertyAccessor(entity),
mongoConverter.getConversionService());
accessor.setProperty(r, Optional.of(0));
});
}
public void insert(Collection<? extends Object> batchToSave, Class<?> entityClass) {
@@ -909,11 +910,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
continue;
}
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(element.getClass());
if (entity == null) {
throw new InvalidDataAccessApiUsageException("No PersistentEntity information found for " + element.getClass());
}
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(element.getClass());
String collection = entity.getCollection();
List<T> collectionElements = elementsByCollection.get(collection);
@@ -970,35 +967,26 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.notNull(objectToSave, "Object to save must not be null!");
Assert.hasText(collectionName, "Collection name must not be null or empty!");
MongoPersistentEntity<?> mongoPersistentEntity = getPersistentEntity(objectToSave.getClass());
Optional<? extends MongoPersistentEntity<?>> entity = getPersistentEntity(objectToSave.getClass());
Optional<MongoPersistentProperty> versionProperty = entity.flatMap(it -> it.getVersionProperty());
// No optimistic locking -> simple save
if (mongoPersistentEntity == null || !mongoPersistentEntity.hasVersionProperty()) {
doSave(collectionName, objectToSave, this.mongoConverter);
return;
}
doSaveVersioned(objectToSave, mongoPersistentEntity, collectionName);
mapIfAllPresent(entity, versionProperty, //
(l, r) -> doSaveVersioned(objectToSave, l, collectionName))//
.orElseGet(() -> doSave(collectionName, objectToSave, this.mongoConverter));
}
private <T> void doSaveVersioned(T objectToSave, MongoPersistentEntity<?> entity, String collectionName) {
private <T> T doSaveVersioned(T objectToSave, MongoPersistentEntity<?> entity, String collectionName) {
ConvertingPropertyAccessor convertingAccessor = new ConvertingPropertyAccessor(
entity.getPropertyAccessor(objectToSave), mongoConverter.getConversionService());
MongoPersistentProperty idProperty = entity.getIdProperty();
MongoPersistentProperty versionProperty = entity.getVersionProperty();
Optional<MongoPersistentProperty> versionProperty = entity.getVersionProperty();
Optional<Number> versionNumber = versionProperty.flatMap(it -> convertingAccessor.getProperty(it, Number.class));
Object version = convertingAccessor.getProperty(versionProperty);
Number versionNumber = convertingAccessor.getProperty(versionProperty, Number.class);
// Fresh instance -> initialize version property
if (version == null) {
doInsert(collectionName, objectToSave, this.mongoConverter);
} else {
return mapIfAllPresent(versionProperty, versionNumber, (property, number) -> {
// Bump version number
convertingAccessor.setProperty(versionProperty, versionNumber.longValue() + 1);
convertingAccessor.setProperty(property, Optional.of(number.longValue() + 1));
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));
assertUpdateableIdIfNotSet(objectToSave);
@@ -1011,8 +999,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Update update = Update.fromDocument(document, ID_FIELD);
// Create query for entity with the id and old version
Object id = convertingAccessor.getProperty(idProperty);
Query query = new Query(Criteria.where(idProperty.getName()).is(id).and(versionProperty.getName()).is(version));
MongoPersistentProperty idProperty = entity.getRequiredIdProperty();
Object id = convertingAccessor.getProperty(idProperty)
.orElseThrow(() -> new IllegalStateException("Required id not found!"));
Query query = new Query(Criteria.where(idProperty.getName()).is(id).and(property.getName()).is(number));
UpdateResult result = doUpdate(collectionName, query, update, objectToSave.getClass(), false, false);
@@ -1022,10 +1012,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
versionNumber, collectionName));
}
maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, document, collectionName));
}
return objectToSave;
}).orElseGet(() -> {
doInsert(collectionName, objectToSave, this.mongoConverter);
return objectToSave;
});
}
protected <T> void doSave(String collectionName, T objectToSave, MongoWriter<T> writer) {
protected <T> T doSave(String collectionName, T objectToSave, MongoWriter<T> writer) {
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));
assertUpdateableIdIfNotSet(objectToSave);
@@ -1037,6 +1033,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
populateIdIfNecessary(objectToSave, id);
maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, dbDoc, collectionName));
return objectToSave;
}
protected Object insertDocument(final String collectionName, final Document document, final Class<?> entityClass) {
@@ -1174,7 +1172,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
public UpdateResult doInCollection(MongoCollection<Document> collection)
throws MongoException, DataAccessException {
MongoPersistentEntity<?> entity = entityClass == null ? null : getPersistentEntity(entityClass);
Optional<? extends MongoPersistentEntity<?>> entity = entityClass == null ? null
: getPersistentEntity(entityClass);
increaseVersionForUpdateIfNecessary(entity, update);
@@ -1210,23 +1209,23 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
});
}
private void increaseVersionForUpdateIfNecessary(MongoPersistentEntity<?> persistentEntity, Update update) {
private void increaseVersionForUpdateIfNecessary(Optional<? extends MongoPersistentEntity<?>> persistentEntity,
Update update) {
if (persistentEntity != null && persistentEntity.hasVersionProperty()) {
String versionFieldName = persistentEntity.getVersionProperty().getFieldName();
ifAllPresent(persistentEntity, persistentEntity.flatMap(it -> it.getVersionProperty()), (entity, property) -> {
String versionFieldName = property.getFieldName();
if (!update.modifies(versionFieldName)) {
update.inc(versionFieldName, 1L);
}
}
});
}
private boolean documentContainsVersionProperty(Document document, MongoPersistentEntity<?> persistentEntity) {
private boolean documentContainsVersionProperty(Document document,
Optional<? extends MongoPersistentEntity<?>> persistentEntity) {
if (persistentEntity == null || !persistentEntity.hasVersionProperty()) {
return false;
}
return document.containsKey(persistentEntity.getVersionProperty().getFieldName());
return mapIfAllPresent(persistentEntity, persistentEntity.flatMap(it -> it.getVersionProperty()), //
(entity, property) -> document.containsKey(property.getFieldName()))//
.orElse(false);
}
public DeleteResult remove(Object object) {
@@ -1256,25 +1255,21 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @param object
* @return
*/
private Entry<String, Object> extractIdPropertyAndValue(Object object) {
private Pair<String, Optional<Object>> extractIdPropertyAndValue(Object object) {
Assert.notNull(object, "Id cannot be extracted from 'null'.");
Class<?> objectType = object.getClass();
if (object instanceof Document) {
return Collections.singletonMap(ID_FIELD, ((Document) object).get(ID_FIELD)).entrySet().iterator().next();
return Pair.of(ID_FIELD, Optional.ofNullable(((Document) object).get(ID_FIELD)));
}
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(objectType);
MongoPersistentProperty idProp = entity == null ? null : entity.getIdProperty();
Optional<? extends MongoPersistentEntity<?>> entity = mappingContext.getPersistentEntity(objectType);
return mapIfAllPresent(entity, entity.flatMap(it -> it.getIdProperty()), //
if (idProp == null || entity == null) {
throw new MappingException("No id property found for object of type " + objectType);
}
Object idValue = entity.getPropertyAccessor(object).getProperty(idProp);
return Collections.singletonMap(idProp.getFieldName(), idValue).entrySet().iterator().next();
(l, r) -> Pair.of(r.getFieldName(), l.getPropertyAccessor(object).getProperty(r)))//
.orElseThrow(() -> new MappingException("No id property found for object of type " + objectType));
}
/**
@@ -1285,8 +1280,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
*/
private Query getIdQueryFor(Object object) {
Entry<String, Object> id = extractIdPropertyAndValue(object);
return new Query(where(id.getKey()).is(id.getValue()));
Pair<String, Optional<Object>> id = extractIdPropertyAndValue(object);
return new Query(where(id.getFirst()).is(id.getSecond().orElse(null)));
}
/**
@@ -1300,34 +1295,38 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.notEmpty(objects, "Cannot create Query for empty collection.");
Iterator<?> it = objects.iterator();
Entry<String, Object> firstEntry = extractIdPropertyAndValue(it.next());
Pair<String, Optional<Object>> pair = extractIdPropertyAndValue(it.next());
ArrayList<Object> ids = new ArrayList<Object>(objects.size());
ids.add(firstEntry.getValue());
ids.add(pair.getSecond().orElse(null));
while (it.hasNext()) {
ids.add(extractIdPropertyAndValue(it.next()).getValue());
ids.add(extractIdPropertyAndValue(it.next()).getSecond().orElse(null));
}
return new Query(where(firstEntry.getKey()).in(ids));
return new Query(where(pair.getFirst()).in(ids));
}
private void assertUpdateableIdIfNotSet(Object entity) {
private void assertUpdateableIdIfNotSet(Object value) {
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entity.getClass());
MongoPersistentProperty idProperty = persistentEntity == null ? null : persistentEntity.getIdProperty();
Optional<? extends MongoPersistentEntity<?>> persistentEntity = mappingContext
.getPersistentEntity(value.getClass());
Optional<MongoPersistentProperty> idProperty = persistentEntity.flatMap(it -> it.getIdProperty());
if (idProperty == null || persistentEntity == null) {
return;
}
Optionals.ifAllPresent(persistentEntity, idProperty, (entity, property) -> {
Object idValue = persistentEntity.getPropertyAccessor(entity).getProperty(idProperty);
Optional<Object> propertyValue = entity.getPropertyAccessor(value).getProperty(property);
if (idValue == null && !MongoSimpleTypes.AUTOGENERATED_ID_TYPES.contains(idProperty.getType())) {
throw new InvalidDataAccessApiUsageException(
String.format("Cannot autogenerate id of type %s for entity of type %s!", idProperty.getType().getName(),
entity.getClass().getName()));
}
if (propertyValue.isPresent()) {
return;
}
if (!MongoSimpleTypes.AUTOGENERATED_ID_TYPES.contains(property.getType())) {
throw new InvalidDataAccessApiUsageException(
String.format("Cannot autogenerate id of type %s for entity of type %s!", property.getType().getName(),
value.getClass().getName()));
}
});
}
public DeleteResult remove(Query query, String collectionName) {
@@ -1351,7 +1350,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.hasText(collectionName, "Collection name must not be null or empty!");
final Document queryObject = query.getQueryObject();
final MongoPersistentEntity<?> entity = getPersistentEntity(entityClass);
final Optional<? extends MongoPersistentEntity<?>> entity = getPersistentEntity(entityClass);
return execute(collectionName, new CollectionCallback<DeleteResult>() {
public DeleteResult doInCollection(MongoCollection<Document> collection)
@@ -1431,7 +1430,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
result = result.sort(query.getSortObject());
}
result = result.filter(queryMapper.getMappedObject(query.getQueryObject(), null));
result = result.filter(queryMapper.getMappedObject(query.getQueryObject(), Optional.empty()));
}
if (mapReduceOptions != null) {
@@ -1477,7 +1476,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
if (criteria == null) {
document.put("cond", null);
} else {
document.put("cond", queryMapper.getMappedObject(criteria.getCriteriaObject(), null));
document.put("cond", queryMapper.getMappedObject(criteria.getCriteriaObject(), Optional.empty()));
}
// If initial document was a JavaScript string, potentially loaded by Spring's Resource abstraction, load it and
// convert to Document
@@ -1853,7 +1852,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
*/
protected <T> T doFindOne(String collectionName, Document query, Document fields, Class<T> entityClass) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
Optional<? extends MongoPersistentEntity<?>> entity = mappingContext.getPersistentEntity(entityClass);
Document mappedQuery = queryMapper.getMappedObject(query, entity);
Document mappedFields = fields == null ? null : queryMapper.getMappedObject(fields, entity);
@@ -1903,7 +1902,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
protected <S, T> List<T> doFind(String collectionName, Document query, Document fields, Class<S> entityClass,
CursorPreparer preparer, DocumentCallback<T> objectCallback) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
Optional<? extends MongoPersistentEntity<?>> entity = mappingContext.getPersistentEntity(entityClass);
Document mappedFields = queryMapper.getMappedFields(fields, entity);
Document mappedQuery = queryMapper.getMappedObject(query, entity);
@@ -1954,7 +1953,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
serializeToJsonSafely(query), fields, sort, entityClass, collectionName);
}
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
Optional<? extends MongoPersistentEntity<?>> entity = mappingContext.getPersistentEntity(entityClass);
return executeFindOneInternal(new FindAndRemoveCallback(queryMapper.getMappedObject(query, entity), fields, sort),
new ReadDocumentCallback<T>(readerToUse, entityClass, collectionName), collectionName);
@@ -1969,7 +1968,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
options = new FindAndModifyOptions();
}
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
Optional<? extends MongoPersistentEntity<?>> entity = mappingContext.getPersistentEntity(entityClass);
increaseVersionForUpdateIfNecessary(entity, update);
@@ -2005,21 +2004,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return;
}
MongoPersistentProperty idProp = getIdPropertyFor(savedObject.getClass());
getIdPropertyFor(savedObject.getClass()).ifPresent(idProp -> {
if (idProp == null) {
return;
}
ConversionService conversionService = mongoConverter.getConversionService();
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(savedObject.getClass());
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(savedObject);
ConversionService conversionService = mongoConverter.getConversionService();
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(savedObject.getClass());
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(savedObject);
if (accessor.getProperty(idProp) != null) {
return;
}
new ConvertingPropertyAccessor(accessor, conversionService).setProperty(idProp, id);
Optional<Object> value = accessor.getProperty(idProp);
value.ifPresent(it -> new ConvertingPropertyAccessor(accessor, conversionService).setProperty(idProp, value));
});
}
private MongoCollection<Document> getAndPrepareCollection(MongoDatabase db, String collectionName) {
@@ -2150,13 +2143,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return exceptionTranslator;
}
private MongoPersistentEntity<?> getPersistentEntity(Class<?> type) {
return type == null ? null : mappingContext.getPersistentEntity(type);
private Optional<? extends MongoPersistentEntity<?>> getPersistentEntity(Class<?> type) {
return Optional.ofNullable(type).flatMap(it -> mappingContext.getPersistentEntity(it));
}
private MongoPersistentProperty getIdPropertyFor(Class<?> type) {
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(type);
return persistentEntity == null ? null : persistentEntity.getIdProperty();
private Optional<MongoPersistentProperty> getIdPropertyFor(Class<?> type) {
return mappingContext.getPersistentEntity(type).flatMap(it -> it.getIdProperty());
}
private <T> String determineEntityCollectionName(T obj) {
@@ -2174,12 +2166,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
"No class parameter provided, entity collection can't be determined!");
}
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
if (entity == null) {
throw new InvalidDataAccessApiUsageException(
"No Persistent Entity information found for the class " + entityClass.getName());
}
return entity.getCollection();
return mappingContext.getRequiredPersistentEntity(entityClass).getCollection();
}
/**

View File

@@ -24,6 +24,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -43,7 +44,6 @@ import org.springframework.data.convert.ThreeTenBackPortConverters;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.data.util.CacheValue;
import org.springframework.util.Assert;
/**
@@ -71,9 +71,9 @@ public class CustomConversions {
private final List<Object> converters;
private final Map<ConvertiblePair, CacheValue<Class<?>>> customReadTargetTypes;
private final Map<ConvertiblePair, CacheValue<Class<?>>> customWriteTargetTypes;
private final Map<Class<?>, CacheValue<Class<?>>> rawWriteTargetTypes;
private final Map<ConvertiblePair, Optional<Class<?>>> customReadTargetTypes;
private final Map<ConvertiblePair, Optional<Class<?>>> customWriteTargetTypes;
private final Map<Class<?>, Optional<Class<?>>> rawWriteTargetTypes;
/**
* Creates an empty {@link CustomConversions} object.
@@ -91,12 +91,12 @@ public class CustomConversions {
Assert.notNull(converters, "List of converters must not be null!");
this.readingPairs = new LinkedHashSet<ConvertiblePair>();
this.writingPairs = new LinkedHashSet<ConvertiblePair>();
this.customSimpleTypes = new HashSet<Class<?>>();
this.customReadTargetTypes = new ConcurrentHashMap<ConvertiblePair, CacheValue<Class<?>>>();
this.customWriteTargetTypes = new ConcurrentHashMap<ConvertiblePair, CacheValue<Class<?>>>();
this.rawWriteTargetTypes = new ConcurrentHashMap<Class<?>, CacheValue<Class<?>>>();
this.readingPairs = new LinkedHashSet<>();
this.writingPairs = new LinkedHashSet<>();
this.customSimpleTypes = new HashSet<>();
this.customReadTargetTypes = new ConcurrentHashMap<>();
this.customWriteTargetTypes = new ConcurrentHashMap<>();
this.rawWriteTargetTypes = new ConcurrentHashMap<>();
List<Object> toRegister = new ArrayList<Object>();
@@ -241,13 +241,8 @@ public class CustomConversions {
*/
public Class<?> getCustomWriteTarget(final Class<?> sourceType) {
return getOrCreateAndCache(sourceType, rawWriteTargetTypes, new Producer() {
@Override
public Class<?> get() {
return getCustomTarget(sourceType, null, writingPairs);
}
});
return rawWriteTargetTypes.computeIfAbsent(sourceType, it -> getCustomTarget(sourceType, null, writingPairs))
.orElse(null);
}
/**
@@ -265,14 +260,8 @@ public class CustomConversions {
return getCustomWriteTarget(sourceType);
}
return getOrCreateAndCache(new ConvertiblePair(sourceType, requestedTargetType), customWriteTargetTypes,
new Producer() {
@Override
public Class<?> get() {
return getCustomTarget(sourceType, requestedTargetType, writingPairs);
}
});
return customWriteTargetTypes.computeIfAbsent(new ConvertiblePair(sourceType, requestedTargetType),
it -> getCustomTarget(sourceType, requestedTargetType, writingPairs)).orElse(null);
}
/**
@@ -324,14 +313,8 @@ public class CustomConversions {
return null;
}
return getOrCreateAndCache(new ConvertiblePair(sourceType, requestedTargetType), customReadTargetTypes,
new Producer() {
@Override
public Class<?> get() {
return getCustomTarget(sourceType, requestedTargetType, readingPairs);
}
});
return customReadTargetTypes.computeIfAbsent(new ConvertiblePair(sourceType, requestedTargetType),
it -> getCustomTarget(sourceType, requestedTargetType, readingPairs)).orElse(null);
}
/**
@@ -343,54 +326,26 @@ public class CustomConversions {
* @param pairs must not be {@literal null}.
* @return
*/
private static Class<?> getCustomTarget(Class<?> sourceType, Class<?> requestedTargetType,
private static Optional<Class<?>> getCustomTarget(Class<?> sourceType, Class<?> requestedTargetType,
Collection<ConvertiblePair> pairs) {
Assert.notNull(sourceType, "Source Class must not be null!");
Assert.notNull(pairs, "Collection of ConvertiblePair must not be null!");
if (requestedTargetType != null && pairs.contains(new ConvertiblePair(sourceType, requestedTargetType))) {
return requestedTargetType;
return Optional.of(requestedTargetType);
}
for (ConvertiblePair typePair : pairs) {
if (typePair.getSourceType().isAssignableFrom(sourceType)) {
Class<?> targetType = typePair.getTargetType();
if (requestedTargetType == null || targetType.isAssignableFrom(requestedTargetType)) {
return targetType;
return Optional.of(targetType);
}
}
}
return null;
}
/**
* Will try to find a value for the given key in the given cache or produce one using the given {@link Producer} and
* store it in the cache.
*
* @param key the key to lookup a potentially existing value, must not be {@literal null}.
* @param cache the cache to find the value in, must not be {@literal null}.
* @param producer the {@link Producer} to create values to cache, must not be {@literal null}.
* @return
*/
private static <T> Class<?> getOrCreateAndCache(T key, Map<T, CacheValue<Class<?>>> cache, Producer producer) {
CacheValue<Class<?>> cacheValue = cache.get(key);
if (cacheValue != null) {
return cacheValue.getValue();
}
Class<?> type = producer.get();
cache.put(key, CacheValue.<Class<?>> ofNullable(type));
return type;
}
private interface Producer {
Class<?> get();
return Optional.empty();
}
@WritingConverter

View File

@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core.convert;
import org.bson.Document;
import java.util.List;
import java.util.Optional;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
@@ -45,7 +46,7 @@ public interface DbRefResolver {
* @param callback will never be {@literal null}.
* @return
*/
Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
Optional<Object> resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
DbRefProxyHandler proxyHandler);
/**

View File

@@ -61,8 +61,9 @@ class DefaultDbRefProxyHandler implements DbRefProxyHandler {
return proxy;
}
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(property);
MongoPersistentProperty idProperty = entity.getIdProperty();
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(property);
MongoPersistentProperty idProperty = entity.getIdProperty()
.orElseThrow(() -> new IllegalStateException("Couldn't find identifier property!"));
if (idProperty.usePropertyAccess()) {
return proxy;

View File

@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@@ -84,17 +85,17 @@ public class DefaultDbRefResolver implements DbRefResolver {
* @see org.springframework.data.mongodb.core.convert.DbRefResolver#resolveDbRef(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, org.springframework.data.mongodb.core.convert.DbRefResolverCallback)
*/
@Override
public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
public Optional<Object> resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback,
DbRefProxyHandler handler) {
Assert.notNull(property, "Property must not be null!");
Assert.notNull(callback, "Callback must not be null!");
if (isLazyDbRef(property)) {
return createLazyLoadingProxy(property, dbref, callback, handler);
return Optional.of(createLazyLoadingProxy(property, dbref, callback, handler));
}
return callback.resolve(property);
return Optional.ofNullable(callback.resolve(property));
}
/*

View File

@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core.convert;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.bson.Document;
@@ -26,6 +27,7 @@ import org.springframework.data.convert.DefaultTypeMapper;
import org.springframework.data.convert.SimpleTypeInformationMapper;
import org.springframework.data.convert.TypeAliasAccessor;
import org.springframework.data.convert.TypeInformationMapper;
import org.springframework.data.mapping.Alias;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.ClassTypeInformation;
@@ -116,13 +118,13 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<Bson> implements M
accessor.writeTypeTo(result, new Document("$in", restrictedMappedTypes));
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.convert.DefaultTypeMapper#getFallbackTypeFor(java.lang.Object)
*/
@Override
protected TypeInformation<?> getFallbackTypeFor(Bson source) {
return source instanceof BasicDBList ? LIST_TYPE_INFO : MAP_TYPE_INFO;
protected Optional<TypeInformation<?>> getFallbackTypeFor(Bson source) {
return Optional.of(source instanceof BasicDBList ? LIST_TYPE_INFO : MAP_TYPE_INFO);
}
/**
@@ -142,16 +144,16 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<Bson> implements M
* (non-Javadoc)
* @see org.springframework.data.convert.TypeAliasAccessor#readAliasFrom(java.lang.Object)
*/
public Object readAliasFrom(Bson source) {
public Alias readAliasFrom(Bson source) {
if (source instanceof List) {
return null;
return Alias.NONE;
}
if (source instanceof Document) {
return ((Document) source).get(typeKey);
return Alias.ofOptional(Optional.ofNullable(((Document) source).get(typeKey)));
} else if (source instanceof DBObject) {
return ((DBObject) source).get(typeKey);
return Alias.ofOptional(Optional.ofNullable(((DBObject) source).get(typeKey)));
}
throw new IllegalArgumentException("Cannot read alias from " + source.getClass());

View File

@@ -18,6 +18,8 @@ package org.springframework.data.mongodb.core.convert;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import org.bson.Document;
import org.bson.conversions.Bson;
@@ -89,6 +91,15 @@ class DocumentAccessor {
}
}
public void computeIfAbsent(MongoPersistentProperty prop, Supplier<Optional<Object>> supplier) {
if (hasValue(prop)) {
return;
}
supplier.get().ifPresent(it -> put(prop, it));
}
/**
* Returns the value the given {@link MongoPersistentProperty} refers to. By default this will be a direct field but
* the method will also transparently resolve nested values the {@link MongoPersistentProperty} might refer to through

View File

@@ -24,6 +24,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import org.bson.Document;
@@ -34,7 +35,6 @@ import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.convert.EntityInstantiator;
@@ -224,7 +224,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
if (typeToUse.isCollectionLike() && bson instanceof List) {
return (S) readCollectionOrArray(typeToUse, (List) bson, path);
return (S) readCollectionOrArray(typeToUse, (List<?>) bson, path);
}
if (typeToUse.isMap()) {
@@ -239,13 +239,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return (S) bson;
}
// Retrieve persistent entity info
MongoPersistentEntity<S> persistentEntity = (MongoPersistentEntity<S>) mappingContext
.getPersistentEntity(typeToUse);
if (persistentEntity == null) {
throw new MappingException("No mapping metadata found for " + rawType.getName());
}
return read(persistentEntity, bson, path);
return read((MongoPersistentEntity<S>) mappingContext.getRequiredPersistentEntity(typeToUse), (Document) bson,
path);
}
private ParameterValueProvider<MongoPersistentProperty> getParameterProvider(MongoPersistentEntity<?> entity,
@@ -259,7 +255,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
path);
}
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final Bson bson, final ObjectPath path) {
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final Document bson, final ObjectPath path) {
final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(bson, spELContext);
@@ -270,19 +266,21 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
final PersistentPropertyAccessor accessor = new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance),
conversionService);
final MongoPersistentProperty idProperty = entity.getIdProperty();
final Optional<MongoPersistentProperty> idProperty = entity.getIdProperty();
final S result = instance;
// make sure id property is set before all other properties
Object idValue = null;
DocumentAccessor documentAccessor = new DocumentAccessor(bson);
if (idProperty != null && documentAccessor.hasValue(idProperty)) {
idValue = getValueInternal(idProperty, bson, evaluator, path);
accessor.setProperty(idProperty, idValue);
}
// make sure id property is set before all other properties
Object idValue = idProperty.filter(it -> documentAccessor.hasValue(it)).map(it -> {
final ObjectPath currentPath = path.push(result, entity, idValue != null ? documentAccessor.get(idProperty) : null);
Optional<Object> value = getValueInternal(it, bson, evaluator, path);
accessor.setProperty(it, value);
return value;
});
final ObjectPath currentPath = path.push(result, entity,
idValue != null ? idProperty.map(it -> bson.get(it.getFieldName())).orElse(null) : null);
// Set properties not already set in the constructor
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
@@ -364,7 +362,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Object target = obj instanceof LazyLoadingProxy ? ((LazyLoadingProxy) obj).getTarget() : obj;
writeInternal(target, bson, type);
writeInternal(target, bson, Optional.of(type));
if (asMap(bson).containsKey("_is") && asMap(bson).get("_id") == null) {
removeFromMap(bson, "_id");
}
@@ -382,7 +380,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @param bson
*/
@SuppressWarnings("unchecked")
protected void writeInternal(final Object obj, final Bson bson, final TypeInformation<?> typeHint) {
protected void writeInternal(final Object obj, final Bson bson, final Optional<TypeInformation<?>> typeHint) {
if (null == obj) {
return;
@@ -403,11 +401,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
if (Collection.class.isAssignableFrom(entityType)) {
writeCollectionInternal((Collection<?>) obj, ClassTypeInformation.LIST, (List) bson);
writeCollectionInternal((Collection<?>) obj, Optional.of(ClassTypeInformation.LIST), (BasicDBList) bson);
return;
}
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityType);
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(entityType);
writeInternal(obj, bson, entity);
addCustomTypeKeyIfNecessary(typeHint, obj, bson);
}
@@ -423,34 +421,28 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
final PersistentPropertyAccessor accessor = entity.getPropertyAccessor(obj);
final MongoPersistentProperty idProperty = entity.getIdProperty();
DocumentAccessor dbObjectAccessor = new DocumentAccessor(bson);
if (!asMap(bson).containsKey("_id") && null != idProperty) {
try {
Object id = accessor.getProperty(idProperty);
addToMap(bson, "_id", idMapper.convertId(id));
} catch (ConversionException ignored) {}
}
Optional<MongoPersistentProperty> idProperty = entity.getIdProperty();
idProperty.ifPresent(
prop -> dbObjectAccessor.computeIfAbsent(prop, () -> idMapper.convertId(accessor.getProperty(prop))));
// Write the properties
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
public void doWithPersistentProperty(MongoPersistentProperty prop) {
if (prop.equals(idProperty) || !prop.isWritable()) {
if (idProperty.map(it -> it.equals(prop)).orElse(false) || !prop.isWritable()) {
return;
}
Object propertyObj = accessor.getProperty(prop);
accessor.getProperty(prop).ifPresent(it -> {
if (!conversions.isSimpleType(it.getClass())) {
if (null != propertyObj) {
if (!conversions.isSimpleType(propertyObj.getClass())) {
writePropertyInternal(propertyObj, bson, prop);
writePropertyInternal(it, bson, prop);
} else {
writeSimpleInternal(propertyObj, bson, prop);
writeSimpleInternal(it, bson, prop);
}
}
});
}
});
@@ -459,11 +451,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
public void doWithAssociation(Association<MongoPersistentProperty> association) {
MongoPersistentProperty inverseProp = association.getInverse();
Object propertyObj = accessor.getProperty(inverseProp);
if (null != propertyObj) {
writePropertyInternal(propertyObj, bson, inverseProp);
}
accessor.getProperty(inverseProp).ifPresent(it -> writePropertyInternal(it, bson, inverseProp));
}
});
}
@@ -481,7 +469,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
TypeInformation<?> type = prop.getTypeInformation();
if (valueType.isCollectionLike()) {
List collectionInternal = createCollection(asCollection(obj), prop);
List<Object> collectionInternal = createCollection(asCollection(obj), prop);
accessor.put(prop, collectionInternal);
return;
}
@@ -531,10 +519,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Document document = existingValue instanceof Document ? (Document) existingValue : new Document();
MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass())
? mappingContext.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type);
? mappingContext.getRequiredPersistentEntity(obj.getClass()) : mappingContext.getRequiredPersistentEntity(type);
writeInternal(obj, document, entity);
addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, document);
addCustomTypeKeyIfNecessary(Optional.of(ClassTypeInformation.from(prop.getRawType())), obj, document);
accessor.put(prop, document);
}
@@ -569,7 +557,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
protected List<Object> createCollection(Collection<?> collection, MongoPersistentProperty property) {
if (!property.isDbReference()) {
return writeCollectionInternal(collection, property.getTypeInformation(), new ArrayList());
return writeCollectionInternal(collection, Optional.of(property.getTypeInformation()), new BasicDBList());
}
List<Object> dbList = new ArrayList<Object>();
@@ -631,9 +619,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @param sink the {@link BasicDBList} to write to.
* @return
*/
private List writeCollectionInternal(Collection<?> source, TypeInformation<?> type, List sink) {
private BasicDBList writeCollectionInternal(Collection<?> source, Optional<TypeInformation<?>> type,
BasicDBList sink) {
TypeInformation<?> componentType = type == null ? null : type.getComponentType();
Optional<TypeInformation<?>> componentType = type.flatMap(it -> it.getComponentType());
for (Object element : source) {
@@ -678,8 +667,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
writeCollectionInternal(asCollection(val), propertyType.getMapValueType(), new BasicDBList()));
} else {
Document document = new Document();
TypeInformation<?> valueTypeInfo = propertyType.isMap() ? propertyType.getMapValueType()
: ClassTypeInformation.OBJECT;
Optional<TypeInformation<?>> valueTypeInfo = propertyType.isMap() ? propertyType.getMapValueType()
: Optional.of(ClassTypeInformation.OBJECT);
writeInternal(val, document, valueTypeInfo);
addToMap(bson, simpleKey, document);
}
@@ -765,10 +754,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @param value must not be {@literal null}.
* @param bson must not be {@literal null}.
*/
protected void addCustomTypeKeyIfNecessary(TypeInformation<?> type, Object value, Bson bson) {
protected void addCustomTypeKeyIfNecessary(Optional<TypeInformation<?>> type, Object value, Bson bson) {
TypeInformation<?> actualType = type != null ? type.getActualType() : null;
Class<?> reference = actualType == null ? Object.class : actualType.getType();
Optional<Class<?>> actualType = type.map(it -> it.getActualType()).map(it -> it.getType());
Class<?> reference = actualType.orElse(Object.class);
Class<?> valueType = ClassUtils.getUserClass(value.getClass());
boolean notTheSameClass = !valueType.equals(reference);
@@ -857,34 +846,30 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return (DBRef) target;
}
MongoPersistentEntity<?> targetEntity = mappingContext.getPersistentEntity(target.getClass());
targetEntity = targetEntity == null ? targetEntity = mappingContext.getPersistentEntity(property) : targetEntity;
Optional<? extends MongoPersistentEntity<?>> targetEntity = mappingContext.getPersistentEntity(target.getClass());
targetEntity = targetEntity.isPresent() ? targetEntity : mappingContext.getPersistentEntity(property);
if (null == targetEntity) {
throw new MappingException("No mapping metadata found for " + target.getClass());
}
MongoPersistentProperty idProperty = targetEntity.getIdProperty();
MongoPersistentEntity<?> entity = targetEntity
.orElseThrow(() -> new MappingException("No mapping metadata found for " + target.getClass()));
if (idProperty == null) {
throw new MappingException("No id property found on class " + targetEntity.getType());
}
Optional<MongoPersistentProperty> idProperty = entity.getIdProperty();
Object id = null;
return idProperty.map(it -> {
if (target.getClass().equals(idProperty.getType())) {
id = target;
} else {
PersistentPropertyAccessor accessor = targetEntity.getPropertyAccessor(target);
id = accessor.getProperty(idProperty);
}
Object id = target.getClass().equals(it.getType()) ? target : entity.getPropertyAccessor(target).getProperty(it);
if (null == id) {
throw new MappingException("Cannot create a reference to an object with a NULL id.");
}
if (null == id) {
throw new MappingException("Cannot create a reference to an object with a NULL id.");
}
return dbRefResolver.createDbRef(property == null ? null : property.getDBRef(), targetEntity,
idMapper.convertId(id));
return dbRefResolver.createDbRef(property == null ? null : property.getDBRef(), entity,
idMapper.convertId(Optional.of(id)));
}).orElseThrow(() -> new MappingException("No id property found on class " + entity.getType()));
}
/*
@@ -892,7 +877,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @see org.springframework.data.mongodb.core.convert.ValueResolver#getValueInternal(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, com.mongodb.Document, org.springframework.data.mapping.model.SpELExpressionEvaluator, java.lang.Object)
*/
@Override
public Object getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator,
public Optional<Object> getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator,
ObjectPath path) {
return new MongoDbPropertyValueProvider(bson, evaluator, path).getPropertyValue(prop);
}
@@ -913,8 +898,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Class<?> collectionType = targetType.getType();
TypeInformation<?> componentType = targetType.getComponentType();
Class<?> rawComponentType = componentType == null ? null : componentType.getType();
TypeInformation<?> componentType = targetType.getComponentType().orElse(ClassTypeInformation.OBJECT);
Class<?> rawComponentType = componentType.getType();
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>()
@@ -973,21 +958,20 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Class<?> mapType = typeMapper.readType(bson, type).getType();
TypeInformation<?> keyType = type.getComponentType();
Class<?> rawKeyType = keyType == null ? null : keyType.getType();
TypeInformation<?> valueType = type.getMapValueType();
Class<?> rawValueType = valueType == null ? null : valueType.getType();
Optional<TypeInformation<?>> valueType = type.getMapValueType();
Class<?> rawKeyType = type.getComponentType().map(it -> it.getType()).orElse(null);
Class<?> rawValueType = type.getMapValueType().map(it -> it.getType()).orElse(null);
Map<String, Object> sourceMap = asMap(bson);
Map<Object, Object> map = CollectionFactory.createMap(mapType, rawKeyType, sourceMap.keySet().size());
if (!DBRef.class.equals(rawValueType) && isCollectionOfDbRefWhereBulkFetchIsPossible(sourceMap.values())) {
bulkReadAndConvertDBRefMapIntoTarget(valueType, rawValueType, sourceMap, map);
bulkReadAndConvertDBRefMapIntoTarget(valueType.orElse(null), rawValueType, sourceMap, map);
return map;
}
for (Entry<String, Object> entry : sourceMap.entrySet()) {
if (typeMapper.isTypeKey(entry.getKey())) {
continue;
}
@@ -999,19 +983,19 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
Object value = entry.getValue();
TypeInformation<?> defaultedValueType = valueType.orElse(ClassTypeInformation.OBJECT);
if (value instanceof Document) {
map.put(key, read(valueType, (Document) value, path));
map.put(key, read(defaultedValueType, (Document) value, path));
} else if (value instanceof BasicDBObject) {
map.put(key, read(valueType, (BasicDBObject) value, path));
map.put(key, read(defaultedValueType, (BasicDBObject) value, path));
} else if (value instanceof DBRef) {
map.put(key, DBRef.class.equals(rawValueType) ? value
: readAndConvertDBRef((DBRef) value, valueType, ObjectPath.ROOT, rawValueType));
: readAndConvertDBRef((DBRef) value, defaultedValueType, ObjectPath.ROOT, rawValueType));
} else if (value instanceof List) {
map.put(key, readCollectionOrArray(valueType, (List) value, path));
map.put(key, readCollectionOrArray(valueType.orElse(ClassTypeInformation.LIST), (List) value, path));
} else {
Class<?> valueClass = valueType == null ? null : valueType.getType();
map.put(key, getPotentiallyConvertedSimpleRead(value, valueClass));
map.put(key, getPotentiallyConvertedSimpleRead(value, rawValueType));
}
}
@@ -1043,9 +1027,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
((DBObject) bson).put(key, value);
return;
}
throw new IllegalArgumentException(
String.format("Cannot add key/value pair to %s. as map. Given Bson must be a Document or DBObject!",
bson.getClass()));
throw new IllegalArgumentException(String.format(
"Cannot add key/value pair to %s. as map. Given Bson must be a Document or DBObject!", bson.getClass()));
}
@SuppressWarnings("unchecked")
@@ -1061,8 +1044,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return;
}
throw new IllegalArgumentException(String.format(
"Cannot add all to %s. Given Bson must be a Document or DBObject.", bson.getClass()));
throw new IllegalArgumentException(
String.format("Cannot add all to %s. Given Bson must be a Document or DBObject.", bson.getClass()));
}
private void removeFromMap(Bson bson, String key) {
@@ -1105,10 +1088,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
TypeInformation<?> typeHint = typeInformation;
if (obj instanceof List) {
return maybeConvertList((List) obj, typeHint);
return maybeConvertList((List<Object>) obj, typeHint);
}
if (obj instanceof Document) {
Document newValueDocument = new Document();
for (String vk : ((Document) obj).keySet()) {
Object o = ((Document) obj).get(vk);
@@ -1118,6 +1102,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
if (obj instanceof DBObject) {
Document newValueDbo = new Document();
for (String vk : ((DBObject) obj).keySet()) {
@@ -1130,7 +1115,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (obj instanceof Map) {
Map<Object, Object> converted = new LinkedHashMap<Object, Object>();
Document result = new Document();
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) obj).entrySet()) {
result.put(entry.getKey().toString(), convertToMongoType(entry.getValue(), typeHint));
}
@@ -1160,9 +1147,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return !obj.getClass().equals(typeInformation.getType()) ? newDocument : removeTypeInfo(newDocument, true);
}
public List maybeConvertList(Iterable<?> source, TypeInformation<?> typeInformation) {
public List<Object> maybeConvertList(Iterable<?> source, TypeInformation<?> typeInformation) {
List<Object> newDbl = new ArrayList<>();
List newDbl = new ArrayList();
for (Object element : source) {
newDbl.add(convertToMongoType(element, typeInformation));
}
@@ -1244,8 +1232,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
*/
public MongoDbPropertyValueProvider(Bson source, SpELExpressionEvaluator evaluator, ObjectPath path) {
Assert.notNull(source, "Bson source must not be null!");
Assert.notNull(source, "Source document must no be null!");
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null!");
Assert.notNull(path, "ObjectPath must not be null!");
this.source = new DocumentAccessor(source);
this.evaluator = evaluator;
@@ -1256,16 +1245,13 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* (non-Javadoc)
* @see org.springframework.data.convert.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
public <T> T getPropertyValue(MongoPersistentProperty property) {
public <T> Optional<T> getPropertyValue(MongoPersistentProperty property) {
return Optional
String expression = property.getSpelExpression();
Object value = expression != null ? evaluator.evaluate(expression) : source.get(property);
if (value == null) {
return null;
}
return readValue(value, property.getTypeInformation(), path);
.ofNullable(property.getSpelExpression()//
.map(it -> evaluator.evaluate(it))//
.orElseGet(() -> source.get(property)))//
.map(it -> readValue(it, property.getTypeInformation(), path));
}
}

View File

@@ -40,6 +40,7 @@ import org.springframework.data.mongodb.core.query.MongoRegexCreator;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.data.repository.core.support.ExampleMatcherAccessor;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.util.Optionals;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -79,7 +80,7 @@ public class MongoExampleMapper {
Assert.notNull(example, "Example must not be null!");
return getMappedExample(example, mappingContext.getPersistentEntity(example.getProbeType()));
return getMappedExample(example, mappingContext.getRequiredPersistentEntity(example.getProbeType()));
}
/**
@@ -90,7 +91,6 @@ public class MongoExampleMapper {
* @param entity must not be {@literal null}.
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Document getMappedExample(Example<?> example, MongoPersistentEntity<?> entity) {
Assert.notNull(example, "Example must not be null!");
@@ -98,9 +98,9 @@ public class MongoExampleMapper {
Document reference = (Document) converter.convertToMongoType(example.getProbe());
if (entity.hasIdProperty() && entity.getIdentifierAccessor(example.getProbe()).getIdentifier() == null) {
reference.remove(entity.getIdProperty().getFieldName());
}
Optionals.ifAllPresent(entity.getIdProperty(), //
entity.getIdentifierAccessor(example.getProbe()).getIdentifier(), //
(property, identifier) -> reference.remove(property.getFieldName()));
ExampleMatcherAccessor matcherAccessor = new ExampleMatcherAccessor(example.getMatcher());
@@ -141,7 +141,7 @@ public class MongoExampleMapper {
private String getMappedPropertyPath(String path, Class<?> probeType) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(probeType);
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(probeType);
Iterator<String> parts = Arrays.asList(path.split("\\.")).iterator();
@@ -151,39 +151,34 @@ public class MongoExampleMapper {
while (parts.hasNext()) {
final String part = parts.next();
MongoPersistentProperty prop = entity.getPersistentProperty(part);
String part = parts.next();
MongoPersistentProperty prop = entity.getPersistentProperty(part).orElse(null);
if (prop == null) {
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
@Override
public void doWithPersistentProperty(MongoPersistentProperty property) {
if (property.getFieldName().equals(part)) {
stack.push(property);
}
entity.doWithProperties((PropertyHandler<MongoPersistentProperty>) property -> {
if (property.getFieldName().equals(part)) {
stack.push(property);
}
});
if (stack.isEmpty()) {
return "";
}
prop = stack.pop();
}
resultParts.add(prop.getName());
if (prop.isEntity() && mappingContext.hasPersistentEntityFor(prop.getActualType())) {
entity = mappingContext.getPersistentEntity(prop.getActualType());
entity = mappingContext.getRequiredPersistentEntity(prop.getActualType());
} else {
break;
}
}
return StringUtils.collectionToDelimitedString(resultParts, ".");
}
private void applyPropertySpecs(String path, Document source, Class<?> probeType,

View File

@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core.convert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.util.Assert;
@@ -115,8 +116,8 @@ class ObjectPath {
*
* @return
*/
public Object getCurrentObject() {
return items.isEmpty() ? null : items.get(items.size() - 1).getObject();
public Optional<Object> getCurrentObject() {
return items.isEmpty() ? Optional.empty() : Optional.of(items.get(items.size() - 1).getObject());
}
/*

View File

@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import org.bson.BsonValue;
@@ -94,6 +95,10 @@ public class QueryMapper {
this.exampleMapper = new MongoExampleMapper(converter);
}
public Document getMappedObject(Bson query, Optional<? extends MongoPersistentEntity<?>> entity) {
return getMappedObject(query, entity.orElse(null));
}
/**
* Replaces the property keys used in the given {@link Document} with the appropriate keys by using the
* {@link PersistentEntity} metadata.
@@ -167,6 +172,10 @@ public class QueryMapper {
return mappedSort;
}
public Document getMappedSort(Document sortObject, Optional<? extends MongoPersistentEntity<?>> entity) {
return getMappedSort(sortObject, entity.orElse(null));
}
/**
* Maps fields to retrieve to the {@link MongoPersistentEntity}s properties. <br />
* Also onverts and potentially adds missing property {@code $meta} representation.
@@ -183,6 +192,10 @@ public class QueryMapper {
return mappedFields.keySet().isEmpty() ? null : mappedFields;
}
public Document getMappedFields(Document fieldsObject, Optional<? extends MongoPersistentEntity<?>> entity) {
return getMappedFields(fieldsObject, entity.orElse(null));
}
private void mapMetaAttributes(Document source, MongoPersistentEntity<?> entity, MetaMapping metaMapping) {
if (entity == null || source == null) {
@@ -311,7 +324,7 @@ public class QueryMapper {
} else if (valueDbo.containsField("$ne")) {
resultDbo.put("$ne", convertId(valueDbo.get("$ne")));
} else {
return getMappedObject(resultDbo, null);
return getMappedObject(resultDbo, Optional.empty());
}
return resultDbo;
}
@@ -330,7 +343,7 @@ public class QueryMapper {
} else if (valueDbo.containsKey("$ne")) {
resultDbo.put("$ne", convertId(valueDbo.get("$ne")));
} else {
return getMappedObject(resultDbo, null);
return getMappedObject(resultDbo, Optional.empty());
}
return resultDbo;
@@ -380,8 +393,8 @@ public class QueryMapper {
}
MongoPersistentEntity<?> entity = documentField.getPropertyEntity();
return entity.hasIdProperty()
&& (type.equals(DBRef.class) || entity.getIdProperty().getActualType().isAssignableFrom(type));
return entity.hasIdProperty() && (type.equals(DBRef.class)
|| entity.getIdProperty().map(it -> it.getActualType().isAssignableFrom(type)).orElse(false));
}
/**
@@ -524,28 +537,31 @@ public class QueryMapper {
return converter.toDBRef(source, property);
}
private Optional<Object> convertId(Object id) {
return convertId(Optional.of(id));
}
/**
* Converts the given raw id value into either {@link ObjectId} or {@link String}.
*
* @param id
* @return
*/
public Object convertId(Object id) {
public Optional<Object> convertId(Optional<Object> id) {
if (id == null) {
return null;
}
return id.map(it -> {
if (id instanceof String) {
return ObjectId.isValid(id.toString()) ? conversionService.convert(id, ObjectId.class) : id;
}
if (it instanceof String) {
return ObjectId.isValid(it.toString()) ? conversionService.convert(it, ObjectId.class) : it;
}
try {
return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService.convert(id, ObjectId.class)
: delegateConvertToMongoType(id, null);
} catch (ConversionException o_O) {
return delegateConvertToMongoType(id, null);
}
try {
return conversionService.canConvert(it.getClass(), ObjectId.class)
? conversionService.convert(it, ObjectId.class) : delegateConvertToMongoType(it, null);
} catch (ConversionException o_O) {
return delegateConvertToMongoType(it, null);
}
});
}
/**
@@ -820,13 +836,9 @@ public class QueryMapper {
@Override
public boolean isIdField() {
MongoPersistentProperty idProperty = entity.getIdProperty();
if (idProperty != null) {
return idProperty.getName().equals(name) || idProperty.getFieldName().equals(name);
}
return DEFAULT_ID_NAMES.contains(name);
return entity.getIdProperty()//
.map(it -> it.getName().equals(name) || it.getFieldName().equals(name))//
.orElseGet(() -> DEFAULT_ID_NAMES.contains(name));
}
/*
@@ -845,7 +857,7 @@ public class QueryMapper {
@Override
public MongoPersistentEntity<?> getPropertyEntity() {
MongoPersistentProperty property = getProperty();
return property == null ? null : mappingContext.getPersistentEntity(property);
return property == null ? null : mappingContext.getRequiredPersistentEntity(property);
}
/*
@@ -875,8 +887,11 @@ public class QueryMapper {
if (this.path != null) {
for (MongoPersistentProperty p : this.path) {
if (p.isAssociation()) {
return p.getAssociation();
Optional<Association<MongoPersistentProperty>> association = p.getAssociation();
if (association.isPresent()) {
return association.get();
}
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core.convert;
import java.util.Optional;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
@@ -38,5 +40,6 @@ interface ValueResolver {
* @param parent
* @return
*/
Object getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator, ObjectPath parent);
Optional<Object> getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator,
ObjectPath path);
}

View File

@@ -20,6 +20,7 @@ import java.lang.reflect.Modifier;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
@@ -53,8 +54,8 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @author Mark Paluch
*/
public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, MongoPersistentProperty> implements
MongoPersistentEntity<T>, ApplicationContextAware {
public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, MongoPersistentProperty>
implements MongoPersistentEntity<T>, ApplicationContextAware {
private static final String AMBIGUOUS_FIELD_MAPPING = "Ambiguous field mapping detected! Both %s and %s map to the same field name %s! Disambiguate using @Field annotation!";
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
@@ -73,24 +74,18 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
*/
public BasicMongoPersistentEntity(TypeInformation<T> typeInformation) {
super(typeInformation, MongoPersistentPropertyComparator.INSTANCE);
super(typeInformation, Optional.of(MongoPersistentPropertyComparator.INSTANCE));
Class<?> rawType = typeInformation.getType();
String fallback = MongoCollectionUtils.getPreferredCollectionName(rawType);
Document document = this.findAnnotation(Document.class);
Optional<Document> document = this.findAnnotation(Document.class);
this.expression = detectExpression(document);
this.expression = document.map(it -> detectExpression(it)).orElse(null);
this.context = new StandardEvaluationContext();
if (document != null) {
this.collection = StringUtils.hasText(document.collection()) ? document.collection() : fallback;
this.language = StringUtils.hasText(document.language()) ? document.language() : "";
} else {
this.collection = fallback;
this.language = "";
}
this.collection = document.filter(it -> StringUtils.hasText(it.collection())).map(it -> it.collection())
.orElse(fallback);
this.language = document.filter(it -> StringUtils.hasText(it.language())).map(it -> it.language()).orElse("");
}
/*
@@ -127,7 +122,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
*/
@Override
public MongoPersistentProperty getTextScoreProperty() {
return getPersistentProperty(TextScore.class);
return getPersistentProperty(TextScore.class).orElse(null);
}
/*
@@ -207,40 +202,38 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
return null;
}
MongoPersistentProperty currentIdProperty = getIdProperty();
Optional<MongoPersistentProperty> currentIdProperty = getIdProperty();
boolean currentIdPropertyIsSet = currentIdProperty != null;
@SuppressWarnings("null")
boolean currentIdPropertyIsExplicit = currentIdPropertyIsSet ? currentIdProperty.isExplicitIdProperty() : false;
boolean newIdPropertyIsExplicit = property.isExplicitIdProperty();
return currentIdProperty.map(it -> {
if (!currentIdPropertyIsSet) {
return property;
boolean currentIdPropertyIsExplicit = it.isExplicitIdProperty();
boolean newIdPropertyIsExplicit = property.isExplicitIdProperty();
Optional<Field> currentIdPropertyField = it.getField();
}
if (newIdPropertyIsExplicit && currentIdPropertyIsExplicit) {
throw new MappingException(
String.format(
"Attempt to add explicit id property %s but already have an property %s registered "
+ "as explicit id. Check your mapping configuration!",
property.getField(), currentIdPropertyField));
@SuppressWarnings("null")
Field currentIdPropertyField = currentIdProperty.getField();
} else if (newIdPropertyIsExplicit && !currentIdPropertyIsExplicit) {
// explicit id property takes precedence over implicit id property
return property;
if (newIdPropertyIsExplicit && currentIdPropertyIsExplicit) {
throw new MappingException(String.format(
"Attempt to add explicit id property %s but already have an property %s registered "
+ "as explicit id. Check your mapping configuration!", property.getField(), currentIdPropertyField));
} else if (!newIdPropertyIsExplicit && currentIdPropertyIsExplicit) {
// no id property override - current property is explicitly defined
} else if (newIdPropertyIsExplicit && !currentIdPropertyIsExplicit) {
// explicit id property takes precedence over implicit id property
return property;
} else {
throw new MappingException(
String.format("Attempt to add id property %s but already have an property %s registered "
+ "as id. Check your mapping configuration!", property.getField(), currentIdPropertyField));
}
} else if (!newIdPropertyIsExplicit && currentIdPropertyIsExplicit) {
// no id property override - current property is explicitly defined
return null;
} else {
throw new MappingException(String.format(
"Attempt to add id property %s but already have an property %s registered "
+ "as id. Check your mapping configuration!", property.getField(), currentIdPropertyField));
}
}).orElse(property);
return null;
}
/**
@@ -274,8 +267,8 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
*
* @author Oliver Gierke
*/
private static class AssertFieldNameUniquenessHandler implements PropertyHandler<MongoPersistentProperty>,
AssociationHandler<MongoPersistentProperty> {
private static class AssertFieldNameUniquenessHandler
implements PropertyHandler<MongoPersistentProperty>, AssociationHandler<MongoPersistentProperty> {
private final Map<String, MongoPersistentProperty> properties = new HashMap<String, MongoPersistentProperty>();
@@ -293,8 +286,8 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
MongoPersistentProperty existingProperty = properties.get(fieldName);
if (existingProperty != null) {
throw new MappingException(String.format(AMBIGUOUS_FIELD_MAPPING, property.toString(),
existingProperty.toString(), fieldName));
throw new MappingException(
String.format(AMBIGUOUS_FIELD_MAPPING, property.toString(), existingProperty.toString(), fieldName));
}
properties.put(fieldName, property);

View File

@@ -15,10 +15,9 @@
*/
package org.springframework.data.mongodb.core.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import org.bson.types.ObjectId;
@@ -29,6 +28,7 @@ import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.util.StringUtils;
@@ -72,10 +72,10 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
* @param simpleTypeHolder
* @param fieldNamingStrategy
*/
public BasicMongoPersistentProperty(Field field, PropertyDescriptor propertyDescriptor,
MongoPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder, FieldNamingStrategy fieldNamingStrategy) {
public BasicMongoPersistentProperty(Property property, MongoPersistentEntity<?> owner,
SimpleTypeHolder simpleTypeHolder, FieldNamingStrategy fieldNamingStrategy) {
super(field, propertyDescriptor, owner, simpleTypeHolder);
super(property, owner, simpleTypeHolder);
this.fieldNamingStrategy = fieldNamingStrategy == null ? PropertyNameFieldNamingStrategy.INSTANCE
: fieldNamingStrategy;
@@ -120,15 +120,11 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
if (isIdProperty()) {
if (owner == null) {
if (!getOwner().getIdProperty().isPresent()) {
return ID_FIELD_NAME;
}
if (owner.getIdProperty() == null) {
return ID_FIELD_NAME;
}
if (owner.isIdProperty(this)) {
if (getOwner().isIdProperty(this)) {
return ID_FIELD_NAME;
}
}
@@ -158,14 +154,13 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
private String getAnnotatedFieldName() {
org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(
Optional<org.springframework.data.mongodb.core.mapping.Field> annotation = findAnnotation(
org.springframework.data.mongodb.core.mapping.Field.class);
if (annotation != null && StringUtils.hasText(annotation.value())) {
return annotation.value();
}
return null;
return annotation//
.filter(it -> StringUtils.hasText(it.value()))//
.map(it -> it.value())//
.orElse(null);
}
/*
@@ -173,9 +168,11 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#getFieldOrder()
*/
public int getFieldOrder() {
org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(
Optional<org.springframework.data.mongodb.core.mapping.Field> annotation = findAnnotation(
org.springframework.data.mongodb.core.mapping.Field.class);
return annotation != null ? annotation.order() : Integer.MAX_VALUE;
return annotation.map(it -> it.order()).orElse(Integer.MAX_VALUE);
}
/*
@@ -200,7 +197,7 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#getDBRef()
*/
public DBRef getDBRef() {
return findAnnotation(DBRef.class);
return findAnnotation(DBRef.class).orElse(null);
}
/*

View File

@@ -15,10 +15,8 @@
*/
package org.springframework.data.mongodb.core.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
/**
@@ -43,9 +41,9 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty
* @param simpleTypeHolder
* @param fieldNamingStrategy
*/
public CachingMongoPersistentProperty(Field field, PropertyDescriptor propertyDescriptor,
MongoPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder, FieldNamingStrategy fieldNamingStrategy) {
super(field, propertyDescriptor, owner, simpleTypeHolder, fieldNamingStrategy);
public CachingMongoPersistentProperty(Property property, MongoPersistentEntity<?> owner,
SimpleTypeHolder simpleTypeHolder, FieldNamingStrategy fieldNamingStrategy) {
super(property, owner, simpleTypeHolder, fieldNamingStrategy);
}
/*

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.mapping;
import lombok.Value;
import java.util.Optional;
import java.util.function.BiConsumer;
import org.springframework.data.util.Optionals;
/**
* @author Oliver Gierke
*/
@Value(staticConstructor = "of")
public class EntityHandler<T extends MongoPersistentEntity<?>> {
Optional<T> entity;
public void doWithVersionProperty(BiConsumer<T, MongoPersistentProperty> consumer) {
Optionals.ifAllPresent(entity, entity.flatMap(it -> it.getVersionProperty()), consumer);
}
}

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.data.mongodb.core.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.AbstractMap;
import org.springframework.beans.BeansException;
@@ -25,6 +23,7 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
@@ -76,9 +75,9 @@ public class MongoMappingContext extends AbstractMappingContext<BasicMongoPersis
* @see org.springframework.data.mapping.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.MutablePersistentEntity, org.springframework.data.mapping.SimpleTypeHolder)
*/
@Override
public MongoPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
BasicMongoPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new CachingMongoPersistentProperty(field, descriptor, owner, simpleTypeHolder, fieldNamingStrategy);
public MongoPersistentProperty createPersistentProperty(Property property, BasicMongoPersistentEntity<?> owner,
SimpleTypeHolder simpleTypeHolder) {
return new CachingMongoPersistentProperty(property, owner, simpleTypeHolder, fieldNamingStrategy);
}
/*

View File

@@ -18,7 +18,6 @@ package org.springframework.data.mongodb.core.convert;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import com.mongodb.BasicDBObject;
import org.bson.BsonDocument;
import org.bson.Document;
import org.junit.Test;
@@ -28,6 +27,8 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import com.mongodb.BasicDBObject;
/**
* Unit tests for {@link DocumentAccessor}.
*
@@ -36,8 +37,8 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
public class DocumentAccessorUnitTests {
MongoMappingContext context = new MongoMappingContext();
MongoPersistentEntity<?> projectingTypeEntity = context.getPersistentEntity(ProjectingType.class);
MongoPersistentProperty fooProperty = projectingTypeEntity.getPersistentProperty("foo");
MongoPersistentEntity<?> projectingTypeEntity = context.getRequiredPersistentEntity(ProjectingType.class);
MongoPersistentProperty fooProperty = projectingTypeEntity.getRequiredPersistentProperty("foo");
@Test // DATAMONGO-766
public void putsNestedFieldCorrectly() {
@@ -80,14 +81,14 @@ public class DocumentAccessorUnitTests {
@Test // DATAMONGO-1335
public void writesAllNestingsCorrectly() {
MongoPersistentEntity<?> entity = context.getPersistentEntity(TypeWithTwoNestings.class);
MongoPersistentEntity<?> entity = context.getRequiredPersistentEntity(TypeWithTwoNestings.class);
Document target = new Document();
DocumentAccessor accessor = new DocumentAccessor(target);
accessor.put(entity.getPersistentProperty("id"), "id");
accessor.put(entity.getPersistentProperty("b"), "b");
accessor.put(entity.getPersistentProperty("c"), "c");
accessor.put(entity.getRequiredPersistentProperty("id"), "id");
accessor.put(entity.getRequiredPersistentProperty("b"), "b");
accessor.put(entity.getRequiredPersistentProperty("c"), "c");
Document nestedA = DocumentTestUtils.getAsDocument(target, "a");
@@ -100,11 +101,11 @@ public class DocumentAccessorUnitTests {
public void exposesAvailabilityOfFields() {
DocumentAccessor accessor = new DocumentAccessor(new Document("a", new BasicDBObject("c", "d")));
MongoPersistentEntity<?> entity = context.getPersistentEntity(ProjectingType.class);
MongoPersistentEntity<?> entity = context.getRequiredPersistentEntity(ProjectingType.class);
assertThat(accessor.hasValue(entity.getPersistentProperty("foo")), is(false));
assertThat(accessor.hasValue(entity.getPersistentProperty("a")), is(true));
assertThat(accessor.hasValue(entity.getPersistentProperty("name")), is(false));
assertThat(accessor.hasValue(entity.getRequiredPersistentProperty("foo")), is(false));
assertThat(accessor.hasValue(entity.getRequiredPersistentProperty("a")), is(true));
assertThat(accessor.hasValue(entity.getRequiredPersistentProperty("name")), is(false));
}
static class ProjectingType {

View File

@@ -115,6 +115,8 @@ public class MappingMongoConverterUnitTests {
mappingContext.setApplicationContext(context);
mappingContext.afterPropertiesSet();
mappingContext.getPersistentEntity(Address.class);
converter = new MappingMongoConverter(resolver, mappingContext);
converter.afterPropertiesSet();
}
@@ -413,7 +415,6 @@ public class MappingMongoConverterUnitTests {
}
@Test
@SuppressWarnings("unchecked")
public void writesNestedCollectionsCorrectly() {
CollectionWrapper wrapper = new CollectionWrapper();
@@ -495,7 +496,6 @@ public class MappingMongoConverterUnitTests {
GenericType<?> result = converter.read(GenericType.class, new org.bson.Document("content", address));
assertThat(result.content, is(instanceOf(Address.class)));
}
@Test // DATAMONGO-228
@@ -1134,7 +1134,6 @@ public class MappingMongoConverterUnitTests {
}
@Test // DATAMONGO-724
@SuppressWarnings("unchecked")
public void mappingConsidersCustomConvertersNotWritingTypeInformation() {
Person person = new Person();
@@ -1345,8 +1344,8 @@ public class MappingMongoConverterUnitTests {
assertThat(document, is(notNullValue()));
assertThat(document.get("box"), is(instanceOf(org.bson.Document.class)));
assertThat(document.get("box"), is((Object) new org.bson.Document().append("first", toDocument(object.box.getFirst()))
.append("second", toDocument(object.box.getSecond()))));
assertThat(document.get("box"), is((Object) new org.bson.Document()
.append("first", toDocument(object.box.getFirst())).append("second", toDocument(object.box.getSecond()))));
}
private static org.bson.Document toDocument(Point point) {
@@ -1386,7 +1385,7 @@ public class MappingMongoConverterUnitTests {
List<org.bson.Document> points = (List<org.bson.Document>) polygonDoc.get("points");
assertThat(points, hasSize(3));
assertThat(points, Matchers.<org.bson.Document> hasItems(toDocument(object.polygon.getPoints().get(0)),
assertThat(points, Matchers.<org.bson.Document>hasItems(toDocument(object.polygon.getPoints().get(0)),
toDocument(object.polygon.getPoints().get(1)), toDocument(object.polygon.getPoints().get(2))));
}
@@ -1705,12 +1704,11 @@ public class MappingMongoConverterUnitTests {
TypeWithOptional read = converter.read(TypeWithOptional.class, result);
assertThat(read.string, is(Optional.<String> empty()));
assertThat(read.string, is(Optional.<String>empty()));
assertThat(read.localDateTime, is(Optional.of(now)));
}
@Test // DATAMONGO-1118
@SuppressWarnings("unchecked")
public void convertsMapKeyUsingCustomConverterForAndBackwards() {
MappingMongoConverter converter = new MappingMongoConverter(resolver, mappingContext);

View File

@@ -80,7 +80,8 @@ public class MongoExampleMapperUnitTests {
IsBsonObject<Bson> expected = isBsonObject().containing("_id", "steelheart");
assertThat(mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(FlatDocument.class)),
is(expected));
}
@Test // DATAMONGO-1245
@@ -96,7 +97,8 @@ public class MongoExampleMapperUnitTests {
containing("stringValue", "firefight").//
containing("intValue", 100);
assertThat(mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(FlatDocument.class)),
is(expected));
}
@Test // DATAMONGO-1245
@@ -110,7 +112,8 @@ public class MongoExampleMapperUnitTests {
containing("stringValue", "firefight").//
containing("intValue", 100);
assertThat(mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(FlatDocument.class)),
is(expected));
}
@Test // DATAMONGO-1245
@@ -124,7 +127,8 @@ public class MongoExampleMapperUnitTests {
IsBsonObject<Bson> expected = isBsonObject().//
containing("listOfString", list);
assertThat(mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(FlatDocument.class)),
is(expected));
}
@Test // DATAMONGO-1245
@@ -135,7 +139,8 @@ public class MongoExampleMapperUnitTests {
IsBsonObject<Bson> expected = isBsonObject().containing("custom_field_name", "Mitosis");
assertThat(mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(FlatDocument.class)),
is(expected));
}
@Test // DATAMONGO-1245
@@ -146,7 +151,7 @@ public class MongoExampleMapperUnitTests {
probe.flatDoc.stringValue = "conflux";
org.bson.Document document = mapper.getMappedExample(Example.of(probe),
context.getPersistentEntity(WrapperDocument.class));
context.getRequiredPersistentEntity(WrapperDocument.class));
assertThat(document,
isBsonObject().containing("_class", new org.bson.Document("$in", new String[] { probe.getClass().getName() })));
@@ -161,7 +166,8 @@ public class MongoExampleMapperUnitTests {
IsBsonObject<Bson> expected = isBsonObject().containing("flatDoc\\.stringValue", "conflux");
assertThat(mapper.getMappedExample(of(probe), context.getPersistentEntity(WrapperDocument.class)), is(expected));
assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(WrapperDocument.class)),
is(expected));
}
@Test // DATAMONGO-1245
@@ -173,7 +179,7 @@ public class MongoExampleMapperUnitTests {
Example<?> example = Example.of(probe, matching().withIncludeNullValues());
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(WrapperDocument.class)), //
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(WrapperDocument.class)), //
isBsonObject().containing("flatDoc.stringValue", "conflux"));
}
@@ -190,7 +196,7 @@ public class MongoExampleMapperUnitTests {
containing("stringValue.$regex", "^firefight").//
containing("intValue", 100);
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class)), is(expected));
}
@Test // DATAMONGO-1245
@@ -206,7 +212,7 @@ public class MongoExampleMapperUnitTests {
containing("stringValue.$regex", "^" + Pattern.quote("fire.ight")).//
containing("intValue", 100);
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class)), is(expected));
}
@Test // DATAMONGO-1245
@@ -222,7 +228,7 @@ public class MongoExampleMapperUnitTests {
containing("stringValue.$regex", "firefight$").//
containing("intValue", 100);
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class)), is(expected));
}
@Test // DATAMONGO-1245
@@ -238,7 +244,7 @@ public class MongoExampleMapperUnitTests {
containing("stringValue.$regex", "firefight").//
containing("custom_field_name.$regex", "^(cat|dog).*shelter\\d?");
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class)), is(expected));
}
@Test // DATAMONGO-1245
@@ -254,7 +260,7 @@ public class MongoExampleMapperUnitTests {
containing("stringValue", new org.bson.Document("$regex", "firefight$").append("$options", "i")).//
containing("intValue", 100);
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class)), is(expected));
}
@Test // DATAMONGO-1245
@@ -270,7 +276,7 @@ public class MongoExampleMapperUnitTests {
containing("stringValue", new org.bson.Document("$regex", Pattern.quote("firefight")).append("$options", "i")).//
containing("intValue", 100);
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class)), is(expected));
}
@Test // DATAMONGO-1245
@@ -281,10 +287,11 @@ public class MongoExampleMapperUnitTests {
probe.referenceDocument = new ReferenceDocument();
probe.referenceDocument.id = "200";
org.bson.Document document = mapper.getMappedExample(of(probe), context.getPersistentEntity(WithDBRef.class));
org.bson.Document document = mapper.getMappedExample(of(probe),
context.getRequiredPersistentEntity(WithDBRef.class));
com.mongodb.DBRef reference = getTypedValue(document, "referenceDocument", com.mongodb.DBRef.class);
assertThat(reference.getId(), Is.<Object>is("200"));
assertThat(reference.getId(), Is.<Object> is("200"));
assertThat(reference.getCollectionName(), is("refDoc"));
}
@@ -294,7 +301,8 @@ public class MongoExampleMapperUnitTests {
FlatDocument probe = new FlatDocument();
probe.stringValue = "steelheart";
org.bson.Document document = mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class));
org.bson.Document document = mapper.getMappedExample(of(probe),
context.getRequiredPersistentEntity(FlatDocument.class));
assertThat(document, isBsonObject().containing("stringValue", "steelheart"));
}
@@ -305,10 +313,11 @@ public class MongoExampleMapperUnitTests {
ClassWithGeoTypes probe = new ClassWithGeoTypes();
probe.legacyPoint = new Point(10D, 20D);
org.bson.Document document = mapper.getMappedExample(of(probe), context.getPersistentEntity(WithDBRef.class));
org.bson.Document document = mapper.getMappedExample(of(probe),
context.getRequiredPersistentEntity(WithDBRef.class));
assertThat(document.get("legacyPoint.x"), Is.<Object>is(10D));
assertThat(document.get("legacyPoint.y"), Is.<Object>is(20D));
assertThat(document.get("legacyPoint.x"), Is.<Object> is(10D));
assertThat(document.get("legacyPoint.y"), Is.<Object> is(20D));
}
@Test // DATAMONGO-1245
@@ -325,7 +334,7 @@ public class MongoExampleMapperUnitTests {
containing("stringValue", "string").//
containing("intValue", 10);
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class)), is(expected));
}
@Test // DATAMONGO-1245
@@ -342,7 +351,7 @@ public class MongoExampleMapperUnitTests {
containing("custom_field_name", "foo").//
containing("intValue", 10);
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class)), is(expected));
}
@Test // DATAMONGO-1245
@@ -360,7 +369,8 @@ public class MongoExampleMapperUnitTests {
containing("flatDoc\\.custom_field_name", "foo").//
containing("flatDoc\\.intValue", 10);
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(WrapperDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(WrapperDocument.class)),
is(expected));
}
@Test // DATAMONGO-1245
@@ -378,7 +388,8 @@ public class MongoExampleMapperUnitTests {
containing("flatDoc\\.stringValue", "string").//
containing("flatDoc\\.intValue", 10);
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(WrapperDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(WrapperDocument.class)),
is(expected));
}
@Test // DATAMONGO-1245
@@ -394,7 +405,7 @@ public class MongoExampleMapperUnitTests {
containing("stringValue.$regex", ".*firefight.*").//
containing("custom_field_name", "steelheart");
assertThat(mapper.getMappedExample(example, context.getPersistentEntity(FlatDocument.class)), is(expected));
assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class)), is(expected));
}
@Test // DATAMONGO-1245
@@ -405,7 +416,8 @@ public class MongoExampleMapperUnitTests {
probe.customNamedField = "steelheart";
probe.anotherStringValue = "calamity";
org.bson.Document document = mapper.getMappedExample(of(probe), context.getPersistentEntity(FlatDocument.class));
org.bson.Document document = mapper.getMappedExample(of(probe),
context.getRequiredPersistentEntity(FlatDocument.class));
assertThat(document, isBsonObject().containing("anotherStringValue", "calamity"));
}

View File

@@ -34,6 +34,7 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
@@ -87,7 +88,10 @@ public class BasicMongoPersistentPropertyUnitTests {
@Test // DATAMONGO-553
public void usesPropertyAccessForThrowableCause() {
MongoPersistentProperty property = getPropertyFor(ReflectionUtils.findField(Throwable.class, "cause"));
BasicMongoPersistentEntity<Throwable> entity = new BasicMongoPersistentEntity<>(
ClassTypeInformation.from(Throwable.class));
MongoPersistentProperty property = getPropertyFor(entity, "cause");
assertThat(property.usePropertyAccess(), is(true));
}
@@ -96,13 +100,13 @@ public class BasicMongoPersistentPropertyUnitTests {
Field field = ReflectionUtils.findField(Person.class, "lastname");
MongoPersistentProperty property = new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder(),
UppercaseFieldNamingStrategy.INSTANCE);
MongoPersistentProperty property = new BasicMongoPersistentProperty(Property.of(field), entity,
new SimpleTypeHolder(), UppercaseFieldNamingStrategy.INSTANCE);
assertThat(property.getFieldName(), is("LASTNAME"));
field = ReflectionUtils.findField(Person.class, "firstname");
property = new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder(),
property = new BasicMongoPersistentProperty(Property.of(field), entity, new SimpleTypeHolder(),
UppercaseFieldNamingStrategy.INSTANCE);
assertThat(property.getFieldName(), is("foo"));
}
@@ -111,8 +115,8 @@ public class BasicMongoPersistentPropertyUnitTests {
public void rejectsInvalidValueReturnedByFieldNamingStrategy() {
Field field = ReflectionUtils.findField(Person.class, "lastname");
MongoPersistentProperty property = new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder(),
InvalidFieldNamingStrategy.INSTANCE);
MongoPersistentProperty property = new BasicMongoPersistentProperty(Property.of(field), entity,
new SimpleTypeHolder(), InvalidFieldNamingStrategy.INSTANCE);
exception.expect(MappingException.class);
exception.expectMessage(InvalidFieldNamingStrategy.class.getName());
@@ -192,7 +196,7 @@ public class BasicMongoPersistentPropertyUnitTests {
}
private MongoPersistentProperty getPropertyFor(MongoPersistentEntity<?> persistentEntity, Field field) {
return new BasicMongoPersistentProperty(field, null, persistentEntity, new SimpleTypeHolder(),
return new BasicMongoPersistentProperty(Property.of(field), persistentEntity, new SimpleTypeHolder(),
PropertyNameFieldNamingStrategy.INSTANCE);
}