Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ca297df50 | ||
|
|
22078d9bb0 | ||
|
|
adfa7cdd88 | ||
|
|
88deac4ca7 | ||
|
|
0275e6fb4c | ||
|
|
291b89a8b6 | ||
|
|
fe9d086334 | ||
|
|
cf4db90a7f | ||
|
|
dc67830c6f | ||
|
|
33004f217d | ||
|
|
72adb309ee | ||
|
|
2e493ea8c5 | ||
|
|
f0fc3961d2 | ||
|
|
f80fd92692 | ||
|
|
87ef66cb12 | ||
|
|
332d9d95f4 | ||
|
|
c18d6381aa | ||
|
|
53697179c3 | ||
|
|
7b3acb2890 | ||
|
|
d1ac323e4a | ||
|
|
b1068687bb | ||
|
|
6eae6d3e2c | ||
|
|
abfb98afe1 | ||
|
|
f361368893 | ||
|
|
063438002b | ||
|
|
9b54a5cd39 | ||
|
|
14360f2ab4 | ||
|
|
81c368c851 | ||
|
|
cf3818e04c | ||
|
|
da9870504f | ||
|
|
1285f4f26e | ||
|
|
791938f05d | ||
|
|
1b2d98dd3d | ||
|
|
de364c65ab | ||
|
|
57a74b0427 | ||
|
|
f35df8fe69 | ||
|
|
2d3aac1826 | ||
|
|
15db4ba6ea | ||
|
|
f02ac5ea44 | ||
|
|
86633e01db | ||
|
|
5fe3763f9c | ||
|
|
d1e2b143f3 | ||
|
|
61ab232bc1 | ||
|
|
443cde6236 | ||
|
|
b23796fb45 | ||
|
|
605f7459f7 | ||
|
|
ef6db5970b | ||
|
|
47a5a32713 | ||
|
|
1675528fc7 | ||
|
|
3455cbc634 | ||
|
|
ed779e52b7 | ||
|
|
c70898b019 |
@@ -26,7 +26,7 @@ Add the Maven dependency:
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.4.0.RELEASE</version>
|
||||
<version>1.4.2.RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
20
pom.xml
20
pom.xml
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.4.0.RELEASE</version>
|
||||
<version>1.4.4.BUILD-SNAPSHOT</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.0.RELEASE</version>
|
||||
<version>1.3.4.BUILD-SNAPSHOT</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.0.RELEASE</springdata.commons>
|
||||
<springdata.commons>1.7.4.BUILD-SNAPSHOT</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>
|
||||
@@ -124,10 +124,14 @@
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-release</id>
|
||||
<url>http://repo.spring.io/libs-release/</url>
|
||||
<id>spring-libs-snapshopt</id>
|
||||
<url>http://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<url>http://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.4.0.RELEASE</version>
|
||||
<version>1.4.4.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.4.0.RELEASE</version>
|
||||
<version>1.4.4.BUILD-SNAPSHOT</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.0.RELEASE</version>
|
||||
<version>1.4.4.BUILD-SNAPSHOT</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.0.RELEASE</version>
|
||||
<version>1.4.4.BUILD-SNAPSHOT</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.0.RELEASE</version>
|
||||
<version>1.4.4.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -137,6 +137,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
<version>${slf4j}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -116,7 +116,9 @@ public abstract class AbstractMongoConfiguration {
|
||||
* entities.
|
||||
*/
|
||||
protected String getMappingBasePackage() {
|
||||
return getClass().getPackage().getName();
|
||||
|
||||
Package mappingBasePackage = getClass().getPackage();
|
||||
return mappingBasePackage == null ? null : mappingBasePackage.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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,17 +15,24 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.*;
|
||||
import static org.springframework.data.mongodb.config.BeanNames.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
|
||||
import org.springframework.data.auditing.config.AnnotationAuditingConfiguration;
|
||||
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
|
||||
import org.springframework.data.config.ParsingUtils;
|
||||
import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener;
|
||||
import org.springframework.data.support.IsNewStrategyFactory;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -57,7 +64,7 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
|
||||
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
|
||||
|
||||
registerIsNewStrategyFactoryIfNecessary(registry);
|
||||
defaultDependenciesIfNecessary(registry, annotationMetadata);
|
||||
super.registerBeanDefinitions(annotationMetadata, registry);
|
||||
}
|
||||
|
||||
@@ -92,14 +99,33 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param registry, the {@link BeanDefinitionRegistry} to use to register an {@link IsNewStrategyFactory} to.
|
||||
* Register default bean definitions for a {@link MongoMappingContext} and an {@link IsNewStrategyFactory} in case we
|
||||
* don't find beans with the assumed names in the registry.
|
||||
*
|
||||
* @param registry the {@link BeanDefinitionRegistry} to use to register the components into.
|
||||
* @param source the source which the registered components shall be registered with
|
||||
*/
|
||||
private void registerIsNewStrategyFactoryIfNecessary(BeanDefinitionRegistry registry) {
|
||||
private void defaultDependenciesIfNecessary(BeanDefinitionRegistry registry, Object source) {
|
||||
|
||||
if (!registry.containsBeanDefinition(BeanNames.IS_NEW_STRATEGY_FACTORY)) {
|
||||
registry.registerBeanDefinition(BeanNames.IS_NEW_STRATEGY_FACTORY,
|
||||
BeanDefinitionBuilder.rootBeanDefinition(MappingContextIsNewStrategyFactory.class)
|
||||
.addConstructorArgReference(BeanNames.MAPPING_CONTEXT).getBeanDefinition());
|
||||
if (!registry.containsBeanDefinition(MAPPING_CONTEXT)) {
|
||||
|
||||
RootBeanDefinition definition = new RootBeanDefinition(MongoMappingContext.class);
|
||||
definition.setRole(ROLE_INFRASTRUCTURE);
|
||||
definition.setSource(source);
|
||||
|
||||
registry.registerBeanDefinition(MAPPING_CONTEXT, definition);
|
||||
}
|
||||
|
||||
if (!registry.containsBeanDefinition(IS_NEW_STRATEGY_FACTORY)) {
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(MappingContextIsNewStrategyFactory.class);
|
||||
builder.addConstructorArgReference(MAPPING_CONTEXT);
|
||||
|
||||
AbstractBeanDefinition definition = ParsingUtils.getSourceBeanDefinition(builder, source);
|
||||
definition.setRole(ROLE_INFRASTRUCTURE);
|
||||
|
||||
registry.registerBeanDefinition(IS_NEW_STRATEGY_FACTORY, definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -1370,13 +1371,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
"Can not use skip or field specification with map reduce operations");
|
||||
}
|
||||
if (query.getQueryObject() != null) {
|
||||
copyMapReduceOptions.put("query", query.getQueryObject());
|
||||
copyMapReduceOptions.put("query", queryMapper.getMappedObject(query.getQueryObject(), null));
|
||||
}
|
||||
if (query.getLimit() > 0) {
|
||||
copyMapReduceOptions.put("limit", query.getLimit());
|
||||
}
|
||||
if (query.getSortObject() != null) {
|
||||
copyMapReduceOptions.put("sort", query.getSortObject());
|
||||
copyMapReduceOptions.put("sort", queryMapper.getMappedObject(query.getSortObject(), null));
|
||||
}
|
||||
}
|
||||
return copyMapReduceOptions;
|
||||
@@ -1573,6 +1574,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
increaseVersionForUpdateIfNecessary(entity, update);
|
||||
|
||||
DBObject mappedQuery = queryMapper.getMappedObject(query, entity);
|
||||
DBObject mappedUpdate = updateMapper.getMappedObject(update.getUpdateObject(), entity);
|
||||
|
||||
@@ -1856,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
|
||||
|
||||
/**
|
||||
@@ -2049,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;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2079,7 +2095,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
cursorToUse = cursorToUse.limit(query.getLimit());
|
||||
}
|
||||
if (query.getSortObject() != null) {
|
||||
cursorToUse = cursorToUse.sort(query.getSortObject());
|
||||
DBObject sortDbo = type != null ? getMappedSortObject(query, type) : query.getSortObject();
|
||||
cursorToUse = cursorToUse.sort(sortDbo);
|
||||
}
|
||||
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.
|
||||
@@ -248,7 +248,7 @@ public class Aggregation {
|
||||
|
||||
if (operation instanceof FieldsExposingAggregationOperation) {
|
||||
FieldsExposingAggregationOperation exposedFieldsOperation = (FieldsExposingAggregationOperation) operation;
|
||||
context = new ExposedFieldsAggregationOperationContext(exposedFieldsOperation.getFields());
|
||||
context = new ExposedFieldsAggregationOperationContext(exposedFieldsOperation.getFields(), rootContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -268,14 +268,21 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
return field.isAliased();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the synthetic
|
||||
*/
|
||||
public boolean isSynthetic() {
|
||||
return synthetic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the field can be referred to using the given name.
|
||||
*
|
||||
* @param input
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public boolean canBeReferredToBy(String input) {
|
||||
return getTarget().equals(input);
|
||||
public boolean canBeReferredToBy(String name) {
|
||||
return getName().equals(name) || getTarget().equals(name);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -340,6 +347,7 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
public FieldReference(ExposedField field) {
|
||||
|
||||
Assert.notNull(field, "ExposedField must not be null!");
|
||||
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -32,16 +32,22 @@ import com.mongodb.DBObject;
|
||||
class ExposedFieldsAggregationOperationContext implements AggregationOperationContext {
|
||||
|
||||
private final ExposedFields exposedFields;
|
||||
private final AggregationOperationContext rootContext;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExposedFieldsAggregationOperationContext} from the given {@link ExposedFields}.
|
||||
* Creates a new {@link ExposedFieldsAggregationOperationContext} from the given {@link ExposedFields}. Uses the given
|
||||
* {@link AggregationOperationContext} to perform a mapping to mongo types if necessary.
|
||||
*
|
||||
* @param exposedFields must not be {@literal null}.
|
||||
* @param rootContext must not be {@literal null}.
|
||||
*/
|
||||
public ExposedFieldsAggregationOperationContext(ExposedFields exposedFields) {
|
||||
public ExposedFieldsAggregationOperationContext(ExposedFields exposedFields, AggregationOperationContext rootContext) {
|
||||
|
||||
Assert.notNull(exposedFields, "ExposedFields must not be null!");
|
||||
Assert.notNull(rootContext, "RootContext must not be null!");
|
||||
|
||||
this.exposedFields = exposedFields;
|
||||
this.rootContext = rootContext;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -50,7 +56,7 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
*/
|
||||
@Override
|
||||
public DBObject getMappedObject(DBObject dbObject) {
|
||||
return dbObject;
|
||||
return rootContext.getMappedObject(dbObject);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -59,7 +65,7 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
*/
|
||||
@Override
|
||||
public FieldReference getReference(Field field) {
|
||||
return getReference(field.getTarget());
|
||||
return getReference(field, field.getTarget());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -68,11 +74,42 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
*/
|
||||
@Override
|
||||
public FieldReference getReference(String name) {
|
||||
return getReference(null, name);
|
||||
}
|
||||
|
||||
ExposedField field = exposedFields.getField(name);
|
||||
/**
|
||||
* Returns a {@link FieldReference} to the given {@link Field} with the given {@code name}.
|
||||
*
|
||||
* @param field may be {@literal null}
|
||||
* @param name must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
private FieldReference getReference(Field field, String name) {
|
||||
|
||||
if (field != null) {
|
||||
return new FieldReference(field);
|
||||
Assert.notNull(name, "Name must not be null!");
|
||||
|
||||
ExposedField exposedField = exposedFields.getField(name);
|
||||
|
||||
if (exposedField != null) {
|
||||
|
||||
if (field != null) {
|
||||
// we return a FieldReference to the given field directly to make sure that we reference the proper alias here.
|
||||
return new FieldReference(new ExposedField(field, exposedField.isSynthetic()));
|
||||
}
|
||||
|
||||
return new FieldReference(exposedField);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -26,11 +28,11 @@ import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.objenesis.Objenesis;
|
||||
import org.objenesis.ObjenesisStd;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.cglib.proxy.Callback;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
import org.springframework.cglib.proxy.Factory;
|
||||
import org.springframework.cglib.proxy.MethodProxy;
|
||||
import org.springframework.core.SpringVersion;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.mongodb.LazyLoadingException;
|
||||
@@ -51,10 +53,10 @@ import com.mongodb.DBRef;
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @since 1.4
|
||||
*/
|
||||
public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
private static final boolean IS_SPRING_4_OR_BETTER = SpringVersion.getVersion().startsWith("4");
|
||||
private static final boolean OBJENESIS_PRESENT = ClassUtils.isPresent("org.objenesis.Objenesis", null);
|
||||
|
||||
private final MongoDbFactory mongoDbFactory;
|
||||
@@ -78,13 +80,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 +111,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 +124,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);
|
||||
@@ -132,7 +137,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
proxyFactory.setProxyTargetClass(true);
|
||||
proxyFactory.setTargetClass(propertyType);
|
||||
|
||||
if (IS_SPRING_4_OR_BETTER || !OBJENESIS_PRESENT) {
|
||||
if (!OBJENESIS_PRESENT) {
|
||||
proxyFactory.addAdvice(interceptor);
|
||||
return proxyFactory.getProxy();
|
||||
}
|
||||
@@ -141,7 +146,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 +161,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 +221,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 +320,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 +349,8 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the proxy into its backing object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private synchronized Object resolve() {
|
||||
@@ -248,24 +370,30 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isResolved() {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static class to accomodate optional dependency on Objenesis.
|
||||
* Static class to accommodate optional dependency on Objenesis.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @since 1.4
|
||||
*/
|
||||
private static class ObjenesisProxyEnhancer {
|
||||
|
||||
private static final Objenesis OBJENESIS = new ObjenesisStd(true);
|
||||
private static final boolean IS_SPRING_4_OR_BETTER = ClassUtils.isPresent(
|
||||
"org.springframework.core.DefaultParameterNameDiscoverer", null);
|
||||
|
||||
private static final InstanceCreatorStrategy INSTANCE_CREATOR;
|
||||
|
||||
static {
|
||||
|
||||
if (IS_SPRING_4_OR_BETTER) {
|
||||
INSTANCE_CREATOR = new Spring4ObjenesisInstanceCreatorStrategy();
|
||||
} else {
|
||||
INSTANCE_CREATOR = new DefaultObjenesisInstanceCreatorStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
public static Object enhanceAndGet(ProxyFactory proxyFactory, Class<?> type,
|
||||
org.springframework.cglib.proxy.MethodInterceptor interceptor) {
|
||||
@@ -273,10 +401,79 @@ 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 factory = (Factory) INSTANCE_CREATOR.newInstance(enhancer.createClass());
|
||||
factory.setCallbacks(new Callback[] { interceptor });
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy for constructing new instances of a given {@link Class}.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
interface InstanceCreatorStrategy {
|
||||
Object newInstance(Class<?> clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link InstanceCreatorStrategy} that uses Objenesis from the classpath.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static class DefaultObjenesisInstanceCreatorStrategy implements InstanceCreatorStrategy {
|
||||
|
||||
private static final Objenesis OBJENESIS = new ObjenesisStd(true);
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.ObjenesisProxyEnhancer.InstanceCreatorStrategy#newInstance(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Object newInstance(Class<?> clazz) {
|
||||
return OBJENESIS.newInstance(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link InstanceCreatorStrategy} that uses a repackaged version of Objenesis from Spring 4.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static class Spring4ObjenesisInstanceCreatorStrategy implements InstanceCreatorStrategy {
|
||||
|
||||
private static final String SPRING4_OBJENESIS_CLASS_NAME = "org.springframework.objenesis.ObjenesisStd";
|
||||
private static final Object OBJENESIS;
|
||||
private static final Method NEW_INSTANCE_METHOD;
|
||||
|
||||
static {
|
||||
|
||||
try {
|
||||
Class<?> objenesisClass = ClassUtils.forName(SPRING4_OBJENESIS_CLASS_NAME,
|
||||
ObjenesisProxyEnhancer.class.getClassLoader());
|
||||
|
||||
OBJENESIS = BeanUtils.instantiateClass(objenesisClass.getConstructor(boolean.class), true);
|
||||
NEW_INSTANCE_METHOD = objenesisClass.getMethod("newInstance", Class.class);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not setup Objenesis infrastructure with Spring 4 ", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.ObjenesisProxyEnhancer.InstanceCreatorStrategy#newInstance(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Object newInstance(Class<?> clazz) {
|
||||
|
||||
try {
|
||||
return NEW_INSTANCE_METHOD.invoke(OBJENESIS, clazz);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not created instance for " + clazz, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -19,6 +19,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -274,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) {
|
||||
@@ -284,7 +288,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
});
|
||||
|
||||
wrapper.setProperty(inverseProp, obj);
|
||||
wrapper.setProperty(property, obj);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -304,6 +308,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
Assert.isTrue(annotation != null, "The referenced property has to be mapped with @DBRef!");
|
||||
}
|
||||
|
||||
// @see DATAMONGO-913
|
||||
if (object instanceof LazyLoadingProxy) {
|
||||
return ((LazyLoadingProxy) object).toDBRef();
|
||||
}
|
||||
|
||||
return createDBRef(object, referingProperty);
|
||||
}
|
||||
|
||||
@@ -401,6 +410,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 {
|
||||
@@ -447,13 +457,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);
|
||||
|
||||
@@ -794,7 +823,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
* @param sourceValue must not be {@literal null}.
|
||||
* @return the converted {@link Collection} or array, will never be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings({ "unchecked", "null" })
|
||||
private Object readCollectionOrArray(TypeInformation<?> targetType, BasicDBList sourceValue, Object parent) {
|
||||
|
||||
Assert.notNull(targetType);
|
||||
@@ -807,11 +836,20 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
|
||||
|
||||
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>() : CollectionFactory
|
||||
.createCollection(collectionType, sourceValue.size());
|
||||
TypeInformation<?> componentType = targetType.getComponentType();
|
||||
Class<?> rawComponentType = componentType == null ? null : componentType.getType();
|
||||
|
||||
Collection<Object> items;
|
||||
|
||||
if (targetType.getType().isArray()) {
|
||||
items = new ArrayList<Object>();
|
||||
} else if (EnumSet.class.isAssignableFrom(collectionType)) {
|
||||
Assert.notNull(rawComponentType, "Component type must not be null for enum sets!");
|
||||
items = EnumSet.noneOf(rawComponentType.asSubclass(Enum.class));
|
||||
} else {
|
||||
items = CollectionFactory.createCollection(collectionType, sourceValue.size());
|
||||
}
|
||||
|
||||
for (int i = 0; i < sourceValue.size(); i++) {
|
||||
|
||||
Object dbObjItem = sourceValue.get(i);
|
||||
@@ -836,31 +874,43 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
* @param dbObject
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings({ "unchecked", "null", "rawtypes" })
|
||||
protected Map<Object, Object> readMap(TypeInformation<?> type, DBObject dbObject, Object parent) {
|
||||
|
||||
Assert.notNull(dbObject);
|
||||
|
||||
Class<?> mapType = typeMapper.readType(dbObject, type).getType();
|
||||
Map<Object, Object> map = CollectionFactory.createMap(mapType, dbObject.keySet().size());
|
||||
|
||||
TypeInformation<?> keyType = type.getComponentType();
|
||||
Class<?> rawKeyType = keyType == null ? null : keyType.getType();
|
||||
|
||||
TypeInformation<?> valueType = type.getMapValueType();
|
||||
Class<?> rawValueType = valueType == null ? null : valueType.getType();
|
||||
|
||||
Map<Object, Object> map;
|
||||
|
||||
if (EnumMap.class.isAssignableFrom(mapType)) {
|
||||
Assert.notNull(keyType, "Key type must nut be null for enum maps!");
|
||||
map = new EnumMap(rawKeyType.asSubclass(Enum.class));
|
||||
} else {
|
||||
map = CollectionFactory.createMap(mapType, dbObject.keySet().size());
|
||||
}
|
||||
|
||||
Map<String, Object> sourceMap = dbObject.toMap();
|
||||
|
||||
for (Entry<String, Object> entry : sourceMap.entrySet()) {
|
||||
|
||||
if (typeMapper.isTypeKey(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object key = potentiallyUnescapeMapKey(entry.getKey());
|
||||
|
||||
TypeInformation<?> keyTypeInformation = type.getComponentType();
|
||||
if (keyTypeInformation != null) {
|
||||
Class<?> keyType = keyTypeInformation.getType();
|
||||
key = conversionService.convert(key, keyType);
|
||||
if (rawKeyType != null) {
|
||||
key = conversionService.convert(key, rawKeyType);
|
||||
}
|
||||
|
||||
Object value = entry.getValue();
|
||||
TypeInformation<?> valueType = type.getMapValueType();
|
||||
Class<?> rawValueType = valueType == null ? null : valueType.getType();
|
||||
|
||||
if (value instanceof DBObject) {
|
||||
map.put(key, read(valueType, (DBObject) value, parent));
|
||||
@@ -911,7 +961,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);
|
||||
|
||||
@@ -138,7 +138,7 @@ public class QueryMapper {
|
||||
value = getMappedValue(field, rawValue);
|
||||
}
|
||||
|
||||
return Collections.singletonMap(key, value).entrySet().iterator().next();
|
||||
return createMapEntry(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +168,7 @@ public class QueryMapper {
|
||||
BasicDBList newConditions = new BasicDBList();
|
||||
|
||||
for (Object condition : conditions) {
|
||||
newConditions.add(condition instanceof DBObject ? getMappedObject((DBObject) condition, entity)
|
||||
newConditions.add(isDBObject(condition) ? getMappedObject((DBObject) condition, entity)
|
||||
: convertSimpleOrDBObject(condition, entity));
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ public class QueryMapper {
|
||||
|
||||
if (documentField.isIdField()) {
|
||||
|
||||
if (value instanceof DBObject) {
|
||||
if (isDBObject(value)) {
|
||||
DBObject valueDbo = (DBObject) value;
|
||||
DBObject resultDbo = new BasicDBObject(valueDbo.toMap());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,13 +284,13 @@ public class QueryMapper {
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
private Object convertSimpleOrDBObject(Object source, MongoPersistentEntity<?> entity) {
|
||||
protected Object convertSimpleOrDBObject(Object source, MongoPersistentEntity<?> entity) {
|
||||
|
||||
if (source instanceof BasicDBList) {
|
||||
return delegateConvertToMongoType(source, entity);
|
||||
}
|
||||
|
||||
if (source instanceof DBObject) {
|
||||
if (isDBObject(source)) {
|
||||
return getMappedObject((DBObject) source, entity);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -305,7 +322,7 @@ public class QueryMapper {
|
||||
*/
|
||||
protected Object convertAssociation(Object source, MongoPersistentProperty property) {
|
||||
|
||||
if (property == null || source == null || source instanceof DBRef) {
|
||||
if (property == null || source == null || source instanceof DBRef || source instanceof DBObject) {
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -329,6 +346,40 @@ public class QueryMapper {
|
||||
return createDbRefFor(source, property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given value is a {@link DBObject}.
|
||||
*
|
||||
* @param value can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected final boolean isDBObject(Object value) {
|
||||
return value instanceof DBObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Entry} for the given {@link Field} with the given value.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @param value can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected final Entry<String, Object> createMapEntry(Field field, Object value) {
|
||||
return createMapEntry(field.getMappedKey(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Entry} with the given key and value.
|
||||
*
|
||||
* @param key must not be {@literal null} or empty.
|
||||
* @param value can be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
private Entry<String, Object> createMapEntry(String key, Object value) {
|
||||
|
||||
Assert.hasText(key, "Key must not be null or empty!");
|
||||
return Collections.singletonMap(key, value).entrySet().iterator().next();
|
||||
}
|
||||
|
||||
private DBRef createDbRefFor(Object source, MongoPersistentProperty property) {
|
||||
|
||||
if (source instanceof DBRef) {
|
||||
@@ -560,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);
|
||||
|
||||
@@ -569,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();
|
||||
}
|
||||
|
||||
@@ -579,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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@@ -26,6 +25,7 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update.Modifier;
|
||||
import org.springframework.data.mongodb.core.query.Update.Modifiers;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
@@ -76,10 +76,23 @@ public class UpdateMapper extends QueryMapper {
|
||||
@Override
|
||||
protected Entry<String, Object> getMappedObjectForField(Field field, Object rawValue) {
|
||||
|
||||
if (!isUpdateModifier(rawValue)) {
|
||||
return super.getMappedObjectForField(field, getMappedValue(field, rawValue));
|
||||
if (isDBObject(rawValue)) {
|
||||
return createMapEntry(field, convertSimpleOrDBObject(rawValue, field.getPropertyEntity()));
|
||||
}
|
||||
|
||||
if (isQuery(rawValue)) {
|
||||
return createMapEntry(field,
|
||||
super.getMappedObject(((Query) rawValue).getQueryObject(), field.getPropertyEntity()));
|
||||
}
|
||||
|
||||
if (isUpdateModifier(rawValue)) {
|
||||
return getMappedUpdateModifier(field, rawValue);
|
||||
}
|
||||
|
||||
return super.getMappedObjectForField(field, getMappedValue(field, rawValue));
|
||||
}
|
||||
|
||||
private Entry<String, Object> getMappedUpdateModifier(Field field, Object rawValue) {
|
||||
Object value = null;
|
||||
|
||||
if (rawValue instanceof Modifier) {
|
||||
@@ -96,11 +109,10 @@ public class UpdateMapper extends QueryMapper {
|
||||
|
||||
value = modificationOperations;
|
||||
} else {
|
||||
|
||||
throw new IllegalArgumentException(String.format("Unable to map value of type '%s'!", rawValue.getClass()));
|
||||
}
|
||||
|
||||
return Collections.singletonMap(field.getMappedKey(), value).entrySet().iterator().next();
|
||||
return createMapEntry(field, value);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -116,6 +128,10 @@ public class UpdateMapper extends QueryMapper {
|
||||
return value instanceof Modifier || value instanceof Modifiers;
|
||||
}
|
||||
|
||||
private boolean isQuery(Object value) {
|
||||
return value instanceof Query;
|
||||
}
|
||||
|
||||
private DBObject getMappedValue(Modifier modifier) {
|
||||
|
||||
Object value = converter.convertToMongoType(modifier.getValue(), ClassTypeInformation.OBJECT);
|
||||
@@ -163,6 +179,15 @@ public class UpdateMapper extends QueryMapper {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.QueryMapper.MetadataBackedField#getMappedKey()
|
||||
*/
|
||||
@Override
|
||||
public String getMappedKey() {
|
||||
return this.getPath() == null ? key : super.getMappedKey();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.QueryMapper.MetadataBackedField#getPropertyConverter()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,13 +126,13 @@ public abstract class AbstractMongoEventListener<E> implements ApplicationListen
|
||||
|
||||
public void onAfterDelete(DBObject dbo) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("onAfterConvert({})", dbo);
|
||||
LOG.debug("onAfterDelete({})", dbo);
|
||||
}
|
||||
}
|
||||
|
||||
public void onBeforeDelete(DBObject dbo) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("onAfterConvert({})", dbo);
|
||||
LOG.debug("onBeforeDelete({})", dbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.mongodb.DBObject;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public final class NearQuery {
|
||||
|
||||
@@ -143,10 +144,12 @@ public final class NearQuery {
|
||||
/**
|
||||
* Configures the {@link Pageable} to use.
|
||||
*
|
||||
* @param pageable
|
||||
* @param pageable must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public NearQuery with(Pageable pageable) {
|
||||
|
||||
Assert.notNull(pageable, "Pageable must not be 'null'.");
|
||||
this.num = pageable.getOffset() + pageable.getPageSize();
|
||||
this.skip = pageable.getOffset();
|
||||
return this;
|
||||
@@ -311,13 +314,18 @@ public final class NearQuery {
|
||||
/**
|
||||
* Adds an actual query to the {@link NearQuery} to restrict the objects considered for the actual near operation.
|
||||
*
|
||||
* @param query
|
||||
* @param query must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public NearQuery query(Query query) {
|
||||
|
||||
Assert.notNull(query, "Cannot apply 'null' query on NearQuery.");
|
||||
this.query = query;
|
||||
this.skip = query.getSkip();
|
||||
this.num = query.getLimit();
|
||||
|
||||
if (query.getLimit() != 0) {
|
||||
this.num = query.getLimit();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,22 +79,24 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(method, parameters);
|
||||
Query query = createQuery(new ConvertingParameterAccessor(operations.getConverter(), accessor));
|
||||
|
||||
Object result = null;
|
||||
|
||||
if (method.isGeoNearQuery() && method.isPageQuery()) {
|
||||
|
||||
MongoParameterAccessor countAccessor = new MongoParametersParameterAccessor(method, parameters);
|
||||
Query countQuery = createCountQuery(new ConvertingParameterAccessor(operations.getConverter(), countAccessor));
|
||||
|
||||
return new GeoNearExecution(accessor).execute(query, countQuery);
|
||||
result = new GeoNearExecution(accessor).execute(query, countQuery);
|
||||
} else if (method.isGeoNearQuery()) {
|
||||
return new GeoNearExecution(accessor).execute(query);
|
||||
} else if (method.isCollectionQuery()) {
|
||||
return new CollectionExecution(accessor.getPageable()).execute(query);
|
||||
result = new CollectionExecution(accessor.getPageable()).execute(query);
|
||||
} else if (method.isPageQuery()) {
|
||||
return new PagedExecution(accessor.getPageable()).execute(query);
|
||||
result = new PagedExecution(accessor.getPageable()).execute(query);
|
||||
} else {
|
||||
result = new SingleEntityExecution(isCountQuery()).execute(query);
|
||||
}
|
||||
|
||||
Object result = new SingleEntityExecution(isCountQuery()).execute(query);
|
||||
|
||||
if (result == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,14 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.util.JSONParseException;
|
||||
|
||||
/**
|
||||
* {@link RepositoryQuery} implementation for Mongo.
|
||||
@@ -66,7 +70,24 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery);
|
||||
return creator.createQuery();
|
||||
Query query = creator.createQuery();
|
||||
|
||||
String fieldSpec = this.getQueryMethod().getFieldSpecification();
|
||||
|
||||
if (!StringUtils.hasText(fieldSpec)) {
|
||||
return query;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
BasicQuery result = new BasicQuery(query.getQueryObject().toString(), fieldSpec);
|
||||
result.setSortObject(query.getSortObject());
|
||||
return result;
|
||||
|
||||
} catch (JSONParseException o_O) {
|
||||
throw new IllegalStateException(String.format("Invalid query or field specification in %s!", getQueryMethod(),
|
||||
o_O));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
|
||||
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* Sample configuration class in default package.
|
||||
*
|
||||
* @see DATAMONGO-877
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
public class ConfigClassInDefaultPackage extends AbstractMongoConfiguration {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.config.AbstractMongoConfiguration#getDatabaseName()
|
||||
*/
|
||||
@Override
|
||||
protected String getDatabaseName() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.config.AbstractMongoConfiguration#mongo()
|
||||
*/
|
||||
@Override
|
||||
public Mongo mongo() throws Exception {
|
||||
return new MongoClient();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
/**
|
||||
* Unit test for {@link ConfigClassInDefaultPackage}.
|
||||
*
|
||||
* @see DATAMONGO-877
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ConfigClassInDefaultPackageUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-877
|
||||
*/
|
||||
@Test
|
||||
public void loadsConfigClassFromDefaultPackage() {
|
||||
new AnnotationConfigApplicationContext(ConfigClassInDefaultPackage.class).close();
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
@@ -96,6 +97,21 @@ public class AuditingViaJavaConfigRepositoriesTests {
|
||||
assertThat(createdBy.getFirstname(), is(this.auditor.getFirstname()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-843
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
public void defaultsMappingContextIfNoneConfigured() {
|
||||
new AnnotationConfigApplicationContext(SampleConfig.class);
|
||||
}
|
||||
|
||||
@Repository
|
||||
static interface AuditablePersonRepository extends MongoRepository<AuditablePerson, String> {}
|
||||
|
||||
@Configuration
|
||||
@EnableMongoAuditing
|
||||
static class SampleConfig {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -186,6 +186,8 @@ public class MongoTemplateTests {
|
||||
template.dropCollection(DocumentWithCollectionOfSimpleType.class);
|
||||
template.dropCollection(DocumentWithMultipleCollections.class);
|
||||
template.dropCollection(DocumentWithDBRefCollection.class);
|
||||
template.dropCollection(SomeContent.class);
|
||||
template.dropCollection(SomeTemplate.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -298,6 +300,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 +314,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
|
||||
@@ -2293,8 +2290,6 @@ public class MongoTemplateTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* <<<<<<< HEAD
|
||||
*
|
||||
* @see DATAMONOGO-828
|
||||
*/
|
||||
@Test
|
||||
@@ -2394,7 +2389,7 @@ public class MongoTemplateTests {
|
||||
assertThat(result.dbRefAnnotatedList.get(0), is(notNullValue()));
|
||||
assertThat(result.dbRefAnnotatedList.get(0).id, is((Object) "1"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-852
|
||||
*/
|
||||
@@ -2422,7 +2417,7 @@ public class MongoTemplateTests {
|
||||
* @see DATAMONGO-468
|
||||
*/
|
||||
@Test
|
||||
public void shouldBeAbleToUpdateDbRefPropertyWithDomainObject(){
|
||||
public void shouldBeAbleToUpdateDbRefPropertyWithDomainObject() {
|
||||
|
||||
Sample sample1 = new Sample("1", "A");
|
||||
Sample sample2 = new Sample("2", "B");
|
||||
@@ -2434,17 +2429,273 @@ public class MongoTemplateTests {
|
||||
doc.dbRefProperty = sample1;
|
||||
template.save(doc);
|
||||
|
||||
Update update = new Update().set("dbRefProperty",sample2);
|
||||
Update update = new Update().set("dbRefProperty", sample2);
|
||||
|
||||
Query qry = query(where("id").is("1"));
|
||||
template.updateFirst(qry, update, DocumentWithDBRefCollection.class);
|
||||
|
||||
DocumentWithDBRefCollection updatedDoc = template.findOne(qry, DocumentWithDBRefCollection.class);
|
||||
|
||||
assertThat(updatedDoc,is(notNullValue()));
|
||||
assertThat(updatedDoc.dbRefProperty,is(notNullValue()));
|
||||
assertThat(updatedDoc.dbRefProperty.id,is(sample2.id));
|
||||
assertThat(updatedDoc.dbRefProperty.field,is(sample2.field));
|
||||
assertThat(updatedDoc, is(notNullValue()));
|
||||
assertThat(updatedDoc.dbRefProperty, is(notNullValue()));
|
||||
assertThat(updatedDoc.dbRefProperty.id, is(sample2.id));
|
||||
assertThat(updatedDoc.dbRefProperty.field, is(sample2.field));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-862
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateShouldWorkForPathsOnInterfaceMethods() {
|
||||
|
||||
DocumentWithCollection document = new DocumentWithCollection(Arrays.<Model> asList(new ModelA("spring"),
|
||||
new ModelA("data")));
|
||||
|
||||
template.save(document);
|
||||
|
||||
Query query = query(where("id").is(document.id).and("models._id").exists(true));
|
||||
Update update = new Update().set("models.$.value", "mongodb");
|
||||
template.findAndModify(query, update, DocumentWithCollection.class);
|
||||
|
||||
DocumentWithCollection result = template.findOne(query(where("id").is(document.id)), DocumentWithCollection.class);
|
||||
assertThat(result.models.get(0).value(), is("mongodb"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-773
|
||||
*/
|
||||
@Test
|
||||
public void testShouldSupportQueryWithIncludedDbRefField() {
|
||||
|
||||
Sample sample = new Sample("47111", "foo");
|
||||
template.save(sample);
|
||||
|
||||
DocumentWithDBRefCollection doc = new DocumentWithDBRefCollection();
|
||||
doc.id = "4711";
|
||||
doc.dbRefProperty = sample;
|
||||
|
||||
template.save(doc);
|
||||
|
||||
Query qry = query(where("id").is(doc.id));
|
||||
qry.fields().include("dbRefProperty");
|
||||
|
||||
List<DocumentWithDBRefCollection> result = template.find(qry, DocumentWithDBRefCollection.class);
|
||||
|
||||
assertThat(result, is(notNullValue()));
|
||||
assertThat(result, hasSize(1));
|
||||
assertThat(result.get(0), is(notNullValue()));
|
||||
assertThat(result.get(0).dbRefProperty, is(notNullValue()));
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-913
|
||||
*/
|
||||
@Test
|
||||
public void shouldRetrieveInitializedValueFromDbRefAssociationAfterLoad() {
|
||||
|
||||
SomeContent content = new SomeContent();
|
||||
content.id = "content-1";
|
||||
content.name = "Content 1";
|
||||
content.text = "Some text";
|
||||
|
||||
template.save(content);
|
||||
|
||||
SomeTemplate tmpl = new SomeTemplate();
|
||||
tmpl.id = "template-1";
|
||||
tmpl.content = content;
|
||||
|
||||
template.save(tmpl);
|
||||
|
||||
SomeTemplate result = template.findOne(query(where("content").is(tmpl.getContent())), SomeTemplate.class);
|
||||
|
||||
assertThat(result, is(notNullValue()));
|
||||
assertThat(result.getContent(), is(notNullValue()));
|
||||
assertThat(result.getContent().getId(), is(notNullValue()));
|
||||
assertThat(result.getContent().getName(), is(notNullValue()));
|
||||
assertThat(result.getContent().getText(), is(content.getText()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-913
|
||||
*/
|
||||
@Test
|
||||
public void shouldReuseExistingDBRefInQueryFromDbRefAssociationAfterLoad() {
|
||||
|
||||
SomeContent content = new SomeContent();
|
||||
content.id = "content-1";
|
||||
content.name = "Content 1";
|
||||
content.text = "Some text";
|
||||
|
||||
template.save(content);
|
||||
|
||||
SomeTemplate tmpl = new SomeTemplate();
|
||||
tmpl.id = "template-1";
|
||||
tmpl.content = content;
|
||||
|
||||
template.save(tmpl);
|
||||
|
||||
SomeTemplate result = template.findOne(query(where("content").is(tmpl.getContent())), SomeTemplate.class);
|
||||
|
||||
// Use lazy-loading-proxy in query
|
||||
result = template.findOne(query(where("content").is(result.getContent())), SomeTemplate.class);
|
||||
|
||||
assertNotNull(result.getContent().getName());
|
||||
assertThat(result.getContent().getName(), is(content.getName()));
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -2454,7 +2705,7 @@ public class MongoTemplateTests {
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
public List<Sample> dbRefAnnotatedList;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
public Sample dbRefProperty;
|
||||
}
|
||||
|
||||
@@ -2482,10 +2733,13 @@ public class MongoTemplateTests {
|
||||
|
||||
static interface Model {
|
||||
String value();
|
||||
|
||||
String id();
|
||||
}
|
||||
|
||||
static class ModelA implements Model {
|
||||
|
||||
@Id String id;
|
||||
private String value;
|
||||
|
||||
ModelA(String value) {
|
||||
@@ -2496,6 +2750,11 @@ public class MongoTemplateTests {
|
||||
public String value() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
static class Document {
|
||||
@@ -2618,4 +2877,39 @@ 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;
|
||||
String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
static class SomeMessage {
|
||||
String id;
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef SomeContent dbrefContent;
|
||||
SomeContent normalContent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,14 @@ import java.util.Collections;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.hamcrest.core.Is;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
@@ -37,6 +41,8 @@ import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.convert.CustomConversions;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
@@ -44,12 +50,16 @@ import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoException;
|
||||
@@ -58,6 +68,7 @@ import com.mongodb.MongoException;
|
||||
* Unit tests for {@link MongoTemplate}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@@ -68,6 +79,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Mock Mongo mongo;
|
||||
@Mock DB db;
|
||||
@Mock DBCollection collection;
|
||||
@Mock DBCursor cursor;
|
||||
|
||||
MongoExceptionTranslator exceptionTranslator = new MongoExceptionTranslator();
|
||||
MappingMongoConverter converter;
|
||||
@@ -83,7 +95,6 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
this.mappingContext = new MongoMappingContext();
|
||||
this.converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), mappingContext);
|
||||
this.template = new MongoTemplate(factory, converter);
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@@ -205,6 +216,46 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
assertThat(entity.id, is(5));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-868
|
||||
*/
|
||||
@Test
|
||||
public void findAndModifyShouldBumpVersionByOneWhenVersionFieldNotIncludedInUpdate() {
|
||||
|
||||
VersionedEntity v = new VersionedEntity();
|
||||
v.id = 1;
|
||||
v.version = 0;
|
||||
|
||||
ArgumentCaptor<DBObject> captor = ArgumentCaptor.forClass(DBObject.class);
|
||||
|
||||
template.findAndModify(new Query(), new Update().set("id", "10"), VersionedEntity.class);
|
||||
verify(collection, times(1)).findAndModify(Matchers.any(DBObject.class),
|
||||
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", 1L)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-868
|
||||
*/
|
||||
@Test
|
||||
public void findAndModifyShouldNotBumpVersionByOneWhenVersionFieldAlreadyIncludedInUpdate() {
|
||||
|
||||
VersionedEntity v = new VersionedEntity();
|
||||
v.id = 1;
|
||||
v.version = 0;
|
||||
|
||||
ArgumentCaptor<DBObject> captor = ArgumentCaptor.forClass(DBObject.class);
|
||||
|
||||
template.findAndModify(new Query(), new Update().set("version", 100), VersionedEntity.class);
|
||||
verify(collection, times(1)).findAndModify(Matchers.any(DBObject.class), isNull(DBObject.class),
|
||||
isNull(DBObject.class), eq(false), captor.capture(), eq(false), eq(false));
|
||||
|
||||
Assert.assertThat(captor.getValue().get("$set"), Is.<Object> is(new BasicDBObject("version", 100)));
|
||||
Assert.assertThat(captor.getValue().get("$inc"), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-533
|
||||
*/
|
||||
@@ -235,6 +286,28 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-948
|
||||
*/
|
||||
@Test
|
||||
public void sortShouldBeTakenAsIsWhenExecutingQueryWithoutSpecificTypeInformation() {
|
||||
|
||||
when(db.getCollection(Mockito.any(String.class))).thenReturn(collection);
|
||||
when(collection.find(Mockito.any(DBObject.class))).thenReturn(cursor);
|
||||
when(cursor.sort(Mockito.any(DBObject.class))).thenReturn(cursor);
|
||||
|
||||
Query query = Query.query(Criteria.where("foo").is("bar")).with(new Sort("foo"));
|
||||
template.executeQuery(query, "collection1", new DocumentCallbackHandler() {
|
||||
|
||||
@Override
|
||||
public void processDocument(DBObject dbObject) {}
|
||||
});
|
||||
|
||||
ArgumentCaptor<DBObject> captor = ArgumentCaptor.forClass(DBObject.class);
|
||||
verify(cursor, times(1)).sort(captor.capture());
|
||||
assertThat(captor.getValue(), equalTo(new BasicDBObjectBuilder().add("foo", 1).get()));
|
||||
}
|
||||
|
||||
class AutogenerateableId {
|
||||
|
||||
@Id BigInteger id;
|
||||
@@ -249,6 +322,12 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
static class VersionedEntity {
|
||||
|
||||
@Id Integer id;
|
||||
@Version Integer version;
|
||||
}
|
||||
|
||||
enum MyConverter implements Converter<AutogenerateableId, String> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.core.CollectionCallback;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.util.Version;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -822,6 +823,40 @@ public class AggregationTests {
|
||||
assertThat(invoice.getTotalAmount(), is(closeTo(9.877, 000001)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-924
|
||||
*/
|
||||
@Test
|
||||
public void shouldAllowGroupingByAliasedFieldDefinedInFormerAggregationStage() {
|
||||
|
||||
mongoTemplate.dropCollection(CarPerson.class);
|
||||
|
||||
CarPerson person1 = new CarPerson("first1", "last1", new CarDescriptor.Entry("MAKE1", "MODEL1", 2000),
|
||||
new CarDescriptor.Entry("MAKE1", "MODEL2", 2001), new CarDescriptor.Entry("MAKE2", "MODEL3", 2010),
|
||||
new CarDescriptor.Entry("MAKE3", "MODEL4", 2014));
|
||||
|
||||
CarPerson person2 = new CarPerson("first2", "last2", new CarDescriptor.Entry("MAKE3", "MODEL4", 2014));
|
||||
|
||||
CarPerson person3 = new CarPerson("first3", "last3", new CarDescriptor.Entry("MAKE2", "MODEL5", 2011));
|
||||
|
||||
mongoTemplate.save(person1);
|
||||
mongoTemplate.save(person2);
|
||||
mongoTemplate.save(person3);
|
||||
|
||||
TypedAggregation<CarPerson> agg = Aggregation.newAggregation(CarPerson.class,
|
||||
unwind("descriptors.carDescriptor.entries"), //
|
||||
project() //
|
||||
.and("descriptors.carDescriptor.entries.make").as("make") //
|
||||
.and("descriptors.carDescriptor.entries.model").as("model") //
|
||||
.and("firstName").as("firstName") //
|
||||
.and("lastName").as("lastName"), //
|
||||
group("make"));
|
||||
|
||||
AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class);
|
||||
|
||||
assertThat(result.getMappedResults(), hasSize(3));
|
||||
}
|
||||
|
||||
private void assertLikeStats(LikeStats like, String id, long count) {
|
||||
|
||||
assertThat(like, is(notNullValue()));
|
||||
@@ -938,4 +973,52 @@ public class AggregationTests {
|
||||
this.createDate = createDate;
|
||||
}
|
||||
}
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.Document
|
||||
static class CarPerson {
|
||||
|
||||
@Id private String id;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private Descriptors descriptors;
|
||||
|
||||
public CarPerson(String firstname, String lastname, Entry... entries) {
|
||||
this.firstName = firstname;
|
||||
this.lastName = lastname;
|
||||
|
||||
this.descriptors = new Descriptors();
|
||||
|
||||
this.descriptors.carDescriptor = new CarDescriptor(entries);
|
||||
}
|
||||
}
|
||||
|
||||
static class Descriptors {
|
||||
private CarDescriptor carDescriptor;
|
||||
}
|
||||
|
||||
static class CarDescriptor {
|
||||
|
||||
private List<Entry> entries = new ArrayList<AggregationTests.CarDescriptor.Entry>();
|
||||
|
||||
public CarDescriptor(Entry... entries) {
|
||||
|
||||
for (Entry entry : entries) {
|
||||
this.entries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
static class Entry {
|
||||
private String make;
|
||||
private String model;
|
||||
private int year;
|
||||
|
||||
public Entry() {}
|
||||
|
||||
public Entry(String make, String model, int year) {
|
||||
this.make = make;
|
||||
this.model = model;
|
||||
this.year = year;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,4 +179,49 @@ 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"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-924
|
||||
*/
|
||||
@Test
|
||||
public void referencingProjectionAliasesFromPreviousStepShouldReferToTheSameFieldTarget() {
|
||||
|
||||
DBObject agg = newAggregation( //
|
||||
project().and("foo.bar").as("ba") //
|
||||
, project().and("ba").as("b") //
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject projection0 = extractPipelineElement(agg, 0, "$project");
|
||||
assertThat(projection0, is((DBObject) new BasicDBObject("ba", "$foo.bar")));
|
||||
|
||||
DBObject projection1 = extractPipelineElement(agg, 1, "$project");
|
||||
assertThat(projection1, is((DBObject) new BasicDBObject("b", "$ba")));
|
||||
}
|
||||
|
||||
private DBObject extractPipelineElement(DBObject agg, int index, String operation) {
|
||||
|
||||
List<DBObject> pipeline = (List<DBObject>) agg.get("pipeline");
|
||||
return (DBObject) pipeline.get(index).get(operation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -17,25 +17,40 @@ package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.data.mongodb.core.convert.CustomConversions;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link TypeBasedAggregationOperationContext}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TypeBasedAggregationOperationContextUnitTests {
|
||||
@@ -89,6 +104,104 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
assertThat(context.getReference("id"), is(new FieldReference(new ExposedField(Fields.field("id", "_id"), true))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-912
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseCustomConversionIfPresentAndConversionIsRequiredInFirstStage() {
|
||||
|
||||
CustomConversions customConversions = customAgeConversions();
|
||||
converter.setCustomConversions(customConversions);
|
||||
customConversions.registerConvertersIn((GenericConversionService) converter.getConversionService());
|
||||
|
||||
AggregationOperationContext context = getContext(FooPerson.class);
|
||||
|
||||
MatchOperation matchStage = match(Criteria.where("age").is(new Age(10)));
|
||||
ProjectionOperation projectStage = project("age", "name");
|
||||
|
||||
DBObject agg = newAggregation(matchStage, projectStage).toDbObject("test", context);
|
||||
|
||||
DBObject age = getValue((DBObject) getValue(getPipelineElementFromAggregationAt(agg, 0), "$match"), "age");
|
||||
assertThat(age, is((DBObject) new BasicDBObject("v", 10)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-912
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseCustomConversionIfPresentAndConversionIsRequiredInLaterStage() {
|
||||
|
||||
CustomConversions customConversions = customAgeConversions();
|
||||
converter.setCustomConversions(customConversions);
|
||||
customConversions.registerConvertersIn((GenericConversionService) converter.getConversionService());
|
||||
|
||||
AggregationOperationContext context = getContext(FooPerson.class);
|
||||
|
||||
MatchOperation matchStage = match(Criteria.where("age").is(new Age(10)));
|
||||
ProjectionOperation projectStage = project("age", "name");
|
||||
|
||||
DBObject agg = newAggregation(projectStage, matchStage).toDbObject("test", context);
|
||||
|
||||
DBObject age = getValue((DBObject) getValue(getPipelineElementFromAggregationAt(agg, 1), "$match"), "age");
|
||||
assertThat(age, is((DBObject) new BasicDBObject("v", 10)));
|
||||
}
|
||||
|
||||
@Document(collection = "person")
|
||||
public static class FooPerson {
|
||||
|
||||
final ObjectId id;
|
||||
final String name;
|
||||
final Age age;
|
||||
|
||||
@PersistenceConstructor
|
||||
FooPerson(ObjectId id, String name, Age age) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Age {
|
||||
|
||||
final int value;
|
||||
|
||||
Age(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public CustomConversions customAgeConversions() {
|
||||
return new CustomConversions(Arrays.<Converter<?, ?>> asList(ageWriteConverter(), ageReadConverter()));
|
||||
}
|
||||
|
||||
Converter<Age, DBObject> ageWriteConverter() {
|
||||
return new Converter<Age, DBObject>() {
|
||||
@Override
|
||||
public DBObject convert(Age age) {
|
||||
return new BasicDBObject("v", age.value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Converter<DBObject, Age> ageReadConverter() {
|
||||
return new Converter<DBObject, Age>() {
|
||||
@Override
|
||||
public Age convert(DBObject dbObject) {
|
||||
return new Age(((Integer) dbObject.get("v")));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static DBObject getPipelineElementFromAggregationAt(DBObject agg, int index) {
|
||||
return ((List<DBObject>) agg.get("pipeline")).get(index);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> T getValue(DBObject o, String key) {
|
||||
return (T) o.get(key);
|
||||
}
|
||||
|
||||
private TypeBasedAggregationOperationContext getContext(Class<?> type) {
|
||||
return new TypeBasedAggregationOperationContext(type, context, mapper);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -28,6 +28,8 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -35,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;
|
||||
@@ -1387,6 +1390,7 @@ public class MappingMongoConverterUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-812
|
||||
* @see DATAMONGO-893
|
||||
*/
|
||||
@Test
|
||||
public void convertsListToBasicDBListAndRetainsTypeInformationForComplexObjects() {
|
||||
@@ -1396,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)));
|
||||
|
||||
@@ -1454,6 +1458,76 @@ public class MappingMongoConverterUnitTests {
|
||||
assertThat(dbList.get(0), instanceOf(String.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-833
|
||||
*/
|
||||
@Test
|
||||
public void readsEnumSetCorrectly() {
|
||||
|
||||
BasicDBList enumSet = new BasicDBList();
|
||||
enumSet.add("SECOND");
|
||||
DBObject dbObject = new BasicDBObject("enumSet", enumSet);
|
||||
|
||||
ClassWithEnumProperty result = converter.read(ClassWithEnumProperty.class, dbObject);
|
||||
|
||||
assertThat(result.enumSet, is(instanceOf(EnumSet.class)));
|
||||
assertThat(result.enumSet.size(), is(1));
|
||||
assertThat(result.enumSet, hasItem(SampleEnum.SECOND));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-833
|
||||
*/
|
||||
@Test
|
||||
public void readsEnumMapCorrectly() {
|
||||
|
||||
BasicDBObject enumMap = new BasicDBObject("FIRST", "Dave");
|
||||
ClassWithEnumProperty result = converter.read(ClassWithEnumProperty.class, new BasicDBObject("enumMap", enumMap));
|
||||
|
||||
assertThat(result.enumMap, is(instanceOf(EnumMap.class)));
|
||||
assertThat(result.enumMap.size(), is(1));
|
||||
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;
|
||||
}
|
||||
@@ -1462,6 +1536,8 @@ public class MappingMongoConverterUnitTests {
|
||||
|
||||
SampleEnum sampleEnum;
|
||||
List<SampleEnum> enums;
|
||||
EnumSet<SampleEnum> enumSet;
|
||||
EnumMap<SampleEnum, String> enumMap;
|
||||
}
|
||||
|
||||
static enum SampleEnum {
|
||||
@@ -1479,7 +1555,11 @@ public class MappingMongoConverterUnitTests {
|
||||
abstract void method();
|
||||
}
|
||||
|
||||
static class Address {
|
||||
static interface InterfaceType {
|
||||
|
||||
}
|
||||
|
||||
static class Address implements InterfaceType {
|
||||
String street;
|
||||
String city;
|
||||
}
|
||||
@@ -1519,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 {
|
||||
|
||||
@@ -34,11 +34,14 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
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;
|
||||
@@ -48,6 +51,7 @@ import org.springframework.data.mongodb.core.query.Query;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
|
||||
@@ -57,6 +61,7 @@ import com.mongodb.QueryBuilder;
|
||||
* @author Oliver Gierke
|
||||
* @author Patryk Wasik
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class QueryMapperUnitTests {
|
||||
@@ -501,10 +506,119 @@ public class QueryMapperUnitTests {
|
||||
assertThat(idValuesAfter, is(idValuesBefore));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-821
|
||||
*/
|
||||
@Test
|
||||
public void queryMapperShouldNotTryToMapDBRefListPropertyIfNestedInsideDBObjectWithinDBObject() {
|
||||
|
||||
DBObject queryObject = query(
|
||||
where("referenceList").is(new BasicDBObject("$nested", new BasicDBObject("$keys", 0L)))).getQueryObject();
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(queryObject, context.getPersistentEntity(WithDBRefList.class));
|
||||
DBObject referenceObject = getAsDBObject(mappedObject, "referenceList");
|
||||
DBObject nestedObject = getAsDBObject(referenceObject, "$nested");
|
||||
|
||||
assertThat(nestedObject, is((DBObject) new BasicDBObject("$keys", 0L)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-821
|
||||
*/
|
||||
@Test
|
||||
public void queryMapperShouldNotTryToMapDBRefPropertyIfNestedInsideDBObjectWithinDBObject() {
|
||||
|
||||
DBObject queryObject = query(where("reference").is(new BasicDBObject("$nested", new BasicDBObject("$keys", 0L))))
|
||||
.getQueryObject();
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(queryObject, context.getPersistentEntity(WithDBRef.class));
|
||||
DBObject referenceObject = getAsDBObject(mappedObject, "reference");
|
||||
DBObject nestedObject = getAsDBObject(referenceObject, "$nested");
|
||||
|
||||
assertThat(nestedObject, is((DBObject) new BasicDBObject("$keys", 0L)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-821
|
||||
*/
|
||||
@Test
|
||||
public void queryMapperShouldMapDBRefPropertyIfNestedInDBObject() {
|
||||
|
||||
Reference sample = new Reference();
|
||||
sample.id = 321L;
|
||||
DBObject queryObject = query(where("reference").is(new BasicDBObject("$in", Arrays.asList(sample))))
|
||||
.getQueryObject();
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(queryObject, context.getPersistentEntity(WithDBRef.class));
|
||||
|
||||
DBObject referenceObject = getAsDBObject(mappedObject, "reference");
|
||||
BasicDBList inObject = getAsDBList(referenceObject, "$in");
|
||||
|
||||
assertThat(inObject.get(0), is(instanceOf(com.mongodb.DBRef.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-773
|
||||
*/
|
||||
@Test
|
||||
public void queryMapperShouldBeAbleToProcessQueriesThatIncludeDbRefFields() {
|
||||
|
||||
BasicMongoPersistentEntity<?> persistentEntity = context.getPersistentEntity(WithDBRef.class);
|
||||
|
||||
Query qry = query(where("someString").is("abc"));
|
||||
qry.fields().include("reference");
|
||||
|
||||
DBObject mappedFields = mapper.getMappedObject(qry.getFieldsObject(), persistentEntity);
|
||||
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\"}]}}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-647
|
||||
*/
|
||||
@Test
|
||||
public void customizedFieldNameShouldBeMappedCorrectlyWhenApplyingSort() {
|
||||
|
||||
Query query = query(where("field").is("bar")).with(new Sort(Direction.DESC, "field"));
|
||||
DBObject dbo = mapper.getMappedObject(query.getSortObject(), context.getPersistentEntity(CustomizedField.class));
|
||||
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("foo", -1).get()));
|
||||
}
|
||||
|
||||
@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;
|
||||
@@ -541,6 +655,12 @@ public class QueryMapperUnitTests {
|
||||
@DBRef Reference reference;
|
||||
}
|
||||
|
||||
class WithDBRefList {
|
||||
|
||||
String someString;
|
||||
@DBRef List<Reference> referenceList;
|
||||
}
|
||||
|
||||
class Reference {
|
||||
|
||||
Long id;
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.util.Arrays;
|
||||
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;
|
||||
@@ -36,12 +38,16 @@ 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.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
|
||||
@@ -376,6 +382,151 @@ public class UpdateMapperUnitTests {
|
||||
assertThat(setClause.get("dbRefProperty"), is((Object) new DBRef(null, "entity", entity.id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-862
|
||||
*/
|
||||
@Test
|
||||
public void rendersUpdateAndPreservesKeyForPathsNotPointingToProperty() {
|
||||
|
||||
Update update = new Update().set("listOfInterface.$.value", "expected-value");
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ParentClass.class));
|
||||
|
||||
DBObject setClause = getAsDBObject(mappedObject, "$set");
|
||||
assertThat(setClause.containsField("listOfInterface.$.value"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-863
|
||||
*/
|
||||
@Test
|
||||
public void doesNotConvertRawDbObjects() {
|
||||
|
||||
Update update = new Update();
|
||||
update.pull("options",
|
||||
new BasicDBObject("_id", new BasicDBObject("$in", converter.convertToMongoType(Arrays.asList(1L, 2L)))));
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ParentClass.class));
|
||||
|
||||
DBObject setClause = getAsDBObject(mappedObject, "$pull");
|
||||
DBObject options = getAsDBObject(setClause, "options");
|
||||
DBObject idClause = getAsDBObject(options, "_id");
|
||||
BasicDBList inClause = getAsDBList(idClause, "$in");
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-847
|
||||
*/
|
||||
@Test
|
||||
public void updateMapperConvertsNestedQueryCorrectly() {
|
||||
|
||||
Update update = new Update().pull("list", Query.query(Criteria.where("value").in("foo", "bar")));
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(ParentClass.class));
|
||||
|
||||
DBObject $pull = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$pull");
|
||||
DBObject list = DBObjectTestUtils.getAsDBObject($pull, "aliased");
|
||||
DBObject value = DBObjectTestUtils.getAsDBObject(list, "value");
|
||||
BasicDBList $in = DBObjectTestUtils.getAsDBList(value, "$in");
|
||||
|
||||
assertThat($in, IsIterableContainingInOrder.<Object> contains("foo", "bar"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-847
|
||||
*/
|
||||
@Test
|
||||
public void updateMapperConvertsPullWithNestedQuerfyOnDBRefCorrectly() {
|
||||
|
||||
Update update = new Update().pull("dbRefAnnotatedList", Query.query(Criteria.where("id").is("1")));
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(DocumentWithDBRefCollection.class));
|
||||
|
||||
DBObject $pull = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$pull");
|
||||
DBObject list = DBObjectTestUtils.getAsDBObject($pull, "dbRefAnnotatedList");
|
||||
|
||||
assertThat(list, equalTo(new BasicDBObjectBuilder().add("_id", "1").get()));
|
||||
}
|
||||
|
||||
@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 {
|
||||
@@ -384,6 +535,7 @@ public class UpdateMapperUnitTests {
|
||||
public ModelImpl(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ModelWrapper {
|
||||
@@ -411,6 +563,9 @@ public class UpdateMapperUnitTests {
|
||||
@Field("aliased")//
|
||||
List<? extends AbstractChildClass> list;
|
||||
|
||||
@Field//
|
||||
List<Model> listOfInterface;
|
||||
|
||||
public ParentClass(String id, List<? extends AbstractChildClass> list) {
|
||||
this.id = id;
|
||||
this.list = list;
|
||||
|
||||
@@ -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.
|
||||
@@ -37,6 +37,7 @@ import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.geo.Box;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -50,6 +51,7 @@ import com.mongodb.Mongo;
|
||||
* Integration test for {@link MongoTemplate}'s Map-Reduce operations
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
@@ -276,6 +278,31 @@ public class MapReduceTests {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-938
|
||||
*/
|
||||
@Test
|
||||
public void mapReduceShouldUseQueryMapper() {
|
||||
|
||||
DBCollection c = mongoTemplate.getDb().getCollection("jmrWithGeo");
|
||||
|
||||
c.save(new BasicDBObject("x", new String[] { "a", "b" }).append("loc", new double[] { 0, 0 }));
|
||||
c.save(new BasicDBObject("x", new String[] { "b", "c" }).append("loc", new double[] { 0, 0 }));
|
||||
c.save(new BasicDBObject("x", new String[] { "c", "d" }).append("loc", new double[] { 0, 0 }));
|
||||
|
||||
Query query = new Query(where("x").ne(new String[] { "a", "b" }).and("loc")
|
||||
.within(new Box(new double[] { 0, 0 }, new double[] { 1, 1 })));
|
||||
|
||||
MapReduceResults<ValueObject> results = template.mapReduce(query, "jmrWithGeo", mapFunction, reduceFunction,
|
||||
ValueObject.class);
|
||||
|
||||
Map<String, Float> m = copyToMap(results);
|
||||
assertEquals(3, m.size());
|
||||
assertEquals(1, m.get("b").intValue());
|
||||
assertEquals(2, m.get("c").intValue());
|
||||
assertEquals(1, m.get("d").intValue());
|
||||
}
|
||||
|
||||
private void performMapReduce(boolean inline, boolean withQuery) {
|
||||
createMapReduceData();
|
||||
MapReduceResults<ValueObject> results;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 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.
|
||||
@@ -21,6 +21,7 @@ import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.mongodb.core.DBObjectTestUtils;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Metric;
|
||||
import org.springframework.data.mongodb.core.geo.Metrics;
|
||||
@@ -31,6 +32,7 @@ import org.springframework.data.mongodb.core.geo.Point;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class NearQueryUnitTests {
|
||||
|
||||
@@ -123,4 +125,36 @@ public class NearQueryUnitTests {
|
||||
assertThat(query.getSkip(), is(pageable.getPageNumber() * pageable.getPageSize()));
|
||||
assertThat((Integer) query.toDBObject().get("num"), is((pageable.getPageNumber() + 1) * pageable.getPageSize()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-829
|
||||
*/
|
||||
@Test
|
||||
public void nearQueryShouldInoreZeroLimitFromQuery() {
|
||||
|
||||
NearQuery query = NearQuery.near(new Point(1, 2)).query(Query.query(Criteria.where("foo").is("bar")));
|
||||
assertThat(query.toDBObject().get("num"), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONOGO-829
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void nearQueryShouldThrowExceptionWhenGivenANullQuery() {
|
||||
NearQuery.near(new Point(1, 2)).query(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-829
|
||||
*/
|
||||
@Test
|
||||
public void numShouldNotBeAlteredByQueryWithoutPageable() {
|
||||
|
||||
int num = 100;
|
||||
NearQuery query = NearQuery.near(new Point(1, 2));
|
||||
query.num(num);
|
||||
query.query(Query.query(Criteria.where("foo").is("bar")));
|
||||
|
||||
assertThat(DBObjectTestUtils.getTypedValue(query.toDBObject(), "num", Integer.class), is(num));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 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.
|
||||
@@ -50,6 +50,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
@@ -738,4 +739,91 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
assertThat(result.size(), is(1));
|
||||
assertThat(result.get(0), is(dave));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-871
|
||||
*/
|
||||
@Test
|
||||
public void findsPersonsByFirstnameAsArray() {
|
||||
|
||||
Person[] result = repository.findByThePersonsFirstnameAsArray("Leroi");
|
||||
|
||||
assertThat(result, is(arrayWithSize(1)));
|
||||
assertThat(result, is(arrayContaining(leroi)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-821
|
||||
*/
|
||||
@Test
|
||||
public void findUsingAnnotatedQueryOnDBRef() {
|
||||
|
||||
operations.remove(new org.springframework.data.mongodb.core.query.Query(), User.class);
|
||||
|
||||
User user = new User();
|
||||
user.username = "Terria";
|
||||
operations.save(user);
|
||||
|
||||
alicia.creator = user;
|
||||
repository.save(alicia);
|
||||
|
||||
Page<Person> result = repository.findByHavingCreator(new PageRequest(0, 100));
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-745
|
||||
*/
|
||||
@Test
|
||||
public void findByCustomQueryFirstnamesInListAndLastname() {
|
||||
|
||||
repository.save(new Person("foo", "bar"));
|
||||
repository.save(new Person("bar", "bar"));
|
||||
repository.save(new Person("fuu", "bar"));
|
||||
repository.save(new Person("notfound", "bar"));
|
||||
|
||||
Page<Person> result = repository.findByCustomQueryFirstnamesAndLastname(Arrays.asList("bar", "foo", "fuu"), "bar",
|
||||
new PageRequest(0, 2));
|
||||
|
||||
assertThat(result.getContent(), hasSize(2));
|
||||
assertThat(result.getTotalPages(), is(2));
|
||||
assertThat(result.getTotalElements(), is(3L));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-745
|
||||
*/
|
||||
@Test
|
||||
public void findByCustomQueryLastnameAndStreetInList() {
|
||||
|
||||
repository.save(new Person("foo", "bar").withAddress(new Address("street1", "1", "SB")));
|
||||
repository.save(new Person("bar", "bar").withAddress(new Address("street2", "1", "SB")));
|
||||
repository.save(new Person("fuu", "bar").withAddress(new Address("street1", "2", "RGB")));
|
||||
repository.save(new Person("notfound", "notfound"));
|
||||
|
||||
Page<Person> result = repository.findByCustomQueryLastnameAndAddressStreetInList("bar",
|
||||
Arrays.asList("street1", "street2"), new PageRequest(0, 2));
|
||||
|
||||
assertThat(result.getContent(), hasSize(2));
|
||||
assertThat(result.getTotalPages(), is(2));
|
||||
assertThat(result.getTotalElements(), is(3L));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,6 +261,12 @@ public class Person extends Contact {
|
||||
return this.getId().equals(that.getId());
|
||||
}
|
||||
|
||||
public Person withAddress(Address address) {
|
||||
|
||||
this.address = address;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.data.querydsl.QueryDslPredicateExecutor;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface PersonRepository extends MongoRepository<Person, String>, QueryDslPredicateExecutor<Person> {
|
||||
|
||||
@@ -70,6 +71,12 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
@Query(value = "{ 'firstname' : ?0 }", fields = "{ 'firstname': 1, 'lastname': 1}")
|
||||
List<Person> findByThePersonsFirstname(String firstname);
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-871
|
||||
*/
|
||||
@Query(value = "{ 'firstname' : ?0 }")
|
||||
Person[] findByThePersonsFirstnameAsArray(String firstname);
|
||||
|
||||
/**
|
||||
* Returns all {@link Person}s with a firstname matching the given one (*-wildcard supported).
|
||||
*
|
||||
@@ -245,4 +252,26 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-745
|
||||
*/
|
||||
@Query("{firstname:{$in:?0}, lastname:?1}")
|
||||
Page<Person> findByCustomQueryFirstnamesAndLastname(List<String> firstnames, String lastname, Pageable page);
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-745
|
||||
*/
|
||||
@Query("{lastname:?0, address.street:{$in:?1}}")
|
||||
Page<Person> findByCustomQueryLastnameAndAddressStreetInList(String lastname, List<String> streetNames, Pageable page);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.repository.query;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.mongodb.repository.Person;
|
||||
import org.springframework.data.mongodb.repository.Query;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PartTreeMongoQuery}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PartTreeMongoQueryUnitTests {
|
||||
|
||||
@Mock RepositoryMetadata metadataMock;
|
||||
@Mock MongoOperations mongoOperationsMock;
|
||||
|
||||
MongoMappingContext mappingContext;
|
||||
|
||||
public @Rule ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setUp() {
|
||||
|
||||
when(metadataMock.getDomainType()).thenReturn((Class) Person.class);
|
||||
when(metadataMock.getReturnedDomainClass(Matchers.any(Method.class))).thenReturn((Class) Person.class);
|
||||
mappingContext = new MongoMappingContext();
|
||||
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mock(MongoDbFactory.class));
|
||||
MongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
|
||||
|
||||
when(mongoOperationsMock.getConverter()).thenReturn(converter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMOGO-952
|
||||
*/
|
||||
@Test
|
||||
public void rejectsInvalidFieldSpecification() {
|
||||
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectMessage("findByLastname");
|
||||
|
||||
deriveQueryFromMethod("findByLastname", new Object[] { "foo" });
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMOGO-952
|
||||
*/
|
||||
@Test
|
||||
public void singleFieldJsonIncludeRestrictionShouldBeConsidered() {
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findByFirstname",
|
||||
new Object[] { "foo" });
|
||||
|
||||
assertThat(query.getFieldsObject(), is(new BasicDBObjectBuilder().add("firstname", 1).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMOGO-952
|
||||
*/
|
||||
@Test
|
||||
public void multiFieldJsonIncludeRestrictionShouldBeConsidered() {
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findByFirstnameAndLastname",
|
||||
new Object[] { "foo", "bar" });
|
||||
|
||||
assertThat(query.getFieldsObject(), is(new BasicDBObjectBuilder().add("firstname", 1).add("lastname", 1).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMOGO-952
|
||||
*/
|
||||
@Test
|
||||
public void multiFieldJsonExcludeRestrictionShouldBeConsidered() {
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findPersonByFirstnameAndLastname",
|
||||
new Object[] { "foo", "bar" });
|
||||
|
||||
assertThat(query.getFieldsObject(), is(new BasicDBObjectBuilder().add("firstname", 0).add("lastname", 0).get()));
|
||||
}
|
||||
|
||||
private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object[] args) {
|
||||
|
||||
Class<?>[] types = new Class<?>[args.length];
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
types[i] = args[i].getClass();
|
||||
}
|
||||
|
||||
PartTreeMongoQuery partTreeQuery = createQueryForMethod(method, types);
|
||||
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(partTreeQuery.getQueryMethod(), args);
|
||||
return partTreeQuery.createQuery(new ConvertingParameterAccessor(mongoOperationsMock.getConverter(), accessor));
|
||||
}
|
||||
|
||||
private PartTreeMongoQuery createQueryForMethod(String methodName, Class<?>... paramTypes) {
|
||||
|
||||
try {
|
||||
|
||||
Method method = Repo.class.getMethod(methodName, paramTypes);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadataMock, mappingContext);
|
||||
|
||||
return new PartTreeMongoQuery(queryMethod, mongoOperationsMock);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException(e.getMessage(), e);
|
||||
} catch (SecurityException e) {
|
||||
throw new IllegalArgumentException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
interface Repo extends MongoRepository<Person, Long> {
|
||||
|
||||
@Query(fields = "firstname")
|
||||
Person findByLastname(String lastname);
|
||||
|
||||
@Query(fields = "{ 'firstname' : 1 }")
|
||||
Person findByFirstname(String lastname);
|
||||
|
||||
@Query(fields = "{ 'firstname' : 1, 'lastname' : 1 }")
|
||||
Person findByFirstnameAndLastname(String firstname, String lastname);
|
||||
|
||||
@Query(fields = "{ 'firstname' : 0, 'lastname' : 0 }")
|
||||
Person findPersonByFirstnameAndLastname(String firstname, String lastname);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2013 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.
|
||||
@@ -45,6 +45,7 @@ import com.mongodb.DBObject;
|
||||
* Unit tests for {@link StringBasedMongoQuery}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StringBasedMongoQueryUnitTests {
|
||||
@@ -126,6 +127,19 @@ public class StringBasedMongoQueryUnitTests {
|
||||
assertThat(query.getQueryObject().get("address"), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-821
|
||||
*/
|
||||
@Test
|
||||
public void bindsDbrefCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByHavingSizeFansNotZero");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] {});
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
assertThat(query.getQueryObject(), is(new BasicQuery("{ fans : { $not : { $size : 0 } } }").getQueryObject()));
|
||||
}
|
||||
|
||||
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod(name, parameters);
|
||||
@@ -143,5 +157,9 @@ public class StringBasedMongoQueryUnitTests {
|
||||
|
||||
@Query("{ 'lastname' : ?0, 'address' : ?1 }")
|
||||
Person findByLastnameAndAddress(String lastname, Address address);
|
||||
|
||||
@Query("{ fans : { $not : { $size : 0 } } }")
|
||||
Person findByHavingSizeFansNotZero();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.0.RELEASE/src/docbkx/repositories.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.3.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.0.RELEASE/src/docbkx/repository-namespace-reference.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.3.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.0.RELEASE/src/docbkx/repository-query-keywords-reference.xml">
|
||||
<xi:include href="https://raw.github.com/spring-projects/spring-data-commons/1.7.3.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.3.4.RELEASE</version>
|
||||
<version>1.4.2.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies></programlisting>
|
||||
|
||||
@@ -1,9 +1,155 @@
|
||||
Spring Data MongoDB Changelog
|
||||
=============================
|
||||
|
||||
Changes in version 1.4.0.RELEASE (2014-02-24)
|
||||
Changes in version 1.4.3.RELEASE (2014-06-18)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-955 - Release 1.4.3.
|
||||
* DATAMONGO-953 - Update object should have a proper equals/hashcode/toString.
|
||||
* DATAMONGO-952 - @Query annotation does not work with only field restrictions.
|
||||
* DATAMONGO-948 - Assertion error in MongoTemplate.getMappedSortObject.
|
||||
* DATAMONGO-938 - Exception when creating geo within Criteria using MapReduce.
|
||||
* DATAMONGO-924 - Aggregation not working with as() method in project() pipeline operator.
|
||||
* DATAMONGO-920 - Fix debug messages for delete events in AbstractMongoEventListener.
|
||||
* DATAMONGO-917 - DefaultDbRefResolver throws NPE when bundled into an uberjar.
|
||||
* DATAMONGO-914 - Improve resolving of LazyLoading proxies for classes that override equals/hashcode.
|
||||
* DATAMONGO-913 - Can't query using lazy DBRef objects.
|
||||
* DATAMONGO-912 - Aggregation#project followed by Aggregation#match with custom converter causes IllegalArgumentException.
|
||||
* DATAMONGO-898 - MapReduce seems not to work when javascript not being escaped.
|
||||
* DATAMONGO-847 - Allow usage of Criteria within Update.
|
||||
* DATAMONGO-745 - @Query($in) and Pageable in result Page total = 0.
|
||||
* DATAMONGO-647 - Using "OrderBy" in "query by method name" ignores the @Field annotation for field alias.
|
||||
|
||||
|
||||
Changes in version 1.5.0.RELEASE (2014-05-20)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-936 - Release 1.5 GA.
|
||||
* DATAMONGO-929 - Index key should be the properties dot path when creating index using @Indexed / @CompoundIndex.
|
||||
* DATAMONGO-928 - Error when using field-naming-strategy-ref.
|
||||
* DATAMONGO-926 - Stack Overflow Error with 1.5.0.RC1 Release.
|
||||
* DATAMONGO-925 - MappingMongoConverterParser is incorrectly rejecting field-naming-strategy-ref XML configuration.
|
||||
* DATAMONGO-647 - Using "OrderBy" in "query by method name" ignores the @Field annotation for field alias.
|
||||
* DATAMONGO-367 - @Indexed field in embedded Object creates new collection.
|
||||
|
||||
|
||||
Changes in version 1.5.0.RC1 (2014-05-02)
|
||||
-----------------------------------------
|
||||
* DATAMONGO-924 - Aggregation not working with as() method in project() pipeline operator.
|
||||
* DATAMONGO-921 - Upgrade to MongoDB Java driver 2.12.1.
|
||||
* DATAMONGO-920 - Fix debug messages for delete events in AbstractMongoEventListener.
|
||||
* DATAMONGO-919 - Release 1.5 RC1.
|
||||
* DATAMONGO-917 - DefaultDbRefResolver throws NPE when bundled into an uberjar.
|
||||
* DATAMONGO-914 - Improve resolving of LazyLoading proxies for classes that override equals/hashcode.
|
||||
* DATAMONGO-913 - Can't query using lazy DBRef objects.
|
||||
* DATAMONGO-912 - Aggregation#project followed by Aggregation#match with custom converter causes IllegalArgumentException.
|
||||
* DATAMONGO-910 - Upgrade to latest MongoDB Java driver (2.12).
|
||||
* DATAMONGO-909 - @CompoundIndex on inherited entity classes.
|
||||
* DATAMONGO-908 - Nested field references in group operations broken.
|
||||
* DATAMONGO-907 - Assert compatibility with mongodb 2.6.0.
|
||||
* DATAMONGO-905 - Remove obsolete CGLib dependency from cross store module.
|
||||
* DATAMONGO-901 - MongoRepositoryConfigurationExtension fails to invoke super method.
|
||||
* DATAMONGO-899 - Overhaul automatic index creation.
|
||||
* DATAMONGO-898 - MapReduce seems not to work when javascript not being escaped.
|
||||
* DATAMONGO-897 - FindAndUpdate broken when using @DbRef and interface as target.
|
||||
* DATAMONGO-896 - Assert compatibility with latest MongoDB Java driver.
|
||||
* DATAMONGO-895 - Use most specific type for checks against values in DBObjects.
|
||||
* DATAMONGO-893 - Mapping Convertor does not remove "_class" property on collection of embedded objects.
|
||||
* DATAMONGO-892 - <mongo:mapping-converter> can't be configured as nested bean definition.
|
||||
* DATAMONGO-888 - Mapping is not applied to SortObject during queries.
|
||||
* DATAMONGO-866 - Add new field naming strategy and make it configurable through XML/Java config.
|
||||
* DATAMONGO-847 - Allow usage of Criteria within Update.
|
||||
* DATAMONGO-827 - @Indexed and @CompundIndex cannot be created without giving index name.
|
||||
|
||||
|
||||
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
|
||||
* [DATAMONGO-773] - Verify that @DBRef fields can be included in query.
|
||||
* [DATAMONGO-821] - Fixed handling of keyword expressions for DBRefs.
|
||||
* [DATAMONGO-829] - NearQuery should not default 'num' to zero.
|
||||
* [DATAMONGO-833] - Support for EnumSet and EnumMap in MappingMongoConverter.
|
||||
* [DATAMONGO-843] - Back-port of defaulting of the MappingContext for auditing.
|
||||
* [DATAMONGO-862] - Fixed handling of unmapped paths for updates.
|
||||
* [DATAMONGO-863] - UpdateMapper doesn't convert raw DBObjects anymore.
|
||||
* [DATAMONGO-868] - MongoTemplate.findAndModify(…) increases version if not handled manually.
|
||||
* [DATAMONGO-871] - Add support for arrays as query method return types.
|
||||
* [DATAMONGO-877] - Added guard against null-package in AbstractMappingConfiguration.
|
||||
|
||||
** Improvement
|
||||
* [DATAMONGO-865] - Adjust test dependencies to avoid ClassNotFoundException during test runs.
|
||||
|
||||
Changes in version 1.3.5.RELEASE (2014-03-10)
|
||||
---------------------------------------------
|
||||
** Fix
|
||||
* [DATAMONGO-829] - NearQuery, when used in conjunction with a Query, no longer sets num=0, unless Query specifies otherwise.
|
||||
* [DATAMONGO-871] - Repository queries support array return type.
|
||||
|
||||
** Improvement
|
||||
* [DATAMONGO-865] - Avoid ClassNotFoundException during test runs.
|
||||
|
||||
|
||||
Changes in version 1.4.0.RELEASE (2014-02-24)
|
||||
---------------------------------------------
|
||||
** Fix
|
||||
* [DATAMONGO-354] - MongoTemplate should support multiple $pushAll in one update.
|
||||
* [DATAMONGO-404] - Removing a DBRef using pull does not work.
|
||||
@@ -23,7 +169,7 @@ Changes in version 1.4.0.RELEASE (2014-02-24)
|
||||
* [DATAMONGO-848] - Ensure compatibility with Mongo Java driver 2.12.
|
||||
* [DATAMONGO-853] - Update no longer allows null keys.
|
||||
* [DATAMONGO-856] - Update documentation.
|
||||
|
||||
|
||||
Changes in version 1.3.4.RELEASE (2014-02-17)
|
||||
---------------------------------------------
|
||||
** Bug
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data MongoDB 1.4.0.RELEASE
|
||||
Spring Data MongoDB 1.4.3
|
||||
Copyright (c) [2010-2014] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@@ -7,4 +7,4 @@ You may not use this product except in compliance with the License.
|
||||
This product may include a number of subcomponents with
|
||||
separate copyright notices and license terms. Your use of the source
|
||||
code for the these subcomponents is subject to the terms and
|
||||
conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SPRING DATA MongoDB 1.4.0.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