Compare commits
18 Commits
1.4.1.RELE
...
1.4.2.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1068687bb | ||
|
|
6eae6d3e2c | ||
|
|
abfb98afe1 | ||
|
|
f361368893 | ||
|
|
063438002b | ||
|
|
9b54a5cd39 | ||
|
|
14360f2ab4 | ||
|
|
81c368c851 | ||
|
|
cf3818e04c | ||
|
|
da9870504f | ||
|
|
1285f4f26e | ||
|
|
791938f05d | ||
|
|
1b2d98dd3d | ||
|
|
de364c65ab | ||
|
|
57a74b0427 | ||
|
|
f35df8fe69 | ||
|
|
2d3aac1826 | ||
|
|
15db4ba6ea |
@@ -26,7 +26,7 @@ Add the Maven dependency:
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.4.1.RELEASE</version>
|
||||
<version>1.4.2.RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
8
pom.xml
8
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.4.1.RELEASE</version>
|
||||
<version>1.4.2.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Spring Data MongoDB</name>
|
||||
@@ -15,7 +15,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>1.3.1.RELEASE</version>
|
||||
<version>1.3.2.RELEASE</version>
|
||||
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>1.7.1.RELEASE</springdata.commons>
|
||||
<springdata.commons>1.7.2.RELEASE</springdata.commons>
|
||||
<mongo>2.11.4</mongo>
|
||||
<mongo-osgi>${mongo}</mongo-osgi>
|
||||
</properties>
|
||||
@@ -107,7 +107,7 @@
|
||||
<profile>
|
||||
<id>mongo-next</id>
|
||||
<properties>
|
||||
<mongo>2.12.0-rc0</mongo>
|
||||
<mongo>2.12.0</mongo>
|
||||
<mongo-osgi>2.12.0</mongo-osgi>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.4.1.RELEASE</version>
|
||||
<version>1.4.2.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.4.1.RELEASE</version>
|
||||
<version>1.4.2.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -56,17 +56,13 @@
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>${aspectj}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JPA -->
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
<version>${jpa}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- For Tests -->
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.4.1.RELEASE</version>
|
||||
<version>1.4.2.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.4.1.RELEASE</version>
|
||||
<version>1.4.2.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.4.1.RELEASE</version>
|
||||
<version>1.4.2.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ import org.w3c.dom.Element;
|
||||
* @author Oliver Gierke
|
||||
* @author Maciej Walkowiak
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
|
||||
@@ -83,8 +84,11 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
*/
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
|
||||
BeanDefinitionRegistry registry = parserContext.getRegistry();
|
||||
if (parserContext.isNested()) {
|
||||
parserContext.getReaderContext().error("Mongo Converter must not be defined as nested bean.", element);
|
||||
}
|
||||
|
||||
BeanDefinitionRegistry registry = parserContext.getRegistry();
|
||||
String id = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);
|
||||
id = StringUtils.hasText(id) ? id : "mappingConverter";
|
||||
|
||||
|
||||
@@ -352,7 +352,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
public void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch) {
|
||||
executeQuery(query, collectionName, dch, new QueryCursorPreparer(query));
|
||||
executeQuery(query, collectionName, dch, new QueryCursorPreparer(query, null));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,7 +530,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||
new QueryCursorPreparer(query));
|
||||
new QueryCursorPreparer(query, entityClass));
|
||||
}
|
||||
|
||||
public <T> T findById(Object id, Class<T> entityClass) {
|
||||
@@ -612,8 +612,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
public <T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass,
|
||||
String collectionName) {
|
||||
return doFindAndModify(collectionName, query.getQueryObject(), query.getFieldsObject(), query.getSortObject(),
|
||||
entityClass, update, options);
|
||||
return doFindAndModify(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||
getMappedSortObject(query, entityClass), entityClass, update, options);
|
||||
}
|
||||
|
||||
// Find methods that take a Query to express the query and that return a single object that is also removed from the
|
||||
@@ -624,8 +624,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
public <T> T findAndRemove(Query query, Class<T> entityClass, String collectionName) {
|
||||
return doFindAndRemove(collectionName, query.getQueryObject(), query.getFieldsObject(), query.getSortObject(),
|
||||
entityClass);
|
||||
|
||||
return doFindAndRemove(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||
getMappedSortObject(query, entityClass), entityClass);
|
||||
}
|
||||
|
||||
public long count(Query query, Class<?> entityClass) {
|
||||
@@ -1858,6 +1859,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
return converter;
|
||||
}
|
||||
|
||||
private DBObject getMappedSortObject(Query query, Class<?> type) {
|
||||
|
||||
if (query == null || query.getSortObject() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
|
||||
return queryMapper.getMappedObject(query.getSortObject(), entity);
|
||||
}
|
||||
|
||||
// Callback implementations
|
||||
|
||||
/**
|
||||
@@ -2051,9 +2062,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
class QueryCursorPreparer implements CursorPreparer {
|
||||
|
||||
private final Query query;
|
||||
private final Class<?> type;
|
||||
|
||||
public QueryCursorPreparer(Query query, Class<?> type) {
|
||||
|
||||
public QueryCursorPreparer(Query query) {
|
||||
this.query = query;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2081,7 +2095,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
cursorToUse = cursorToUse.limit(query.getLimit());
|
||||
}
|
||||
if (query.getSortObject() != null) {
|
||||
cursorToUse = cursorToUse.sort(query.getSortObject());
|
||||
cursorToUse = cursorToUse.sort(getMappedSortObject(query, type));
|
||||
}
|
||||
if (StringUtils.hasText(query.getHint())) {
|
||||
cursorToUse = cursorToUse.hint(query.getHint());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2014 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.
|
||||
@@ -69,12 +69,26 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
@Override
|
||||
public FieldReference getReference(String name) {
|
||||
|
||||
Assert.notNull(name, "Name must not be null!");
|
||||
|
||||
ExposedField field = exposedFields.getField(name);
|
||||
|
||||
if (field != null) {
|
||||
return new FieldReference(field);
|
||||
}
|
||||
|
||||
if (name.contains(".")) {
|
||||
|
||||
// for nested field references we only check that the root field exists.
|
||||
ExposedField rootField = exposedFields.getField(name.split("\\.")[0]);
|
||||
|
||||
if (rootField != null) {
|
||||
|
||||
// We have to synthetic to true, in order to render the field-name as is.
|
||||
return new FieldReference(new ExposedField(name, true));
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Invalid reference '%s'!", name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
@@ -93,22 +94,28 @@ public class CustomConversions {
|
||||
this.customSimpleTypes = new HashSet<Class<?>>();
|
||||
this.customReadTargetTypes = new ConcurrentHashMap<GenericConverter.ConvertiblePair, CacheValue>();
|
||||
|
||||
this.converters = new ArrayList<Object>();
|
||||
this.converters.addAll(converters);
|
||||
this.converters.add(CustomToStringConverter.INSTANCE);
|
||||
this.converters.add(BigDecimalToStringConverter.INSTANCE);
|
||||
this.converters.add(StringToBigDecimalConverter.INSTANCE);
|
||||
this.converters.add(BigIntegerToStringConverter.INSTANCE);
|
||||
this.converters.add(StringToBigIntegerConverter.INSTANCE);
|
||||
this.converters.add(URLToStringConverter.INSTANCE);
|
||||
this.converters.add(StringToURLConverter.INSTANCE);
|
||||
this.converters.add(DBObjectToStringConverter.INSTANCE);
|
||||
this.converters.addAll(JodaTimeConverters.getConvertersToRegister());
|
||||
List<Object> toRegister = new ArrayList<Object>();
|
||||
|
||||
for (Object c : this.converters) {
|
||||
toRegister.addAll(converters);
|
||||
toRegister.add(CustomToStringConverter.INSTANCE);
|
||||
toRegister.add(BigDecimalToStringConverter.INSTANCE);
|
||||
toRegister.add(StringToBigDecimalConverter.INSTANCE);
|
||||
toRegister.add(BigIntegerToStringConverter.INSTANCE);
|
||||
toRegister.add(StringToBigIntegerConverter.INSTANCE);
|
||||
toRegister.add(URLToStringConverter.INSTANCE);
|
||||
toRegister.add(StringToURLConverter.INSTANCE);
|
||||
toRegister.add(DBObjectToStringConverter.INSTANCE);
|
||||
toRegister.addAll(JodaTimeConverters.getConvertersToRegister());
|
||||
|
||||
// Add user provided converters to make sure they can override the defaults
|
||||
|
||||
for (Object c : toRegister) {
|
||||
registerConversion(c);
|
||||
}
|
||||
|
||||
Collections.reverse(toRegister);
|
||||
|
||||
this.converters = Collections.unmodifiableList(toRegister);
|
||||
this.simpleTypeHolder = new SimpleTypeHolder(customSimpleTypes, MongoSimpleTypes.HOLDER);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2014 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.
|
||||
@@ -24,15 +24,22 @@ import com.mongodb.DBRef;
|
||||
* Used to resolve associations annotated with {@link org.springframework.data.mongodb.core.mapping.DBRef}.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @since 1.4
|
||||
*/
|
||||
public interface DbRefResolver {
|
||||
|
||||
/**
|
||||
* Resolves the given {@link DBRef} into an object of the given {@link MongoPersistentProperty}'s type. The method
|
||||
* might return a proxy object for the {@link DBRef} or resolve it immediately. In both cases the
|
||||
* {@link DbRefResolverCallback} will be used to obtain the actual backing object.
|
||||
*
|
||||
* @param property will never be {@literal null}.
|
||||
* @param dbref the {@link DBRef} to resolve.
|
||||
* @param callback will never be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Object resolveDbRef(MongoPersistentProperty property, DbRefResolverCallback callback);
|
||||
Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback);
|
||||
|
||||
/**
|
||||
* Creates a {@link DBRef} instance for the given {@link org.springframework.data.mongodb.core.mapping.DBRef}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2014 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.
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
@@ -51,6 +53,7 @@ import com.mongodb.DBRef;
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @since 1.4
|
||||
*/
|
||||
public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
@@ -78,13 +81,13 @@ 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, DbRefResolverCallback callback) {
|
||||
public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback) {
|
||||
|
||||
Assert.notNull(property, "Property must not be null!");
|
||||
Assert.notNull(callback, "Callback must not be null!");
|
||||
|
||||
if (isLazyDbRef(property)) {
|
||||
return createLazyLoadingProxy(property, callback);
|
||||
return createLazyLoadingProxy(property, dbref, callback);
|
||||
}
|
||||
|
||||
return callback.resolve(property);
|
||||
@@ -109,10 +112,11 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
* eventually resolve the value of the property.
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @param dbref can be {@literal null}.
|
||||
* @param callback must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private Object createLazyLoadingProxy(MongoPersistentProperty property, DbRefResolverCallback callback) {
|
||||
private Object createLazyLoadingProxy(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback) {
|
||||
|
||||
ProxyFactory proxyFactory = new ProxyFactory();
|
||||
Class<?> propertyType = property.getType();
|
||||
@@ -121,7 +125,9 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
proxyFactory.addInterface(type);
|
||||
}
|
||||
|
||||
LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor(property, exceptionTranslator, callback);
|
||||
LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor(property, dbref, exceptionTranslator, callback);
|
||||
|
||||
proxyFactory.addInterface(LazyLoadingProxy.class);
|
||||
|
||||
if (propertyType.isInterface()) {
|
||||
proxyFactory.addInterface(propertyType);
|
||||
@@ -141,7 +147,9 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param property
|
||||
* Returns whether the property shall be resolved lazily.
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private boolean isLazyDbRef(MongoPersistentProperty property) {
|
||||
@@ -154,31 +162,46 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
* guaranteed to be performed only once.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor,
|
||||
Serializable {
|
||||
|
||||
private static final Method INITIALIZE_METHOD, TO_DBREF_METHOD;
|
||||
|
||||
private final DbRefResolverCallback callback;
|
||||
private final MongoPersistentProperty property;
|
||||
private final PersistenceExceptionTranslator exceptionTranslator;
|
||||
|
||||
private volatile boolean resolved;
|
||||
private Object result;
|
||||
private DBRef dbref;
|
||||
|
||||
static {
|
||||
try {
|
||||
INITIALIZE_METHOD = LazyLoadingProxy.class.getMethod("initialize");
|
||||
TO_DBREF_METHOD = LazyLoadingProxy.class.getMethod("toDBRef");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link LazyLoadingInterceptor} for the given {@link MongoPersistentProperty},
|
||||
* {@link PersistenceExceptionTranslator} and {@link DbRefResolverCallback}.
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @param dbref can be {@literal null}.
|
||||
* @param callback must not be {@literal null}.
|
||||
*/
|
||||
public LazyLoadingInterceptor(MongoPersistentProperty property, PersistenceExceptionTranslator exceptionTranslator,
|
||||
DbRefResolverCallback callback) {
|
||||
public LazyLoadingInterceptor(MongoPersistentProperty property, DBRef dbref,
|
||||
PersistenceExceptionTranslator exceptionTranslator, DbRefResolverCallback callback) {
|
||||
|
||||
Assert.notNull(property, "Property must not be null!");
|
||||
Assert.notNull(exceptionTranslator, "Exception translator must not be null!");
|
||||
Assert.notNull(callback, "Callback must not be null!");
|
||||
|
||||
this.dbref = dbref;
|
||||
this.callback = callback;
|
||||
this.exceptionTranslator = exceptionTranslator;
|
||||
this.property = property;
|
||||
@@ -199,9 +222,95 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
*/
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
return ReflectionUtils.isObjectMethod(method) ? method.invoke(obj, args) : method.invoke(ensureResolved(), args);
|
||||
|
||||
if (INITIALIZE_METHOD.equals(method)) {
|
||||
return ensureResolved();
|
||||
}
|
||||
|
||||
if (TO_DBREF_METHOD.equals(method)) {
|
||||
return this.dbref;
|
||||
}
|
||||
|
||||
if (isObjectMethod(method) && Object.class.equals(method.getDeclaringClass())) {
|
||||
|
||||
if (ReflectionUtils.isToStringMethod(method)) {
|
||||
return proxyToString(proxy);
|
||||
}
|
||||
|
||||
if (ReflectionUtils.isEqualsMethod(method)) {
|
||||
return proxyEquals(proxy, args[0]);
|
||||
}
|
||||
|
||||
if (ReflectionUtils.isHashCodeMethod(method)) {
|
||||
return proxyHashCode(proxy);
|
||||
}
|
||||
}
|
||||
|
||||
Object target = ensureResolved();
|
||||
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return method.invoke(target, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a to string representation for the given {@code proxy}.
|
||||
*
|
||||
* @param proxy
|
||||
* @return
|
||||
*/
|
||||
private String proxyToString(Object proxy) {
|
||||
|
||||
StringBuilder description = new StringBuilder();
|
||||
if (dbref != null) {
|
||||
description.append(dbref.getRef());
|
||||
description.append(":");
|
||||
description.append(dbref.getId());
|
||||
} else {
|
||||
description.append(System.identityHashCode(proxy));
|
||||
}
|
||||
description.append("$").append(LazyLoadingProxy.class.getSimpleName());
|
||||
|
||||
return description.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hashcode for the given {@code proxy}.
|
||||
*
|
||||
* @param proxy
|
||||
* @return
|
||||
*/
|
||||
private int proxyHashCode(Object proxy) {
|
||||
return proxyToString(proxy).hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an equality check for the given {@code proxy}.
|
||||
*
|
||||
* @param proxy
|
||||
* @param that
|
||||
* @return
|
||||
*/
|
||||
private boolean proxyEquals(Object proxy, Object that) {
|
||||
|
||||
if (!(that instanceof LazyLoadingProxy)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (that == proxy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return proxyToString(proxy).equals(that.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Will trigger the resolution if the proxy is not resolved already or return a previously resolved result.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Object ensureResolved() {
|
||||
|
||||
if (!resolved) {
|
||||
@@ -212,16 +321,28 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for serialization.
|
||||
*
|
||||
* @param out
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
|
||||
ensureResolved();
|
||||
out.writeObject(this.result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for deserialization.
|
||||
*
|
||||
* @param in
|
||||
* @throws IOException
|
||||
*/
|
||||
private void readObject(ObjectInputStream in) throws IOException {
|
||||
|
||||
try {
|
||||
this.resolved = true; // Object is guaranteed to be resolved after serializations
|
||||
this.resolved = true;
|
||||
this.result = in.readObject();
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new LazyLoadingException("Could not deserialize result", e);
|
||||
@@ -229,6 +350,8 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the proxy into its backing object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private synchronized Object resolve() {
|
||||
@@ -248,14 +371,6 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isResolved() {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,6 +388,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(type);
|
||||
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
|
||||
enhancer.setInterfaces(new Class[] { LazyLoadingProxy.class });
|
||||
|
||||
Factory factory = (Factory) OBJENESIS.newInstance(enhancer.createClass());
|
||||
factory.setCallbacks(new Callback[] { interceptor });
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2014 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.convert;
|
||||
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.LazyLoadingInterceptor;
|
||||
|
||||
import com.mongodb.DBRef;
|
||||
|
||||
/**
|
||||
* Allows direct interaction with the underlying {@link LazyLoadingInterceptor}.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @since 1.5
|
||||
*/
|
||||
public interface LazyLoadingProxy {
|
||||
|
||||
/**
|
||||
* Initializes the proxy and returns the wrapped value.
|
||||
*
|
||||
* @return
|
||||
* @since 1.5
|
||||
*/
|
||||
Object initialize();
|
||||
|
||||
/**
|
||||
* Returns the {@link DBRef} represented by this {@link LazyLoadingProxy}, may be null.
|
||||
*
|
||||
* @return
|
||||
* @since 1.5
|
||||
*/
|
||||
DBRef toDBRef();
|
||||
}
|
||||
@@ -276,9 +276,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
entity.doWithAssociations(new AssociationHandler<MongoPersistentProperty>() {
|
||||
public void doWithAssociation(Association<MongoPersistentProperty> association) {
|
||||
|
||||
MongoPersistentProperty inverseProp = association.getInverse();
|
||||
MongoPersistentProperty property = association.getInverse();
|
||||
|
||||
Object obj = dbRefResolver.resolveDbRef(inverseProp, new DbRefResolverCallback() {
|
||||
Object value = dbo.get(property.getName());
|
||||
DBRef dbref = value instanceof DBRef ? (DBRef) value : null;
|
||||
Object obj = dbRefResolver.resolveDbRef(property, dbref, new DbRefResolverCallback() {
|
||||
|
||||
@Override
|
||||
public Object resolve(MongoPersistentProperty property) {
|
||||
@@ -286,7 +288,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
});
|
||||
|
||||
wrapper.setProperty(inverseProp, obj);
|
||||
wrapper.setProperty(property, obj);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -403,6 +405,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
Object propertyObj = wrapper.getProperty(prop, prop.getType(), fieldAccessOnly);
|
||||
|
||||
if (null != propertyObj) {
|
||||
|
||||
if (!conversions.isSimpleType(propertyObj.getClass())) {
|
||||
writePropertyInternal(propertyObj, dbo, prop);
|
||||
} else {
|
||||
@@ -449,13 +452,32 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
|
||||
if (prop.isDbReference()) {
|
||||
DBRef dbRefObj = createDBRef(obj, prop);
|
||||
|
||||
DBRef dbRefObj = null;
|
||||
|
||||
/*
|
||||
* If we already have a LazyLoadingProxy, we use it's cached DBRef value instead of
|
||||
* unnecessarily initializing it only to convert it to a DBRef a few instructions later.
|
||||
*/
|
||||
if (obj instanceof LazyLoadingProxy) {
|
||||
dbRefObj = ((LazyLoadingProxy) obj).toDBRef();
|
||||
}
|
||||
|
||||
dbRefObj = dbRefObj != null ? dbRefObj : createDBRef(obj, prop);
|
||||
|
||||
if (null != dbRefObj) {
|
||||
accessor.put(prop, dbRefObj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a LazyLoadingProxy we make sure it is initialized first.
|
||||
*/
|
||||
if (obj instanceof LazyLoadingProxy) {
|
||||
obj = ((LazyLoadingProxy) obj).initialize();
|
||||
}
|
||||
|
||||
// Lookup potential custom target type
|
||||
Class<?> basicTargetType = conversions.getCustomWriteTarget(obj.getClass(), null);
|
||||
|
||||
@@ -934,7 +956,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return getPotentiallyConvertedSimpleWrite(obj);
|
||||
}
|
||||
|
||||
TypeInformation<?> typeHint = typeInformation == null ? null : ClassTypeInformation.OBJECT;
|
||||
TypeInformation<?> typeHint = typeInformation == null ? ClassTypeInformation.OBJECT : typeInformation;
|
||||
|
||||
if (obj instanceof BasicDBList) {
|
||||
return maybeConvertList((BasicDBList) obj, typeHint);
|
||||
|
||||
@@ -250,14 +250,31 @@ public class QueryMapper {
|
||||
* type of the given value is compatible with the type of the given document field in order to deal with potential
|
||||
* query field exclusions, since MongoDB uses the {@code int} {@literal 0} as an indicator for an excluded field.
|
||||
*
|
||||
* @param documentField
|
||||
* @param documentField must not be {@literal null}.
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
protected boolean isAssociationConversionNecessary(Field documentField, Object value) {
|
||||
return documentField.isAssociation() && value != null
|
||||
&& (documentField.getProperty().getActualType().isAssignableFrom(value.getClass()) //
|
||||
|| documentField.getPropertyEntity().getIdProperty().getActualType().isAssignableFrom(value.getClass()));
|
||||
|
||||
Assert.notNull(documentField, "Document field must not be null!");
|
||||
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!documentField.isAssociation()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Class<? extends Object> type = value.getClass();
|
||||
MongoPersistentProperty property = documentField.getProperty();
|
||||
|
||||
if (property.getActualType().isAssignableFrom(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> entity = documentField.getPropertyEntity();
|
||||
return entity.hasIdProperty() && entity.getIdProperty().getActualType().isAssignableFrom(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,7 +306,7 @@ public class QueryMapper {
|
||||
* @return the converted mongo type or null if source is null
|
||||
*/
|
||||
protected Object delegateConvertToMongoType(Object source, MongoPersistentEntity<?> entity) {
|
||||
return converter.convertToMongoType(source);
|
||||
return converter.convertToMongoType(source, entity == null ? null : entity.getTypeInformation());
|
||||
}
|
||||
|
||||
protected Object convertAssociation(Object source, Field field) {
|
||||
@@ -594,6 +611,21 @@ public class QueryMapper {
|
||||
*/
|
||||
public MetadataBackedField(String name, MongoPersistentEntity<?> entity,
|
||||
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context) {
|
||||
this(name, entity, context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MetadataBackedField} with the given name, {@link MongoPersistentEntity} and
|
||||
* {@link MappingContext} with the given {@link MongoPersistentProperty}.
|
||||
*
|
||||
* @param name must not be {@literal null} or empty.
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param context must not be {@literal null}.
|
||||
* @param property may be {@literal null}.
|
||||
*/
|
||||
public MetadataBackedField(String name, MongoPersistentEntity<?> entity,
|
||||
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context,
|
||||
MongoPersistentProperty property) {
|
||||
|
||||
super(name);
|
||||
|
||||
@@ -603,7 +635,7 @@ public class QueryMapper {
|
||||
this.mappingContext = context;
|
||||
|
||||
this.path = getPath(name);
|
||||
this.property = path == null ? null : path.getLeafProperty();
|
||||
this.property = path == null ? property : path.getLeafProperty();
|
||||
this.association = findAssociation();
|
||||
}
|
||||
|
||||
@@ -613,7 +645,7 @@ public class QueryMapper {
|
||||
*/
|
||||
@Override
|
||||
public MetadataBackedField with(String name) {
|
||||
return new MetadataBackedField(name, entity, mappingContext);
|
||||
return new MetadataBackedField(name, entity, mappingContext, property);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
* Copyright 2010-2014 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.
|
||||
@@ -30,10 +30,8 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class Point {
|
||||
|
||||
@Field(order = 10)
|
||||
private final double x;
|
||||
@Field(order = 20)
|
||||
private final double y;
|
||||
@Field(order = 10) private final double x;
|
||||
@Field(order = 20) private final double y;
|
||||
|
||||
@PersistenceConstructor
|
||||
public Point(double x, double y) {
|
||||
@@ -69,9 +67,9 @@ public class Point {
|
||||
int result = 1;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(x);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
result = prime * result + (int) (temp ^ temp >>> 32);
|
||||
temp = Double.doubleToLongBits(y);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
result = prime * result + (int) (temp ^ temp >>> 32);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -98,6 +96,6 @@ public class Point {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Point [latitude=%f, longitude=%f]", x, y);
|
||||
return String.format("Point [x=%f, y=%f]", x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ import static org.junit.Assert.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -45,37 +47,45 @@ import com.mongodb.DBObject;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class MappingMongoConverterParserIntegrationTests {
|
||||
|
||||
@Rule public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
DefaultListableBeanFactory factory;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
factory = new DefaultListableBeanFactory();
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
|
||||
reader.loadBeanDefinitions(new ClassPathResource("namespace/converter.xml"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-243
|
||||
*/
|
||||
@Test
|
||||
public void allowsDbFactoryRefAttribute() {
|
||||
|
||||
loadValidConfiguration();
|
||||
factory.getBeanDefinition("converter");
|
||||
factory.getBean("converter");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-725
|
||||
*/
|
||||
@Test
|
||||
public void hasCustomTypeMapper() {
|
||||
|
||||
loadValidConfiguration();
|
||||
MappingMongoConverter converter = factory.getBean("converter", MappingMongoConverter.class);
|
||||
MongoTypeMapper customMongoTypeMapper = factory.getBean(CustomMongoTypeMapper.class);
|
||||
|
||||
assertThat(converter.getTypeMapper(), is(customMongoTypeMapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-301
|
||||
*/
|
||||
@Test
|
||||
public void scansForConverterAndSetsUpCustomConversionsAccordingly() {
|
||||
|
||||
loadValidConfiguration();
|
||||
CustomConversions conversions = factory.getBean(CustomConversions.class);
|
||||
assertThat(conversions.hasCustomWriteTarget(Person.class), is(true));
|
||||
assertThat(conversions.hasCustomWriteTarget(Account.class), is(true));
|
||||
@@ -87,6 +97,7 @@ public class MappingMongoConverterParserIntegrationTests {
|
||||
@Test
|
||||
public void activatesAbbreviatingPropertiesCorrectly() {
|
||||
|
||||
loadValidConfiguration();
|
||||
BeanDefinition definition = factory.getBeanDefinition("abbreviatingConverter.mappingContext");
|
||||
Object value = definition.getPropertyValues().getPropertyValue("fieldNamingStrategy").getValue();
|
||||
|
||||
@@ -95,6 +106,32 @@ public class MappingMongoConverterParserIntegrationTests {
|
||||
assertThat(strategy.getBeanClassName(), is(CamelCaseAbbreviatingFieldNamingStrategy.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-892
|
||||
*/
|
||||
@Test
|
||||
public void shouldThrowBeanDefinitionParsingExceptionIfConverterDefinedAsNestedBean() {
|
||||
|
||||
exception.expect(BeanDefinitionParsingException.class);
|
||||
exception.expectMessage("Mongo Converter must not be defined as nested bean.");
|
||||
|
||||
loadNestedBeanConfiguration();
|
||||
}
|
||||
|
||||
private void loadValidConfiguration() {
|
||||
this.loadConfiguration("namespace/converter.xml");
|
||||
}
|
||||
|
||||
private void loadNestedBeanConfiguration() {
|
||||
this.loadConfiguration("namespace/converter-nested-bean-definition.xml");
|
||||
}
|
||||
|
||||
private void loadConfiguration(String configLocation) {
|
||||
factory = new DefaultListableBeanFactory();
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
|
||||
reader.loadBeanDefinitions(new ClassPathResource(configLocation));
|
||||
}
|
||||
|
||||
@Component
|
||||
public static class SampleConverter implements Converter<Person, DBObject> {
|
||||
public DBObject convert(Person source) {
|
||||
|
||||
@@ -34,7 +34,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -61,6 +60,7 @@ import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.convert.CustomConversions;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.LazyLoadingProxy;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.index.Index;
|
||||
import org.springframework.data.mongodb.core.index.Index.Duplicates;
|
||||
@@ -298,6 +298,9 @@ public class MongoTemplateTests {
|
||||
@Test
|
||||
public void rejectsDuplicateIdInInsertAll() {
|
||||
|
||||
thrown.expect(DataIntegrityViolationException.class);
|
||||
thrown.expectMessage("E11000 duplicate key error index: database.person.$_id_");
|
||||
|
||||
MongoTemplate template = new MongoTemplate(factory);
|
||||
template.setWriteResultChecking(WriteResultChecking.EXCEPTION);
|
||||
|
||||
@@ -309,15 +312,7 @@ public class MongoTemplateTests {
|
||||
records.add(person);
|
||||
records.add(person);
|
||||
|
||||
try {
|
||||
template.insertAll(records);
|
||||
fail("Expected DataIntegrityViolationException!");
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
assertThat(
|
||||
e.getMessage(),
|
||||
CoreMatchers
|
||||
.startsWith("Insert list failed: E11000 duplicate key error index: database.person.$_id_ dup key: { : ObjectId"));
|
||||
}
|
||||
template.insertAll(records);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2491,6 +2486,160 @@ public class MongoTemplateTests {
|
||||
assertThat(result.get(0).dbRefProperty.field, is(sample.field));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-880
|
||||
*/
|
||||
@Test
|
||||
public void savingAndReassigningLazyLoadingProxies() {
|
||||
|
||||
template.dropCollection(SomeTemplate.class);
|
||||
template.dropCollection(SomeMessage.class);
|
||||
template.dropCollection(SomeContent.class);
|
||||
|
||||
SomeContent content = new SomeContent();
|
||||
content.id = "C1";
|
||||
content.text = "BUBU";
|
||||
template.save(content);
|
||||
|
||||
SomeTemplate tmpl = new SomeTemplate();
|
||||
tmpl.id = "T1";
|
||||
tmpl.content = content; // @DBRef(lazy=true) tmpl.content
|
||||
|
||||
template.save(tmpl);
|
||||
|
||||
SomeTemplate savedTmpl = template.findById(tmpl.id, SomeTemplate.class);
|
||||
|
||||
SomeMessage message = new SomeMessage();
|
||||
message.id = "M1";
|
||||
message.dbrefContent = savedTmpl.content; // @DBRef message.dbrefContent
|
||||
message.normalContent = savedTmpl.content;
|
||||
|
||||
template.save(message);
|
||||
|
||||
SomeMessage savedMessage = template.findById(message.id, SomeMessage.class);
|
||||
|
||||
assertThat(savedMessage.dbrefContent.text, is(content.text));
|
||||
assertThat(savedMessage.normalContent.text, is(content.text));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-884
|
||||
*/
|
||||
@Test
|
||||
public void callingNonObjectMethodsOnLazyLoadingProxyShouldReturnNullIfUnderlyingDbrefWasDeletedInbetween() {
|
||||
|
||||
template.dropCollection(SomeTemplate.class);
|
||||
template.dropCollection(SomeContent.class);
|
||||
|
||||
SomeContent content = new SomeContent();
|
||||
content.id = "C1";
|
||||
content.text = "BUBU";
|
||||
template.save(content);
|
||||
|
||||
SomeTemplate tmpl = new SomeTemplate();
|
||||
tmpl.id = "T1";
|
||||
tmpl.content = content; // @DBRef(lazy=true) tmpl.content
|
||||
|
||||
template.save(tmpl);
|
||||
|
||||
SomeTemplate savedTmpl = template.findById(tmpl.id, SomeTemplate.class);
|
||||
|
||||
template.remove(content);
|
||||
|
||||
assertThat(savedTmpl.getContent().toString(), is("someContent:C1$LazyLoadingProxy"));
|
||||
assertThat(savedTmpl.getContent(), is(instanceOf(LazyLoadingProxy.class)));
|
||||
assertThat(savedTmpl.getContent().getText(), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-888
|
||||
*/
|
||||
@Test
|
||||
public void sortOnIdFieldPropertyShouldBeMappedCorrectly() {
|
||||
|
||||
DoucmentWithNamedIdField one = new DoucmentWithNamedIdField();
|
||||
one.someIdKey = "1";
|
||||
one.value = "a";
|
||||
|
||||
DoucmentWithNamedIdField two = new DoucmentWithNamedIdField();
|
||||
two.someIdKey = "2";
|
||||
two.value = "b";
|
||||
|
||||
template.save(one);
|
||||
template.save(two);
|
||||
|
||||
Query query = query(where("_id").in("1", "2")).with(new Sort(Direction.DESC, "someIdKey"));
|
||||
assertThat(template.find(query, DoucmentWithNamedIdField.class), contains(two, one));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-888
|
||||
*/
|
||||
@Test
|
||||
public void sortOnAnnotatedFieldPropertyShouldBeMappedCorrectly() {
|
||||
|
||||
DoucmentWithNamedIdField one = new DoucmentWithNamedIdField();
|
||||
one.someIdKey = "1";
|
||||
one.value = "a";
|
||||
|
||||
DoucmentWithNamedIdField two = new DoucmentWithNamedIdField();
|
||||
two.someIdKey = "2";
|
||||
two.value = "b";
|
||||
|
||||
template.save(one);
|
||||
template.save(two);
|
||||
|
||||
Query query = query(where("_id").in("1", "2")).with(new Sort(Direction.DESC, "value"));
|
||||
assertThat(template.find(query, DoucmentWithNamedIdField.class), contains(two, one));
|
||||
}
|
||||
|
||||
static class DoucmentWithNamedIdField {
|
||||
|
||||
@Id String someIdKey;
|
||||
|
||||
@Field(value = "val")//
|
||||
String value;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (someIdKey == null ? 0 : someIdKey.hashCode());
|
||||
result = prime * result + (value == null ? 0 : value.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof DoucmentWithNamedIdField)) {
|
||||
return false;
|
||||
}
|
||||
DoucmentWithNamedIdField other = (DoucmentWithNamedIdField) obj;
|
||||
if (someIdKey == null) {
|
||||
if (other.someIdKey != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!someIdKey.equals(other.someIdKey)) {
|
||||
return false;
|
||||
}
|
||||
if (value == null) {
|
||||
if (other.value != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!value.equals(other.value)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class DocumentWithDBRefCollection {
|
||||
|
||||
@Id public String id;
|
||||
@@ -2670,4 +2819,30 @@ public class MongoTemplateTests {
|
||||
@Id String id;
|
||||
EnumValue value;
|
||||
}
|
||||
|
||||
public static class SomeTemplate {
|
||||
|
||||
String id;
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) SomeContent content;
|
||||
|
||||
public SomeContent getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SomeContent {
|
||||
|
||||
String id;
|
||||
String text;
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
static class SomeMessage {
|
||||
String id;
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef SomeContent dbrefContent;
|
||||
SomeContent normalContent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
org.mockito.Matchers.isNull(DBObject.class), org.mockito.Matchers.isNull(DBObject.class), eq(false),
|
||||
captor.capture(), eq(false), eq(false));
|
||||
|
||||
Assert.assertThat(captor.getValue().get("$inc"), Is.<Object> is(new BasicDBObject("version", 1)));
|
||||
Assert.assertThat(captor.getValue().get("$inc"), Is.<Object> is(new BasicDBObject("version", 1L)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2011-2014 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.
|
||||
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -33,14 +33,13 @@ import com.mongodb.DBCursor;
|
||||
* Unit tests for {@link QueryCursorPreparer}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class QueryCursorPreparerUnitTests {
|
||||
|
||||
@Mock
|
||||
MongoDbFactory factory;
|
||||
@Mock
|
||||
DBCursor cursor;
|
||||
@Mock MongoDbFactory factory;
|
||||
@Mock DBCursor cursor;
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-185
|
||||
@@ -50,7 +49,7 @@ public class QueryCursorPreparerUnitTests {
|
||||
|
||||
Query query = query(where("foo").is("bar")).withHint("hint");
|
||||
|
||||
CursorPreparer preparer = new MongoTemplate(factory).new QueryCursorPreparer(query);
|
||||
CursorPreparer preparer = new MongoTemplate(factory).new QueryCursorPreparer(query, null);
|
||||
preparer.prepare(cursor);
|
||||
|
||||
verify(cursor).hint("hint");
|
||||
|
||||
@@ -179,4 +179,25 @@ public class AggregationUnitTests {
|
||||
DBObject fields = getAsDBObject(secondProjection, "$group");
|
||||
assertThat(fields.get("foosum"), is((Object) new BasicDBObject("$sum", "$foo")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-908
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportReferingToNestedPropertiesInGroupOperation() {
|
||||
|
||||
DBObject agg = newAggregation( //
|
||||
project("cmsParameterId", "rules"), //
|
||||
unwind("rules"), //
|
||||
group("cmsParameterId", "rules.ruleType").count().as("totol") //
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, is(notNullValue()));
|
||||
|
||||
DBObject group = ((List<DBObject>) agg.get("pipeline")).get(2);
|
||||
DBObject fields = getAsDBObject(group, "$group");
|
||||
DBObject id = getAsDBObject(fields, "_id");
|
||||
|
||||
assertThat(id.get("ruleType"), is((Object) "$rules.ruleType"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.net.URL;
|
||||
import java.text.DateFormat;
|
||||
import java.text.Format;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -183,6 +184,19 @@ public class CustomConversionsUnitTests {
|
||||
assertThat(conversions.getCustomWriteTarget(DateTime.class, null), is(equalTo((Class) String.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-881
|
||||
*/
|
||||
@Test
|
||||
public void customConverterOverridesDefault() {
|
||||
|
||||
CustomConversions conversions = new CustomConversions(Arrays.asList(CustomDateTimeConverter.INSTANCE));
|
||||
GenericConversionService conversionService = new DefaultConversionService();
|
||||
conversions.registerConvertersIn(conversionService);
|
||||
|
||||
assertThat(conversionService.convert(new DateTime(), Date.class), is(new Date(0)));
|
||||
}
|
||||
|
||||
enum FormatToStringConverter implements Converter<Format, String> {
|
||||
INSTANCE;
|
||||
|
||||
@@ -227,4 +241,14 @@ public class CustomConversionsUnitTests {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
enum CustomDateTimeConverter implements Converter<DateTime, Date> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Date convert(DateTime source) {
|
||||
return new Date(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2014 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.
|
||||
@@ -50,7 +50,10 @@ import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
|
||||
/**
|
||||
* Unit tests dor {@link DbRefMappingMongoConverterUnitTests}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DbRefMappingMongoConverterUnitTests {
|
||||
@@ -283,6 +286,149 @@ public class DbRefMappingMongoConverterUnitTests {
|
||||
assertThat(deserializedResult.dbRefToSerializableTarget.getValue(), is(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-884
|
||||
*/
|
||||
@Test
|
||||
public void lazyLoadingProxyForToStringObjectMethodOverridingDbref() {
|
||||
|
||||
String id = "42";
|
||||
String value = "bubu";
|
||||
MappingMongoConverter converterSpy = spy(converter);
|
||||
doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any());
|
||||
|
||||
BasicDBObject dbo = new BasicDBObject();
|
||||
WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs();
|
||||
lazyDbRefs.dbRefToToStringObjectMethodOverride = new ToStringObjectMethodOverrideLazyDbRefTarget(id, value);
|
||||
converterSpy.write(lazyDbRefs, dbo);
|
||||
|
||||
WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, dbo);
|
||||
|
||||
assertThat(result.dbRefToToStringObjectMethodOverride, is(notNullValue()));
|
||||
assertProxyIsResolved(result.dbRefToToStringObjectMethodOverride, false);
|
||||
assertThat(result.dbRefToToStringObjectMethodOverride.toString(), is(id + ":" + value));
|
||||
assertProxyIsResolved(result.dbRefToToStringObjectMethodOverride, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-884
|
||||
*/
|
||||
@Test
|
||||
public void callingToStringObjectMethodOnLazyLoadingDbrefShouldNotInitializeProxy() {
|
||||
|
||||
String id = "42";
|
||||
String value = "bubu";
|
||||
MappingMongoConverter converterSpy = spy(converter);
|
||||
doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any());
|
||||
|
||||
BasicDBObject dbo = new BasicDBObject();
|
||||
WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs();
|
||||
lazyDbRefs.dbRefToPlainObject = new LazyDbRefTarget(id, value);
|
||||
converterSpy.write(lazyDbRefs, dbo);
|
||||
|
||||
WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, dbo);
|
||||
|
||||
assertThat(result.dbRefToPlainObject, is(notNullValue()));
|
||||
assertProxyIsResolved(result.dbRefToPlainObject, false);
|
||||
|
||||
// calling Object#toString does not initialize the proxy.
|
||||
String proxyString = result.dbRefToPlainObject.toString();
|
||||
assertThat(proxyString, is("lazyDbRefTarget" + ":" + id + "$LazyLoadingProxy"));
|
||||
assertProxyIsResolved(result.dbRefToPlainObject, false);
|
||||
|
||||
// calling another method not declared on object triggers proxy initialization.
|
||||
assertThat(result.dbRefToPlainObject.getValue(), is(value));
|
||||
assertProxyIsResolved(result.dbRefToPlainObject, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-884
|
||||
*/
|
||||
@Test
|
||||
public void equalsObjectMethodOnLazyLoadingDbrefShouldNotInitializeProxy() {
|
||||
|
||||
String id = "42";
|
||||
String value = "bubu";
|
||||
MappingMongoConverter converterSpy = spy(converter);
|
||||
doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any());
|
||||
|
||||
BasicDBObject dbo = new BasicDBObject();
|
||||
WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs();
|
||||
lazyDbRefs.dbRefToPlainObject = new LazyDbRefTarget(id, value);
|
||||
lazyDbRefs.dbRefToToStringObjectMethodOverride = new ToStringObjectMethodOverrideLazyDbRefTarget(id, value);
|
||||
converterSpy.write(lazyDbRefs, dbo);
|
||||
|
||||
WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, dbo);
|
||||
|
||||
assertThat(result.dbRefToPlainObject, is(notNullValue()));
|
||||
assertProxyIsResolved(result.dbRefToPlainObject, false);
|
||||
|
||||
assertThat(result.dbRefToPlainObject, is(equalTo(result.dbRefToPlainObject)));
|
||||
assertThat(result.dbRefToPlainObject, is(not(equalTo(null))));
|
||||
assertThat(result.dbRefToPlainObject, is(not(equalTo((Object) lazyDbRefs.dbRefToToStringObjectMethodOverride))));
|
||||
|
||||
assertProxyIsResolved(result.dbRefToPlainObject, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-884
|
||||
*/
|
||||
@Test
|
||||
public void hashcodeObjectMethodOnLazyLoadingDbrefShouldNotInitializeProxy() {
|
||||
|
||||
String id = "42";
|
||||
String value = "bubu";
|
||||
MappingMongoConverter converterSpy = spy(converter);
|
||||
doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any());
|
||||
|
||||
BasicDBObject dbo = new BasicDBObject();
|
||||
WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs();
|
||||
lazyDbRefs.dbRefToPlainObject = new LazyDbRefTarget(id, value);
|
||||
lazyDbRefs.dbRefToToStringObjectMethodOverride = new ToStringObjectMethodOverrideLazyDbRefTarget(id, value);
|
||||
converterSpy.write(lazyDbRefs, dbo);
|
||||
|
||||
WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, dbo);
|
||||
|
||||
assertThat(result.dbRefToPlainObject, is(notNullValue()));
|
||||
assertProxyIsResolved(result.dbRefToPlainObject, false);
|
||||
|
||||
assertThat(result.dbRefToPlainObject.hashCode(), is(311365444));
|
||||
|
||||
assertProxyIsResolved(result.dbRefToPlainObject, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-884
|
||||
*/
|
||||
@Test
|
||||
public void lazyLoadingProxyForEqualsAndHashcodeObjectMethodOverridingDbref() {
|
||||
|
||||
String id = "42";
|
||||
String value = "bubu";
|
||||
MappingMongoConverter converterSpy = spy(converter);
|
||||
doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any());
|
||||
|
||||
BasicDBObject dbo = new BasicDBObject();
|
||||
WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs();
|
||||
lazyDbRefs.dbRefEqualsAndHashcodeObjectMethodOverride1 = new EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget(
|
||||
id, value);
|
||||
lazyDbRefs.dbRefEqualsAndHashcodeObjectMethodOverride2 = new EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget(
|
||||
id, value);
|
||||
converterSpy.write(lazyDbRefs, dbo);
|
||||
|
||||
WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, dbo);
|
||||
|
||||
assertProxyIsResolved(result.dbRefEqualsAndHashcodeObjectMethodOverride1, false);
|
||||
assertThat(result.dbRefEqualsAndHashcodeObjectMethodOverride1, is(notNullValue()));
|
||||
result.dbRefEqualsAndHashcodeObjectMethodOverride1.equals(null);
|
||||
assertProxyIsResolved(result.dbRefEqualsAndHashcodeObjectMethodOverride1, true);
|
||||
|
||||
assertProxyIsResolved(result.dbRefEqualsAndHashcodeObjectMethodOverride2, false);
|
||||
assertThat(result.dbRefEqualsAndHashcodeObjectMethodOverride2, is(notNullValue()));
|
||||
result.dbRefEqualsAndHashcodeObjectMethodOverride2.hashCode();
|
||||
assertProxyIsResolved(result.dbRefEqualsAndHashcodeObjectMethodOverride2, true);
|
||||
}
|
||||
|
||||
private Object transport(Object result) {
|
||||
return SerializationUtils.deserialize(SerializationUtils.serialize(result));
|
||||
}
|
||||
@@ -345,6 +491,7 @@ public class DbRefMappingMongoConverterUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
static class LazyDbRefTargetWithPeristenceConstructor extends LazyDbRefTarget {
|
||||
|
||||
boolean persistenceConstructorCalled;
|
||||
@@ -362,6 +509,7 @@ public class DbRefMappingMongoConverterUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
static class LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor extends LazyDbRefTarget {
|
||||
|
||||
boolean persistenceConstructorCalled;
|
||||
@@ -387,4 +535,74 @@ public class DbRefMappingMongoConverterUnitTests {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
static class ToStringObjectMethodOverrideLazyDbRefTarget extends LazyDbRefTarget {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ToStringObjectMethodOverrideLazyDbRefTarget() {}
|
||||
|
||||
public ToStringObjectMethodOverrideLazyDbRefTarget(String id, String value) {
|
||||
super(id, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.id + ":" + this.value;
|
||||
}
|
||||
}
|
||||
|
||||
static class EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget extends LazyDbRefTarget {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget() {}
|
||||
|
||||
public EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget(String id, String value) {
|
||||
super(id, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
||||
result = prime * result + ((value == null) ? 0 : value.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget other = (EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null)
|
||||
return false;
|
||||
} else if (!id.equals(other.id))
|
||||
return false;
|
||||
if (value == null) {
|
||||
if (other.value != null)
|
||||
return false;
|
||||
} else if (!value.equals(other.value))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class WithObjectMethodOverrideLazyDbRefs {
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) LazyDbRefTarget dbRefToPlainObject;
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) ToStringObjectMethodOverrideLazyDbRefTarget dbRefToToStringObjectMethodOverride;
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget dbRefEqualsAndHashcodeObjectMethodOverride2;
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget dbRefEqualsAndHashcodeObjectMethodOverride1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2014 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.
|
||||
@@ -21,6 +21,7 @@ import static org.junit.Assert.*;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.cglib.proxy.Factory;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.LazyLoadingInterceptor;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Utility class to test proxy handling for lazy loading.
|
||||
@@ -39,8 +40,8 @@ public class LazyLoadingTestUtils {
|
||||
public static void assertProxyIsResolved(Object target, boolean expected) {
|
||||
|
||||
LazyLoadingInterceptor interceptor = extractInterceptor(target);
|
||||
assertThat(interceptor.isResolved(), is(expected));
|
||||
assertThat(interceptor.getResult(), is(expected ? notNullValue() : nullValue()));
|
||||
assertThat(ReflectionTestUtils.getField(interceptor, "resolved"), is((Object) expected));
|
||||
assertThat(ReflectionTestUtils.getField(interceptor, "result"), is(expected ? notNullValue() : nullValue()));
|
||||
}
|
||||
|
||||
private static LazyLoadingInterceptor extractInterceptor(Object proxy) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.hamcrest.Matcher;
|
||||
@@ -1389,6 +1390,7 @@ public class MappingMongoConverterUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
* @see DATAMONGO-893
|
||||
*/
|
||||
@Test
|
||||
public void convertsListToBasicDBListAndRetainsTypeInformationForComplexObjects() {
|
||||
@@ -1398,7 +1400,7 @@ public class MappingMongoConverterUnitTests {
|
||||
address.street = "Foo";
|
||||
|
||||
Object result = converter.convertToMongoType(Collections.singletonList(address),
|
||||
ClassTypeInformation.from(Address.class));
|
||||
ClassTypeInformation.from(InterfaceType.class));
|
||||
|
||||
assertThat(result, is(instanceOf(BasicDBList.class)));
|
||||
|
||||
@@ -1487,6 +1489,45 @@ public class MappingMongoConverterUnitTests {
|
||||
assertThat(result.enumMap.get(SampleEnum.FIRST), is("Dave"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-887
|
||||
*/
|
||||
@Test
|
||||
public void readsTreeMapCorrectly() {
|
||||
|
||||
DBObject person = new BasicDBObject("foo", "Dave");
|
||||
DBObject treeMapOfPerson = new BasicDBObject("key", person);
|
||||
DBObject document = new BasicDBObject("treeMapOfPersons", treeMapOfPerson);
|
||||
|
||||
ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, document);
|
||||
|
||||
assertThat(result.treeMapOfPersons, is(notNullValue()));
|
||||
assertThat(result.treeMapOfPersons.get("key"), is(notNullValue()));
|
||||
assertThat(result.treeMapOfPersons.get("key").firstname, is("Dave"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-887
|
||||
*/
|
||||
@Test
|
||||
public void writesTreeMapCorrectly() {
|
||||
|
||||
Person person = new Person();
|
||||
person.firstname = "Dave";
|
||||
|
||||
ClassWithMapProperty source = new ClassWithMapProperty();
|
||||
source.treeMapOfPersons = new TreeMap<String, Person>();
|
||||
source.treeMapOfPersons.put("key", person);
|
||||
|
||||
DBObject result = new BasicDBObject();
|
||||
|
||||
converter.write(source, result);
|
||||
|
||||
DBObject map = getAsDBObject(result, "treeMapOfPersons");
|
||||
DBObject entry = getAsDBObject(map, "key");
|
||||
assertThat(entry.get("foo"), is((Object) "Dave"));
|
||||
}
|
||||
|
||||
static class GenericType<T> {
|
||||
T content;
|
||||
}
|
||||
@@ -1514,7 +1555,11 @@ public class MappingMongoConverterUnitTests {
|
||||
abstract void method();
|
||||
}
|
||||
|
||||
static class Address {
|
||||
static interface InterfaceType {
|
||||
|
||||
}
|
||||
|
||||
static class Address implements InterfaceType {
|
||||
String street;
|
||||
String city;
|
||||
}
|
||||
@@ -1554,6 +1599,7 @@ public class MappingMongoConverterUnitTests {
|
||||
Map<String, Object> mapOfObjects;
|
||||
Map<String, String[]> mapOfStrings;
|
||||
Map<String, Person> mapOfPersons;
|
||||
TreeMap<String, Person> treeMapOfPersons;
|
||||
}
|
||||
|
||||
static class ClassWithNestedMaps {
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.springframework.data.mongodb.core.DBObjectTestUtils;
|
||||
import org.springframework.data.mongodb.core.Person;
|
||||
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.DBRef;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
@@ -568,10 +569,42 @@ public class QueryMapperUnitTests {
|
||||
assertThat(mappedFields, is(notNullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-893
|
||||
*/
|
||||
@Test
|
||||
public void classInformationShouldNotBePresentInDBObjectUsedInFinderMethods() {
|
||||
|
||||
EmbeddedClass embedded = new EmbeddedClass();
|
||||
embedded.id = "1";
|
||||
|
||||
EmbeddedClass embedded2 = new EmbeddedClass();
|
||||
embedded2.id = "2";
|
||||
Query query = query(where("embedded").in(Arrays.asList(embedded, embedded2)));
|
||||
|
||||
DBObject dbo = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Foo.class));
|
||||
assertThat(dbo.toString(), equalTo("{ \"embedded\" : { \"$in\" : [ { \"_id\" : \"1\"} , { \"_id\" : \"2\"}]}}"));
|
||||
}
|
||||
|
||||
@Document
|
||||
public class Foo {
|
||||
@Id private ObjectId id;
|
||||
EmbeddedClass embedded;
|
||||
}
|
||||
|
||||
public class EmbeddedClass {
|
||||
public String id;
|
||||
}
|
||||
|
||||
class IdWrapper {
|
||||
Object id;
|
||||
}
|
||||
|
||||
class ClassWithEmbedded {
|
||||
@Id String id;
|
||||
Sample sample;
|
||||
}
|
||||
|
||||
class ClassWithDefaultId {
|
||||
|
||||
String id;
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.List;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.collection.IsIterableContainingInOrder;
|
||||
import org.hamcrest.core.IsEqual;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -37,6 +38,7 @@ import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.DBObjectTestUtils;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
@@ -412,6 +414,82 @@ public class UpdateMapperUnitTests {
|
||||
assertThat(inClause, IsIterableContainingInOrder.<Object> contains(1L, 2L));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-897
|
||||
*/
|
||||
@Test
|
||||
public void updateOnDbrefPropertyOfInterfaceTypeWithoutExplicitGetterForIdShouldBeMappedCorrectly() {
|
||||
|
||||
Update update = new Update().set("referencedDocument", new InterfaceDocumentDefinitionImpl("1", "Foo"));
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(DocumentWithReferenceToInterfaceImpl.class));
|
||||
|
||||
DBObject $set = DBObjectTestUtils.getAsDBObject(mappedObject, "$set");
|
||||
Object model = $set.get("referencedDocument");
|
||||
|
||||
DBRef expectedDBRef = new DBRef(factory.getDb(), "interfaceDocumentDefinitionImpl", "1");
|
||||
assertThat(model, allOf(instanceOf(DBRef.class), IsEqual.<Object> equalTo(expectedDBRef)));
|
||||
}
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.Document(collection = "DocumentWithReferenceToInterface")
|
||||
static interface DocumentWithReferenceToInterface {
|
||||
|
||||
String getId();
|
||||
|
||||
InterfaceDocumentDefinitionWithoutId getReferencedDocument();
|
||||
|
||||
}
|
||||
|
||||
static interface InterfaceDocumentDefinitionWithoutId {
|
||||
|
||||
String getValue();
|
||||
}
|
||||
|
||||
static class InterfaceDocumentDefinitionImpl implements InterfaceDocumentDefinitionWithoutId {
|
||||
|
||||
@Id String id;
|
||||
String value;
|
||||
|
||||
public InterfaceDocumentDefinitionImpl(String id, String value) {
|
||||
|
||||
this.id = id;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class DocumentWithReferenceToInterfaceImpl implements DocumentWithReferenceToInterface {
|
||||
|
||||
private @Id String id;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
private InterfaceDocumentDefinitionWithoutId referencedDocument;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setModel(InterfaceDocumentDefinitionWithoutId referencedDocument) {
|
||||
this.referencedDocument = referencedDocument;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterfaceDocumentDefinitionWithoutId getReferencedDocument() {
|
||||
return this.referencedDocument;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static interface Model {}
|
||||
|
||||
static class ModelImpl implements Model {
|
||||
|
||||
@@ -739,7 +739,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
assertThat(result.size(), is(1));
|
||||
assertThat(result.get(0), is(dave));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-871
|
||||
*/
|
||||
@@ -751,8 +751,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
assertThat(result, is(arrayWithSize(1)));
|
||||
assertThat(result, is(arrayContaining(leroi)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-821
|
||||
*/
|
||||
@@ -772,4 +771,21 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
assertThat(result.getNumberOfElements(), is(1));
|
||||
assertThat(result.getContent().get(0), is(alicia));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-893
|
||||
*/
|
||||
@Test
|
||||
public void findByNestedPropertyInCollectionShouldFindMatchingDocuments() {
|
||||
|
||||
Person p = new Person("Mary", "Poppins");
|
||||
Address adr = new Address("some", "2", "where");
|
||||
p.setAddress(adr);
|
||||
|
||||
repository.save(p);
|
||||
|
||||
Page<Person> result = repository.findByAddressIn(Arrays.asList(adr), new PageRequest(0, 10));
|
||||
|
||||
assertThat(result.getContent(), hasSize(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,10 +251,15 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
* @see DATAMONGO-770
|
||||
*/
|
||||
List<Person> findByFirstnameContainingIgnoreCase(String firstName);
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-821
|
||||
*/
|
||||
@Query("{ creator : { $exists : true } }")
|
||||
Page<Person> findByHavingCreator(Pageable page);
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-893
|
||||
*/
|
||||
Page<Person> findByAddressIn(List<Address> address, Pageable page);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<mongo:db-factory id="factory" />
|
||||
|
||||
<bean class="org.springframework.data.mongodb.core.MongoTemplate">
|
||||
<constructor-arg>
|
||||
<mongo:mapping-converter />
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -68,7 +68,7 @@
|
||||
<xi:include href="introduction/introduction.xml"/>
|
||||
<xi:include href="introduction/requirements.xml"/>
|
||||
<xi:include href="introduction/getting-started.xml"/>
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.1.RELEASE/src/docbkx/repositories.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.2.RELEASE/src/docbkx/repositories.xml">
|
||||
<xi:fallback href="../../../spring-data-commons/src/docbkx/repositories.xml" />
|
||||
</xi:include>
|
||||
</part>
|
||||
@@ -88,10 +88,10 @@
|
||||
<part id="appendix">
|
||||
<title>Appendix</title>
|
||||
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.1.RELEASE/src/docbkx/repository-namespace-reference.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.2.RELEASE/src/docbkx/repository-namespace-reference.xml">
|
||||
<xi:fallback href="../../../spring-data-commons/src/docbkx/repository-namespace-reference.xml" />
|
||||
</xi:include>
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.1.RELEASE/src/docbkx/repository-query-keywords-reference.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.2.RELEASE/src/docbkx/repository-query-keywords-reference.xml">
|
||||
<xi:fallback href="../../../spring-data-commons/src/docbkx/repository-query-keywords-reference.xml" />
|
||||
</xi:include>
|
||||
</part>
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.4.1.RELEASE</version>
|
||||
<version>1.4.2.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies></programlisting>
|
||||
|
||||
@@ -1,6 +1,67 @@
|
||||
Spring Data MongoDB Changelog
|
||||
=============================
|
||||
|
||||
Changes in version 1.4.2.RELEASE (2014-04-15)
|
||||
---------------------------------------------
|
||||
|
||||
** Fix
|
||||
* [DATAMONGO-880] - Improved handling of persistence of lazy-loaded DBRefs.
|
||||
* [DATAMONGO-884] - Improved handling for Object methods in LazyLoadingInterceptor.
|
||||
* [DATAMONGO-887] - Added unit tests to verify TreeMaps can be converted.
|
||||
* [DATAMONGO-888] - Sorting now considers mapping information.
|
||||
* [DATAMONGO-890] - Fixed Point.toString().
|
||||
* [DATAMONGO-892] - Reject nested MappingMongoConverter declarations in XML.
|
||||
* [DATAMONGO-893] - Converter must not write "_class" information for know types.
|
||||
* [DATAMONGO-897] - Fixed potential NullPointerException in QueryMapper.
|
||||
* [DATAMONGO-908] - Support for nested field references in group operations.
|
||||
|
||||
** Improvement
|
||||
* [DATAMONGO-881] - Allow custom conversions to override default conversions.
|
||||
|
||||
** Task
|
||||
* [DATAMONGO-895] - Use most specific type for checks against values in DBObjects.
|
||||
* [DATAMONGO-896] - Assert compatibility with latest MongoDB Java driver.
|
||||
* [DATAMONGO-905] - Removed obsolete dependency to CGLib from cross-store support.
|
||||
* [DATAMONGO-907] - Assert compatibility with mongodb 2.6.
|
||||
* [DATAMONGO-911] - Release 1.4.2
|
||||
|
||||
|
||||
Changes in version 1.5.0.M1 (2014-03-31)
|
||||
----------------------------------------
|
||||
** Fix
|
||||
* [DATAMONGO-471] - Update operation $addToSet does not support adding a list with $each.
|
||||
* [DATAMONGO-773] - Spring Data MongoDB projection search on @DBref fields.
|
||||
* [DATAMONGO-821] - MappingException for $size queries on subcollections containing dbrefs.
|
||||
* [DATAMONGO-829] - NearQuery, when used in conjunction with a Query, it sets num=0, unless Query specifies otherwise.
|
||||
* [DATAMONGO-833] - EnumSet is not handled correctly.
|
||||
* [DATAMONGO-843] - Unable to use @EnableMongoAuditing annotation in Java config.
|
||||
* [DATAMONGO-862] - Update Array Field Using Positional Operator ($) Does Not Work.
|
||||
* [DATAMONGO-863] - QueryMapper.getMappedValue Fails To Handle Arrays Mapped To $in.
|
||||
* [DATAMONGO-868] - findAndModify method does not increment @Version field.
|
||||
* [DATAMONGO-871] - Declarative query method with array return type causes NPE.
|
||||
* [DATAMONGO-877] - AbstractMongoConfiguration.getMappingBasePackage() throws NullPointerException if config class resides in default package.
|
||||
* [DATAMONGO-880] - Error when trying to persist an object containing a DBRef which was lazy loaded.
|
||||
* [DATAMONGO-884] - Potential NullPointerException for lazy DBRefs.
|
||||
* [DATAMONGO-887] - Repository not instantiated when entity contains field of type TreeMap.
|
||||
* [DATAMONGO-890] - Point class toString method is confusing.
|
||||
|
||||
** Improvement
|
||||
* [DATAMONGO-809] - Make filename optional in GridFsOperations doc and GridFsTemplate implementation.
|
||||
* [DATAMONGO-858] - Add support for common geospatial structures.
|
||||
* [DATAMONGO-865] - Adjust test dependencies to avoid ClassNotFoundException during test runs.
|
||||
* [DATAMONGO-881] - Cannot override default converters in CustomConversions.
|
||||
* [DATAMONGO-882] - Adapt to removal of obsolete generics in BeanWrapper.
|
||||
|
||||
** New Feature
|
||||
* [DATAMONGO-566] - Provide support for removeBy… / deleteBy… methods like for findBy… on repository interfaces.
|
||||
* [DATAMONGO-870] - Add support for sliced query method execution.
|
||||
|
||||
** Task
|
||||
* [DATAMONGO-876] - Adapt to changes introduced for property access configuration.
|
||||
* [DATAMONGO-883] - Update auditing configuration to enable auditing annotations on accessors.
|
||||
* [DATAMONGO-859] - Release 1.5 M1.
|
||||
|
||||
|
||||
Changes in version 1.4.1.RELEASE (2014-03-13)
|
||||
---------------------------------------------
|
||||
** Fix
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data MongoDB 1.4.1.RELEASE
|
||||
Spring Data MongoDB 1.4.2.RELEASE
|
||||
Copyright (c) [2010-2014] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SPRING DATA MongoDB 1.4.1.RELEASE
|
||||
Spring Data MongoDB 1.4.2.RELEASE
|
||||
---------------------------------
|
||||
|
||||
Spring Data MongoDB is released under the terms of the Apache Software License Version 2.0 (see license.txt).
|
||||
|
||||
Reference in New Issue
Block a user