Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41dc57c84f | ||
|
|
85d1fe1ce6 | ||
|
|
ac6067ad53 | ||
|
|
173a62b5ce | ||
|
|
cbbafce73d | ||
|
|
2e74c19995 | ||
|
|
a212b7566c | ||
|
|
08faa52ef4 | ||
|
|
33bc4fffd9 | ||
|
|
eca2108e15 | ||
|
|
dab6034eb9 | ||
|
|
461e7d05d7 | ||
|
|
10c37b101d | ||
|
|
81f2c910f7 | ||
|
|
1fd97713c1 | ||
|
|
2d3eeed9ec | ||
|
|
b22eb6f12f | ||
|
|
dfb0a2a368 | ||
|
|
03bcc56429 | ||
|
|
457fda3fc3 | ||
|
|
54cee64610 | ||
|
|
477499248a | ||
|
|
3b70b6aeee | ||
|
|
163762e99e | ||
|
|
b99833df75 | ||
|
|
4be6231426 | ||
|
|
4673e3d511 | ||
|
|
00e48cc424 | ||
|
|
f8453825fb | ||
|
|
6cda9ab939 | ||
|
|
831d667896 | ||
|
|
17c342895a | ||
|
|
6ef518e6a0 | ||
|
|
ddee2fbb12 | ||
|
|
6512c2cdfb | ||
|
|
0eee05adaa | ||
|
|
17e0154ff3 | ||
|
|
2780f60c65 | ||
|
|
7dd3450362 | ||
|
|
ca4b2a61b8 | ||
|
|
d2ecd65ca5 | ||
|
|
03bd49f6c8 | ||
|
|
51607c5ed8 | ||
|
|
e2cbd3ee28 | ||
|
|
5944e6b57e | ||
|
|
efd46498ef | ||
|
|
3d705a737f | ||
|
|
996c57bccf | ||
|
|
a31e72ff06 | ||
|
|
f07d8fca8c | ||
|
|
69dbdee01f | ||
|
|
dedb9f3dc0 | ||
|
|
7d69b840fe | ||
|
|
4eaef300cb | ||
|
|
ec1a6b5edd | ||
|
|
adc5485c09 | ||
|
|
f622b2916d | ||
|
|
26be0cf948 |
6
pom.xml
6
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.6.0.RC1</version>
|
||||
<version>1.7.0.M1</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.5.0.RC1</version>
|
||||
<version>1.6.0.M1</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.9.0.RC1</springdata.commons>
|
||||
<springdata.commons>1.10.0.M1</springdata.commons>
|
||||
<mongo>2.12.3</mongo>
|
||||
<mongo.osgi>2.12.3</mongo.osgi>
|
||||
</properties>
|
||||
|
||||
7
spring-data-mongodb-cross-store/aop.xml
Normal file
7
spring-data-mongodb-cross-store/aop.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<aspectj>
|
||||
<aspects>
|
||||
<aspect name="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect" />
|
||||
<aspect name="org.springframework.data.mongodb.crossstore.MongoDocumentBacking" />
|
||||
</aspects>
|
||||
</aspectj>
|
||||
@@ -2,22 +2,22 @@
|
||||
<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/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.6.0.RC1</version>
|
||||
<version>1.7.0.M1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
<artifactId>spring-data-mongodb-cross-store</artifactId>
|
||||
<name>Spring Data MongoDB - Cross-Store Support</name>
|
||||
|
||||
|
||||
<properties>
|
||||
<jpa>1.0.0.Final</jpa>
|
||||
<hibernate>3.6.10.Final</hibernate>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring -->
|
||||
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
<version>1.6.0.RC1</version>
|
||||
<version>1.7.0.M1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -126,10 +126,11 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
</aspectLibrary>
|
||||
</aspectLibraries>
|
||||
</aspectLibraries>
|
||||
<complianceLevel>${source.level}</complianceLevel>
|
||||
<source>${source.level}</source>
|
||||
<target>${source.level}</target>
|
||||
<xmlConfigured>aop.xml</xmlConfigured>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.6.0.RC1</version>
|
||||
<version>1.7.0.M1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.6.0.RC1</version>
|
||||
<version>1.7.0.M1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<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>
|
||||
|
||||
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
|
||||
<name>Spring Data MongoDB - Core</name>
|
||||
@@ -11,18 +11,19 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>1.6.0.RC1</version>
|
||||
<version>1.7.0.M1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<validation>1.0.0.GA</validation>
|
||||
<objenesis>1.3</objenesis>
|
||||
<equalsverifier>1.5</equalsverifier>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring -->
|
||||
|
||||
<!-- Spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
@@ -50,7 +51,7 @@
|
||||
<artifactId>spring-expression</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Data -->
|
||||
<!-- Spring Data -->
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
@@ -77,7 +78,7 @@
|
||||
<version>1.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- CDI -->
|
||||
<dependency>
|
||||
<groupId>javax.enterprise</groupId>
|
||||
@@ -86,21 +87,21 @@
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.el</groupId>
|
||||
<artifactId>el-api</artifactId>
|
||||
<version>${cdi}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.openwebbeans.test</groupId>
|
||||
<artifactId>cditest-owb</artifactId>
|
||||
<version>${webbeans}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
@@ -115,7 +116,7 @@
|
||||
<version>${validation}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.objenesis</groupId>
|
||||
<artifactId>objenesis</artifactId>
|
||||
@@ -129,23 +130,29 @@
|
||||
<version>4.2.0.Final</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>${jodatime}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
<version>${slf4j}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>nl.jqno.equalsverifier</groupId>
|
||||
<artifactId>equalsverifier</artifactId>
|
||||
<version>${equalsverifier}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -189,9 +196,14 @@
|
||||
<systemPropertyVariables>
|
||||
<java.util.logging.config.file>src/test/resources/logging.properties</java.util.logging.config.file>
|
||||
</systemPropertyVariables>
|
||||
<properties>
|
||||
<property>
|
||||
<name>listener</name>
|
||||
<value>org.springframework.data.mongodb.test.util.CleanMongoDBJunitRunListener</value>
|
||||
</property>
|
||||
</properties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -55,8 +55,9 @@ import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.GeoResult;
|
||||
import org.springframework.data.geo.GeoResults;
|
||||
import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
@@ -745,8 +746,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
MongoPersistentEntity<?> mongoPersistentEntity = getPersistentEntity(entity.getClass());
|
||||
|
||||
if (mongoPersistentEntity != null && mongoPersistentEntity.hasVersionProperty()) {
|
||||
BeanWrapper<Object> wrapper = BeanWrapper.create(entity, this.mongoConverter.getConversionService());
|
||||
wrapper.setProperty(mongoPersistentEntity.getVersionProperty(), 0);
|
||||
ConvertingPropertyAccessor accessor = new ConvertingPropertyAccessor(
|
||||
mongoPersistentEntity.getPropertyAccessor(entity), mongoConverter.getConversionService());
|
||||
accessor.setProperty(mongoPersistentEntity.getVersionProperty(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -839,11 +841,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
private <T> void doSaveVersioned(T objectToSave, MongoPersistentEntity<?> entity, String collectionName) {
|
||||
|
||||
BeanWrapper<T> beanWrapper = BeanWrapper.create(objectToSave, this.mongoConverter.getConversionService());
|
||||
ConvertingPropertyAccessor convertingAccessor = new ConvertingPropertyAccessor(
|
||||
entity.getPropertyAccessor(objectToSave), mongoConverter.getConversionService());
|
||||
|
||||
MongoPersistentProperty idProperty = entity.getIdProperty();
|
||||
MongoPersistentProperty versionProperty = entity.getVersionProperty();
|
||||
|
||||
Number version = beanWrapper.getProperty(versionProperty, Number.class);
|
||||
Object version = convertingAccessor.getProperty(versionProperty);
|
||||
Number versionNumber = convertingAccessor.getProperty(versionProperty, Number.class);
|
||||
|
||||
// Fresh instance -> initialize version property
|
||||
if (version == null) {
|
||||
@@ -853,12 +858,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
assertUpdateableIdIfNotSet(objectToSave);
|
||||
|
||||
// Create query for entity with the id and old version
|
||||
Object id = beanWrapper.getProperty(idProperty);
|
||||
Object id = convertingAccessor.getProperty(idProperty);
|
||||
Query query = new Query(Criteria.where(idProperty.getName()).is(id).and(versionProperty.getName()).is(version));
|
||||
|
||||
// Bump version number
|
||||
Number number = beanWrapper.getProperty(versionProperty, Number.class);
|
||||
beanWrapper.setProperty(versionProperty, number.longValue() + 1);
|
||||
convertingAccessor.setProperty(versionProperty, versionNumber.longValue() + 1);
|
||||
|
||||
BasicDBObject dbObject = new BasicDBObject();
|
||||
|
||||
@@ -1007,8 +1011,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
update.getUpdateObject(), entity);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Calling update using query: " + queryObj + " and update: " + updateObj + " in collection: "
|
||||
+ collectionName);
|
||||
LOGGER.debug(String.format("Calling update using query: %s and update: %s in collection: %s",
|
||||
serializeToJsonSafely(queryObj), serializeToJsonSafely(updateObj), collectionName));
|
||||
}
|
||||
|
||||
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName,
|
||||
@@ -1089,12 +1093,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(objectType);
|
||||
MongoPersistentProperty idProp = entity == null ? null : entity.getIdProperty();
|
||||
|
||||
if (idProp == null) {
|
||||
if (idProp == null || entity == null) {
|
||||
throw new MappingException("No id property found for object of type " + objectType);
|
||||
}
|
||||
|
||||
Object idValue = BeanWrapper.create(object, mongoConverter.getConversionService())
|
||||
.getProperty(idProp, Object.class);
|
||||
Object idValue = entity.getPropertyAccessor(object).getProperty(idProp);
|
||||
return Collections.singletonMap(idProp.getFieldName(), idValue).entrySet().iterator().next();
|
||||
}
|
||||
|
||||
@@ -1138,12 +1141,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entity.getClass());
|
||||
MongoPersistentProperty idProperty = persistentEntity == null ? null : persistentEntity.getIdProperty();
|
||||
|
||||
if (idProperty == null) {
|
||||
if (idProperty == null || persistentEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConversionService service = mongoConverter.getConversionService();
|
||||
Object idValue = BeanWrapper.create(entity, service).getProperty(idProperty, Object.class);
|
||||
Object idValue = persistentEntity.getPropertyAccessor(entity).getProperty(idProperty);
|
||||
|
||||
if (idValue == null && !MongoSimpleTypes.AUTOGENERATED_ID_TYPES.contains(idProperty.getType())) {
|
||||
throw new InvalidDataAccessApiUsageException(String.format(
|
||||
@@ -1187,7 +1189,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Remove using query: {} in collection: {}.", new Object[] { dboq, collection.getName() });
|
||||
LOGGER.debug("Remove using query: {} in collection: {}.",
|
||||
new Object[] { serializeToJsonSafely(dboq), collection.getName() });
|
||||
}
|
||||
|
||||
WriteResult wr = writeConcernToUse == null ? collection.remove(dboq) : collection.remove(dboq,
|
||||
@@ -1622,7 +1625,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(String.format("find using query: %s fields: %s for class: %s in collection: %s",
|
||||
serializeToJsonSafely(query), mappedFields, entityClass, collectionName));
|
||||
serializeToJsonSafely(mappedQuery), mappedFields, entityClass, collectionName));
|
||||
}
|
||||
|
||||
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields), preparer, objectCallback,
|
||||
@@ -1660,8 +1663,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
Class<T> entityClass) {
|
||||
EntityReader<? super T, DBObject> readerToUse = this.mongoConverter;
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("findAndRemove using query: " + query + " fields: " + fields + " sort: " + sort + " for class: "
|
||||
+ entityClass + " in collection: " + collectionName);
|
||||
LOGGER.debug(String.format("findAndRemove using query: %s fields: %s sort: %s for class: %s in collection: %s",
|
||||
serializeToJsonSafely(query), fields, sort, entityClass, collectionName));
|
||||
}
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
return executeFindOneInternal(new FindAndRemoveCallback(queryMapper.getMappedObject(query, entity), fields, sort),
|
||||
@@ -1685,8 +1688,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
DBObject mappedUpdate = updateMapper.getMappedObject(update.getUpdateObject(), entity);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("findAndModify using query: " + mappedQuery + " fields: " + fields + " sort: " + sort
|
||||
+ " for class: " + entityClass + " and update: " + mappedUpdate + " in collection: " + collectionName);
|
||||
LOGGER.debug(String.format("findAndModify using query: %s fields: %s sort: %s for class: %s and update: %s " +
|
||||
"in collection: %s", serializeToJsonSafely(mappedQuery), fields, sort, entityClass,
|
||||
serializeToJsonSafely(mappedUpdate), collectionName));
|
||||
}
|
||||
|
||||
return executeFindOneInternal(new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate, options),
|
||||
@@ -1718,15 +1722,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
}
|
||||
|
||||
ConversionService conversionService = mongoConverter.getConversionService();
|
||||
BeanWrapper<Object> wrapper = BeanWrapper.create(savedObject, conversionService);
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(savedObject.getClass());
|
||||
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(savedObject);
|
||||
|
||||
Object idValue = wrapper.getProperty(idProp, idProp.getType());
|
||||
|
||||
if (idValue != null) {
|
||||
if (accessor.getProperty(idProp) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper.setProperty(idProp, id);
|
||||
new ConvertingPropertyAccessor(accessor, conversionService).setProperty(idProp, id);
|
||||
}
|
||||
|
||||
private DBCollection getAndPrepareCollection(DB db, String collectionName) {
|
||||
@@ -1995,13 +1998,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
public DBObject doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
if (fields == null) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("findOne using query: " + query + " in db.collection: " + collection.getFullName());
|
||||
LOGGER.debug(String.format("findOne using query: %s in db.collection: %s",
|
||||
serializeToJsonSafely(query), collection.getFullName()));
|
||||
}
|
||||
return collection.findOne(query);
|
||||
} else {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("findOne using query: " + query + " fields: " + fields + " in db.collection: "
|
||||
+ collection.getFullName());
|
||||
LOGGER.debug(String.format("findOne using query: %s fields: %s in db.collection: %s",
|
||||
serializeToJsonSafely(query), fields, collection.getFullName()));
|
||||
}
|
||||
return collection.findOne(query, fields);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.core.convert.converter.GenericConverter;
|
||||
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.convert.JodaTimeConverters;
|
||||
import org.springframework.data.convert.Jsr310Converters;
|
||||
import org.springframework.data.convert.ReadingConverter;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
@@ -112,6 +113,7 @@ public class CustomConversions {
|
||||
|
||||
toRegister.addAll(JodaTimeConverters.getConvertersToRegister());
|
||||
toRegister.addAll(GeoConverters.getConvertersToRegister());
|
||||
toRegister.addAll(Jsr310Converters.getConvertersToRegister());
|
||||
|
||||
for (Object c : toRegister) {
|
||||
registerConversion(c);
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
|
||||
import org.springframework.data.mapping.model.SpELContext;
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
@@ -60,19 +60,19 @@ class DefaultDbRefProxyHandler implements DbRefProxyHandler {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(property);
|
||||
MongoPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
if(idProperty.usePropertyAccess()) {
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(property);
|
||||
MongoPersistentProperty idProperty = entity.getIdProperty();
|
||||
|
||||
if (idProperty.usePropertyAccess()) {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
||||
SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(proxy, spELContext);
|
||||
BeanWrapper<Object> proxyWrapper = BeanWrapper.create(proxy, null);
|
||||
|
||||
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(proxy);
|
||||
|
||||
DBObject object = new BasicDBObject(idProperty.getFieldName(), source.getId());
|
||||
ObjectPath objectPath = ObjectPath.ROOT.push(proxy, persistentEntity, null);
|
||||
proxyWrapper.setProperty(idProperty, resolver.getValueInternal(idProperty, object, evaluator, objectPath));
|
||||
ObjectPath objectPath = ObjectPath.ROOT.push(proxy, entity, null);
|
||||
accessor.setProperty(idProperty, resolver.getValueInternal(idProperty, object, evaluator, objectPath));
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor,
|
||||
Serializable {
|
||||
|
||||
private static final Method INITIALIZE_METHOD, TO_DBREF_METHOD;
|
||||
private static final Method INITIALIZE_METHOD, TO_DBREF_METHOD, FINALIZE_METHOD;
|
||||
|
||||
private final DbRefResolverCallback callback;
|
||||
private final MongoPersistentProperty property;
|
||||
@@ -192,6 +192,7 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
try {
|
||||
INITIALIZE_METHOD = LazyLoadingProxy.class.getMethod("getTarget");
|
||||
TO_DBREF_METHOD = LazyLoadingProxy.class.getMethod("toDBRef");
|
||||
FINALIZE_METHOD = Object.class.getDeclaredMethod("finalize");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -255,6 +256,11 @@ public class DefaultDbRefResolver implements DbRefResolver {
|
||||
if (ReflectionUtils.isHashCodeMethod(method)) {
|
||||
return proxyHashCode(proxy);
|
||||
}
|
||||
|
||||
// DATAMONGO-1076 - finalize methods should not trigger proxy initialization
|
||||
if (FINALIZE_METHOD.equals(method)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Object target = ensureResolved();
|
||||
|
||||
@@ -37,10 +37,11 @@ import org.springframework.data.convert.EntityInstantiator;
|
||||
import org.springframework.data.convert.TypeMapper;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.PreferredConstructor.Parameter;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
@@ -76,6 +77,8 @@ import com.mongodb.DBRef;
|
||||
*/
|
||||
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware, ValueResolver {
|
||||
|
||||
private static final String INCOMPATIBLE_TYPES = "Cannot convert %1$s of type %2$s into an instance of %3$s! Implement a custom Converter<%2$s, %3$s> and register it with the CustomConversions. Parent object was: %4$s";
|
||||
|
||||
protected static final Logger LOGGER = LoggerFactory.getLogger(MappingMongoConverter.class);
|
||||
|
||||
protected final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
@@ -214,6 +217,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return (S) readMap(typeToUse, dbo, path);
|
||||
}
|
||||
|
||||
if (dbo instanceof BasicDBList) {
|
||||
throw new MappingException(String.format(INCOMPATIBLE_TYPES, dbo, BasicDBList.class, typeToUse.getType(), path));
|
||||
}
|
||||
|
||||
// Retrieve persistent entity info
|
||||
MongoPersistentEntity<S> persistentEntity = (MongoPersistentEntity<S>) mappingContext
|
||||
.getPersistentEntity(typeToUse);
|
||||
@@ -242,16 +249,18 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
|
||||
S instance = instantiator.createInstance(entity, provider);
|
||||
|
||||
final BeanWrapper<S> wrapper = BeanWrapper.create(instance, conversionService);
|
||||
final PersistentPropertyAccessor accessor = new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance),
|
||||
conversionService);
|
||||
|
||||
final MongoPersistentProperty idProperty = entity.getIdProperty();
|
||||
final S result = wrapper.getBean();
|
||||
final S result = instance;
|
||||
|
||||
// make sure id property is set before all other properties
|
||||
Object idValue = null;
|
||||
|
||||
if (idProperty != null) {
|
||||
idValue = getValueInternal(idProperty, dbo, evaluator, path);
|
||||
wrapper.setProperty(idProperty, idValue);
|
||||
accessor.setProperty(idProperty, idValue);
|
||||
}
|
||||
|
||||
final ObjectPath currentPath = path.push(result, entity, idValue);
|
||||
@@ -269,7 +278,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper.setProperty(prop, getValueInternal(prop, dbo, evaluator, currentPath));
|
||||
accessor.setProperty(prop, getValueInternal(prop, dbo, evaluator, currentPath));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -278,7 +287,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
public void doWithAssociation(Association<MongoPersistentProperty> association) {
|
||||
|
||||
final MongoPersistentProperty property = association.getInverse();
|
||||
Object value = dbo.get(property.getName());
|
||||
Object value = dbo.get(property.getFieldName());
|
||||
|
||||
if (value == null) {
|
||||
return;
|
||||
@@ -291,7 +300,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
DbRefResolverCallback callback = new DefaultDbRefResolverCallback(dbo, currentPath, evaluator,
|
||||
MappingMongoConverter.this);
|
||||
|
||||
wrapper.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, callback, handler));
|
||||
accessor.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, callback, handler));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -391,13 +400,13 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
throw new MappingException("No mapping metadata found for entity of type " + obj.getClass().getName());
|
||||
}
|
||||
|
||||
final BeanWrapper<Object> wrapper = BeanWrapper.create(obj, conversionService);
|
||||
final PersistentPropertyAccessor accessor = entity.getPropertyAccessor(obj);
|
||||
final MongoPersistentProperty idProperty = entity.getIdProperty();
|
||||
|
||||
if (!dbo.containsField("_id") && null != idProperty) {
|
||||
|
||||
try {
|
||||
Object id = wrapper.getProperty(idProperty, Object.class);
|
||||
Object id = accessor.getProperty(idProperty);
|
||||
dbo.put("_id", idMapper.convertId(id));
|
||||
} catch (ConversionException ignored) {}
|
||||
}
|
||||
@@ -410,7 +419,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return;
|
||||
}
|
||||
|
||||
Object propertyObj = wrapper.getProperty(prop);
|
||||
Object propertyObj = accessor.getProperty(prop);
|
||||
|
||||
if (null != propertyObj) {
|
||||
|
||||
@@ -424,10 +433,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
});
|
||||
|
||||
entity.doWithAssociations(new AssociationHandler<MongoPersistentProperty>() {
|
||||
|
||||
public void doWithAssociation(Association<MongoPersistentProperty> association) {
|
||||
|
||||
MongoPersistentProperty inverseProp = association.getInverse();
|
||||
Class<?> type = inverseProp.getType();
|
||||
Object propertyObj = wrapper.getProperty(inverseProp, type);
|
||||
Object propertyObj = accessor.getProperty(inverseProp);
|
||||
|
||||
if (null != propertyObj) {
|
||||
writePropertyInternal(propertyObj, dbo, inverseProp);
|
||||
}
|
||||
@@ -497,7 +508,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
Object existingValue = accessor.get(prop);
|
||||
BasicDBObject propDbObj = existingValue instanceof BasicDBObject ? (BasicDBObject) existingValue
|
||||
: new BasicDBObject();
|
||||
addCustomTypeKeyIfNecessary(type, obj, propDbObj);
|
||||
addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, propDbObj);
|
||||
|
||||
MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass()) ? mappingContext
|
||||
.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type);
|
||||
@@ -694,7 +705,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
/**
|
||||
* Adds custom type information to the given {@link DBObject} if necessary. That is if the value is not the same as
|
||||
* the one given. This is usually the case if you store a subtype of the actual declared type of the property.
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* @param value must not be {@literal null}.
|
||||
* @param dbObject must not be {@literal null}.
|
||||
@@ -801,8 +812,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
if (target.getClass().equals(idProperty.getType())) {
|
||||
id = target;
|
||||
} else {
|
||||
BeanWrapper<Object> wrapper = BeanWrapper.create(target, conversionService);
|
||||
id = wrapper.getProperty(idProperty, Object.class);
|
||||
PersistentPropertyAccessor accessor = targetEntity.getPropertyAccessor(target);
|
||||
id = accessor.getProperty(idProperty);
|
||||
}
|
||||
|
||||
if (null == id) {
|
||||
@@ -938,7 +949,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
return getPotentiallyConvertedSimpleWrite(obj);
|
||||
}
|
||||
|
||||
TypeInformation<?> typeHint = typeInformation == null ? ClassTypeInformation.OBJECT : typeInformation;
|
||||
TypeInformation<?> typeHint = typeInformation;
|
||||
|
||||
if (obj instanceof BasicDBList) {
|
||||
return maybeConvertList((BasicDBList) obj, typeHint);
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
@@ -120,6 +121,26 @@ class ObjectPath {
|
||||
return items.isEmpty() ? null : items.get(items.size() - 1).getObject();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
if (items.isEmpty()) {
|
||||
return "[empty]";
|
||||
}
|
||||
|
||||
List<String> strings = new ArrayList<String>(items.size());
|
||||
|
||||
for (ObjectPathItem item : items) {
|
||||
strings.add(item.object.toString());
|
||||
}
|
||||
|
||||
return StringUtils.collectionToDelimitedString(strings, " -> ");
|
||||
}
|
||||
|
||||
/**
|
||||
* An item in an {@link ObjectPath}.
|
||||
*
|
||||
|
||||
@@ -334,7 +334,8 @@ public class QueryMapper {
|
||||
}
|
||||
|
||||
MongoPersistentEntity<?> entity = documentField.getPropertyEntity();
|
||||
return entity.hasIdProperty() && entity.getIdProperty().getActualType().isAssignableFrom(type);
|
||||
return entity.hasIdProperty()
|
||||
&& (type.equals(DBRef.class) || entity.getIdProperty().getActualType().isAssignableFrom(type));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,10 +383,16 @@ public class QueryMapper {
|
||||
*/
|
||||
protected Object convertAssociation(Object source, MongoPersistentProperty property) {
|
||||
|
||||
if (property == null || source == null || source instanceof DBRef || source instanceof DBObject) {
|
||||
if (property == null || source == null || source instanceof DBObject) {
|
||||
return source;
|
||||
}
|
||||
|
||||
if (source instanceof DBRef) {
|
||||
|
||||
DBRef ref = (DBRef) source;
|
||||
return new DBRef(ref.getDB(), ref.getRef(), convertId(ref.getId()));
|
||||
}
|
||||
|
||||
if (source instanceof Iterable) {
|
||||
BasicDBList result = new BasicDBList();
|
||||
for (Object element : (Iterable<?>) source) {
|
||||
@@ -785,7 +792,7 @@ public class QueryMapper {
|
||||
*/
|
||||
@Override
|
||||
public String getMappedKey() {
|
||||
return path == null ? name : path.toDotPath(getPropertyConverter());
|
||||
return path == null ? name : path.toDotPath(isAssociation() ? getAssociationConverter() : getPropertyConverter());
|
||||
}
|
||||
|
||||
protected PersistentPropertyPath<MongoPersistentProperty> getPath() {
|
||||
@@ -837,5 +844,56 @@ public class QueryMapper {
|
||||
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
|
||||
return PropertyToFieldNameConverter.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Converter} to use for creating the mapped key of an association. Default implementation is
|
||||
* {@link AssociationConverter}.
|
||||
*
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
|
||||
return new AssociationConverter(getAssociation());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converter to skip all properties after an association property was rendered.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
protected static class AssociationConverter implements Converter<MongoPersistentProperty, String> {
|
||||
|
||||
private final MongoPersistentProperty property;
|
||||
private boolean associationFound;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AssociationConverter} for the given {@link Association}.
|
||||
*
|
||||
* @param association must not be {@literal null}.
|
||||
*/
|
||||
public AssociationConverter(Association<MongoPersistentProperty> association) {
|
||||
|
||||
Assert.notNull(association, "Association must not be null!");
|
||||
this.property = association.getInverse();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public String convert(MongoPersistentProperty source) {
|
||||
|
||||
if (associationFound) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (property.equals(source)) {
|
||||
associationFound = true;
|
||||
}
|
||||
|
||||
return source.getFieldName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,47 +194,48 @@ public class UpdateMapper extends QueryMapper {
|
||||
*/
|
||||
@Override
|
||||
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
|
||||
return isAssociation() ? new AssociationConverter(getAssociation()) : new UpdatePropertyConverter(key);
|
||||
return new UpdatePropertyConverter(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.QueryMapper.MetadataBackedField#getAssociationConverter()
|
||||
*/
|
||||
@Override
|
||||
protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
|
||||
return new UpdateAssociationConverter(getAssociation(), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converter to skip all properties after an association property was rendered.
|
||||
* Special mapper handling positional parameter {@literal $} within property names.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
private static class AssociationConverter implements Converter<MongoPersistentProperty, String> {
|
||||
private static class UpdateKeyMapper {
|
||||
|
||||
private final MongoPersistentProperty property;
|
||||
private boolean associationFound;
|
||||
private final Iterator<String> iterator;
|
||||
|
||||
protected UpdateKeyMapper(String rawKey) {
|
||||
|
||||
Assert.hasText(rawKey, "Key must not be null or empty!");
|
||||
|
||||
this.iterator = Arrays.asList(rawKey.split("\\.")).iterator();
|
||||
this.iterator.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link AssociationConverter} for the given {@link Association}.
|
||||
* Maps the property name while retaining potential positional operator {@literal $}.
|
||||
*
|
||||
* @param association must not be {@literal null}.
|
||||
* @param property
|
||||
* @return
|
||||
*/
|
||||
public AssociationConverter(Association<MongoPersistentProperty> association) {
|
||||
protected String mapPropertyName(MongoPersistentProperty property) {
|
||||
|
||||
Assert.notNull(association, "Association must not be null!");
|
||||
this.property = association.getInverse();
|
||||
String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property);
|
||||
return iterator.hasNext() && iterator.next().equals("$") ? String.format("%s.$", mappedName) : mappedName;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public String convert(MongoPersistentProperty source) {
|
||||
|
||||
if (associationFound) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (property.equals(source)) {
|
||||
associationFound = true;
|
||||
}
|
||||
|
||||
return source.getFieldName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,10 +243,11 @@ public class UpdateMapper extends QueryMapper {
|
||||
* contained in the source update key.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
private static class UpdatePropertyConverter implements Converter<MongoPersistentProperty, String> {
|
||||
|
||||
private final Iterator<String> iterator;
|
||||
private final UpdateKeyMapper mapper;
|
||||
|
||||
/**
|
||||
* Creates a new {@link UpdatePropertyConverter} with the given update key.
|
||||
@@ -256,8 +258,7 @@ public class UpdateMapper extends QueryMapper {
|
||||
|
||||
Assert.hasText(updateKey, "Update key must not be null or empty!");
|
||||
|
||||
this.iterator = Arrays.asList(updateKey.split("\\.")).iterator();
|
||||
this.iterator.next();
|
||||
this.mapper = new UpdateKeyMapper(updateKey);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -266,9 +267,37 @@ public class UpdateMapper extends QueryMapper {
|
||||
*/
|
||||
@Override
|
||||
public String convert(MongoPersistentProperty property) {
|
||||
return mapper.mapPropertyName(property);
|
||||
}
|
||||
}
|
||||
|
||||
String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property);
|
||||
return iterator.hasNext() && iterator.next().equals("$") ? String.format("%s.$", mappedName) : mappedName;
|
||||
/**
|
||||
* {@link Converter} retaining positional parameter {@literal $} for {@link Association}s.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
protected static class UpdateAssociationConverter extends AssociationConverter {
|
||||
|
||||
private final UpdateKeyMapper mapper;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AssociationConverter} for the given {@link Association}.
|
||||
*
|
||||
* @param association must not be {@literal null}.
|
||||
*/
|
||||
public UpdateAssociationConverter(Association<MongoPersistentProperty> association, String key) {
|
||||
|
||||
super(association);
|
||||
this.mapper = new UpdateKeyMapper(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public String convert(MongoPersistentProperty source) {
|
||||
return super.convert(source) == null ? null : mapper.mapPropertyName(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,43 @@ public @interface CompoundIndex {
|
||||
boolean dropDups() default false;
|
||||
|
||||
/**
|
||||
* The name of the index to be created.
|
||||
* The name of the index to be created. <br />
|
||||
* <br />
|
||||
* The name will only be applied as is when defined on root level. For usage on nested or embedded structures the
|
||||
* provided name will be prefixed with the path leading to the entity. <br />
|
||||
* <br />
|
||||
* The structure below
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @Document
|
||||
* class Root {
|
||||
* Hybrid hybrid;
|
||||
* Nested nested;
|
||||
* }
|
||||
*
|
||||
* @Document
|
||||
* @CompoundIndex(name = "compound_index", def = "{'h1': 1, 'h2': 1}")
|
||||
* class Hybrid {
|
||||
* String h1, h2;
|
||||
* }
|
||||
*
|
||||
* @CompoundIndex(name = "compound_index", def = "{'n1': 1, 'n2': 1}")
|
||||
* class Nested {
|
||||
* String n1, n2;
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* resolves in the following index structures
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* db.root.ensureIndex( { hybrid.h1: 1, hybrid.h2: 1 } , { name: "hybrid.compound_index" } )
|
||||
* db.root.ensureIndex( { nested.n1: 1, nested.n2: 1 } , { name: "nested.compound_index" } )
|
||||
* db.hybrid.ensureIndex( { h1: 1, h2: 1 } , { name: "compound_index" } )
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -93,7 +129,10 @@ public @interface CompoundIndex {
|
||||
* stored in.
|
||||
*
|
||||
* @return
|
||||
* @deprecated The collection name is derived from the domain type. Fixing the collection via this attribute might
|
||||
* result in broken definitions. Will be removed in 1.7.
|
||||
*/
|
||||
@Deprecated
|
||||
String collection() default "";
|
||||
|
||||
/**
|
||||
@@ -104,14 +143,4 @@ public @interface CompoundIndex {
|
||||
*/
|
||||
boolean background() default false;
|
||||
|
||||
/**
|
||||
* Configures the number of seconds after which the collection should expire. Defaults to -1 for no expiry.
|
||||
*
|
||||
* @deprecated TTL cannot be defined for {@link CompoundIndex} having more than one field as key. Will be removed in
|
||||
* 1.6.
|
||||
* @see http://docs.mongodb.org/manual/tutorial/expire-data/
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
int expireAfterSeconds() default -1;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,41 @@ import java.lang.annotation.Target;
|
||||
public @interface GeoSpatialIndexed {
|
||||
|
||||
/**
|
||||
* Name of the property in the document that contains the [x, y] or radial coordinates to index.
|
||||
* Index name. <br />
|
||||
* <br />
|
||||
* The name will only be applied as is when defined on root level. For usage on nested or embedded structures the
|
||||
* provided name will be prefixed with the path leading to the entity. <br />
|
||||
* <br />
|
||||
* The structure below
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @Document
|
||||
* class Root {
|
||||
* Hybrid hybrid;
|
||||
* Nested nested;
|
||||
* }
|
||||
*
|
||||
* @Document
|
||||
* class Hybrid {
|
||||
* @GeoSpatialIndexed(name="index") Point h1;
|
||||
* }
|
||||
*
|
||||
* class Nested {
|
||||
* @GeoSpatialIndexed(name="index") Point n1;
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* resolves in the following index structures
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* db.root.ensureIndex( { hybrid.h1: "2d" } , { name: "hybrid.index" } )
|
||||
* db.root.ensureIndex( { nested.n1: "2d" } , { name: "nested.index" } )
|
||||
* db.hybrid.ensureIndex( { h1: "2d" } , { name: "index" } )
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -51,7 +85,10 @@ public @interface GeoSpatialIndexed {
|
||||
* Name of the collection in which to create the index.
|
||||
*
|
||||
* @return
|
||||
* @deprecated The collection name is derived from the domain type. Fixing the collection via this attribute might
|
||||
* result in broken definitions. Will be removed in 1.7.
|
||||
*/
|
||||
@Deprecated
|
||||
String collection() default "";
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,17 @@ import com.mongodb.DBObject;
|
||||
public class Index implements IndexDefinition {
|
||||
|
||||
public enum Duplicates {
|
||||
RETAIN, DROP
|
||||
RETAIN, //
|
||||
|
||||
/**
|
||||
* Dropping Duplicates was removed in MongoDB Server 2.8.0-rc0.
|
||||
* <p>
|
||||
* See https://jira.mongodb.org/browse/SERVER-14710
|
||||
*
|
||||
* @deprecated since 1.7.
|
||||
*/
|
||||
@Deprecated//
|
||||
DROP
|
||||
}
|
||||
|
||||
private final Map<String, Direction> fieldSpec = new LinkedHashMap<String, Direction>();
|
||||
|
||||
@@ -58,7 +58,41 @@ public @interface Indexed {
|
||||
boolean dropDups() default false;
|
||||
|
||||
/**
|
||||
* Index name.
|
||||
* Index name. <br />
|
||||
* <br />
|
||||
* The name will only be applied as is when defined on root level. For usage on nested or embedded structures the
|
||||
* provided name will be prefixed with the path leading to the entity. <br />
|
||||
* <br />
|
||||
* The structure below
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @Document
|
||||
* class Root {
|
||||
* Hybrid hybrid;
|
||||
* Nested nested;
|
||||
* }
|
||||
*
|
||||
* @Document
|
||||
* class Hybrid {
|
||||
* @Indexed(name="index") String h1;
|
||||
* }
|
||||
*
|
||||
* class Nested {
|
||||
* @Indexed(name="index") String n1;
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* resolves in the following index structures
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* db.root.ensureIndex( { hybrid.h1: 1 } , { name: "hybrid.index" } )
|
||||
* db.root.ensureIndex( { nested.n1: 1 } , { name: "nested.index" } )
|
||||
* db.hybrid.ensureIndex( { h1: 1} , { name: "index" } )
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -74,10 +108,13 @@ public @interface Indexed {
|
||||
boolean useGeneratedName() default false;
|
||||
|
||||
/**
|
||||
* Colleciton name for index to be created on.
|
||||
* Collection name for index to be created on.
|
||||
*
|
||||
* @return
|
||||
* @deprecated The collection name is derived from the domain type. Fixing the collection via this attribute might
|
||||
* result in broken definitions. Will be removed in 1.7.
|
||||
*/
|
||||
@Deprecated
|
||||
String collection() default "";
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
@@ -96,7 +95,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
Assert.notNull(document, "Given entity is not collection root.");
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions("", root.getCollection(), root.getType()));
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions("", root.getCollection(), root));
|
||||
indexInformation.addAll(potentiallyCreateTextIndexDefinition(root));
|
||||
|
||||
final CycleGuard guard = new CycleGuard();
|
||||
@@ -138,10 +137,11 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
private List<IndexDefinitionHolder> resolveIndexForClass(final Class<?> type, final String path,
|
||||
final String collection, final CycleGuard guard) {
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(path, collection, type));
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
|
||||
|
||||
final List<IndexDefinitionHolder> indexInformation = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions(path, collection, entity));
|
||||
|
||||
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
|
||||
|
||||
@Override
|
||||
@@ -183,14 +183,13 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
}
|
||||
|
||||
private List<IndexDefinitionHolder> potentiallyCreateCompoundIndexDefinitions(String dotPath, String collection,
|
||||
Class<?> type) {
|
||||
MongoPersistentEntity<?> entity) {
|
||||
|
||||
if (AnnotationUtils.findAnnotation(type, CompoundIndexes.class) == null
|
||||
&& AnnotationUtils.findAnnotation(type, CompoundIndex.class) == null) {
|
||||
if (entity.findAnnotation(CompoundIndexes.class) == null && entity.findAnnotation(CompoundIndex.class) == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return createCompoundIndexDefinitions(dotPath, collection, type);
|
||||
return createCompoundIndexDefinitions(dotPath, collection, entity);
|
||||
}
|
||||
|
||||
private Collection<? extends IndexDefinitionHolder> potentiallyCreateTextIndexDefinition(MongoPersistentEntity<?> root) {
|
||||
@@ -221,7 +220,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
}
|
||||
|
||||
private void appendTextIndexInformation(final String dotPath,
|
||||
final TextIndexDefinitionBuilder indexDefinitionBuilder, MongoPersistentEntity<?> entity,
|
||||
final TextIndexDefinitionBuilder indexDefinitionBuilder, final MongoPersistentEntity<?> entity,
|
||||
final TextIndexIncludeOptions includeOptions, final CycleGuard guard) {
|
||||
|
||||
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
|
||||
@@ -231,7 +230,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
guard.protect(persistentProperty, dotPath);
|
||||
|
||||
if (persistentProperty.isLanguageProperty()) {
|
||||
if (persistentProperty.isExplicitLanguageProperty() && !StringUtils.hasText(dotPath)) {
|
||||
indexDefinitionBuilder.withLanguageOverride(persistentProperty.getFieldName());
|
||||
}
|
||||
|
||||
@@ -258,6 +257,10 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
mappingContext.getPersistentEntity(persistentProperty.getActualType()), optionsForNestedType, guard);
|
||||
} catch (CyclicPropertyReferenceException e) {
|
||||
LOGGER.warn(e.getMessage(), e);
|
||||
} catch (InvalidDataAccessApiUsageException e) {
|
||||
LOGGER.warn(
|
||||
String.format("Potentially invald index structure discovered. Breaking operation for %s.",
|
||||
entity.getName()), e);
|
||||
}
|
||||
} else if (includeOptions.isForce() || indexed != null) {
|
||||
indexDefinitionBuilder.onField(propertyDotPath, weight);
|
||||
@@ -278,21 +281,21 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
* @return
|
||||
*/
|
||||
protected List<IndexDefinitionHolder> createCompoundIndexDefinitions(String dotPath, String fallbackCollection,
|
||||
Class<?> type) {
|
||||
MongoPersistentEntity<?> entity) {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = new ArrayList<MongoPersistentEntityIndexResolver.IndexDefinitionHolder>();
|
||||
CompoundIndexes indexes = AnnotationUtils.findAnnotation(type, CompoundIndexes.class);
|
||||
CompoundIndexes indexes = entity.findAnnotation(CompoundIndexes.class);
|
||||
|
||||
if (indexes != null) {
|
||||
for (CompoundIndex index : indexes.value()) {
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index));
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index, entity));
|
||||
}
|
||||
}
|
||||
|
||||
CompoundIndex index = AnnotationUtils.findAnnotation(type, CompoundIndex.class);
|
||||
CompoundIndex index = entity.findAnnotation(CompoundIndex.class);
|
||||
|
||||
if (index != null) {
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index));
|
||||
indexDefinitions.add(createCompoundIndexDefinition(dotPath, fallbackCollection, index, entity));
|
||||
}
|
||||
|
||||
return indexDefinitions;
|
||||
@@ -300,13 +303,13 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected IndexDefinitionHolder createCompoundIndexDefinition(String dotPath, String fallbackCollection,
|
||||
CompoundIndex index) {
|
||||
CompoundIndex index, MongoPersistentEntity<?> entity) {
|
||||
|
||||
CompoundIndexDefinition indexDefinition = new CompoundIndexDefinition(resolveCompoundIndexKeyFromStringDefinition(
|
||||
dotPath, index.def()));
|
||||
|
||||
if (!index.useGeneratedName()) {
|
||||
indexDefinition.named(index.name());
|
||||
indexDefinition.named(pathAwareIndexName(index.name(), dotPath, null));
|
||||
}
|
||||
|
||||
if (index.unique()) {
|
||||
@@ -321,16 +324,6 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
indexDefinition.background();
|
||||
}
|
||||
|
||||
int ttl = index.expireAfterSeconds();
|
||||
|
||||
if (ttl >= 0) {
|
||||
if (indexDefinition.getIndexKeys().keySet().size() > 1) {
|
||||
LOGGER.warn("TTL is not supported for compound index with more than one key. TTL={} will be ignored.", ttl);
|
||||
} else {
|
||||
indexDefinition.expire(ttl, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
String collection = StringUtils.hasText(index.collection()) ? index.collection() : fallbackCollection;
|
||||
return new IndexDefinitionHolder(dotPath, indexDefinition, collection);
|
||||
}
|
||||
@@ -377,7 +370,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
IndexDirection.ASCENDING.equals(index.direction()) ? Sort.Direction.ASC : Sort.Direction.DESC);
|
||||
|
||||
if (!index.useGeneratedName()) {
|
||||
indexDefinition.named(StringUtils.hasText(index.name()) ? index.name() : dotPath);
|
||||
indexDefinition.named(pathAwareIndexName(index.name(), dotPath, persitentProperty));
|
||||
}
|
||||
|
||||
if (index.unique()) {
|
||||
@@ -419,7 +412,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
indexDefinition.withMin(index.min()).withMax(index.max());
|
||||
|
||||
if (!index.useGeneratedName()) {
|
||||
indexDefinition.named(StringUtils.hasText(index.name()) ? index.name() : persistentProperty.getName());
|
||||
indexDefinition.named(pathAwareIndexName(index.name(), dotPath, persistentProperty));
|
||||
}
|
||||
|
||||
indexDefinition.typed(index.type()).withBucketSize(index.bucketSize()).withAdditionalField(index.additionalField());
|
||||
@@ -427,6 +420,23 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
return new IndexDefinitionHolder(dotPath, indexDefinition, collection);
|
||||
}
|
||||
|
||||
private String pathAwareIndexName(String indexName, String dotPath, MongoPersistentProperty property) {
|
||||
|
||||
String nameToUse = StringUtils.hasText(indexName) ? indexName : "";
|
||||
|
||||
if (!StringUtils.hasText(dotPath) || (property != null && dotPath.equals(property.getFieldName()))) {
|
||||
return StringUtils.hasText(nameToUse) ? nameToUse : dotPath;
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(dotPath)) {
|
||||
|
||||
nameToUse = StringUtils.hasText(nameToUse) ? (property != null ? dotPath.replace("." + property.getFieldName(),
|
||||
"") : dotPath) + "." + nameToUse : dotPath;
|
||||
}
|
||||
return nameToUse;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CycleGuard} holds information about properties and the paths for accessing those. This information is used
|
||||
* to detect potential cycles within the references.
|
||||
@@ -530,7 +540,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
return false;
|
||||
}
|
||||
|
||||
return path.contains(this.path);
|
||||
return path.equals(this.path) || path.contains(this.path + ".") || path.contains("." + this.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.data.mongodb.MongoCollectionUtils;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -53,32 +54,37 @@ import org.springframework.util.StringUtils;
|
||||
public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, MongoPersistentProperty> implements
|
||||
MongoPersistentEntity<T>, ApplicationContextAware {
|
||||
|
||||
private static final String AMBIGUOUS_FIELD_MAPPING = "Ambiguous field mapping detected! Both %s and %s map to the same field name %s! Disambiguate using @DocumentField annotation!";
|
||||
private static final String AMBIGUOUS_FIELD_MAPPING = "Ambiguous field mapping detected! Both %s and %s map to the same field name %s! Disambiguate using @Field annotation!";
|
||||
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
private final String collection;
|
||||
private final String language;
|
||||
private final SpelExpressionParser parser;
|
||||
|
||||
private final StandardEvaluationContext context;
|
||||
private final Expression expression;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BasicMongoPersistentEntity} with the given {@link TypeInformation}. Will default the
|
||||
* collection name to the entities simple type name.
|
||||
*
|
||||
* @param typeInformation
|
||||
* @param typeInformation must not be {@literal null}.
|
||||
*/
|
||||
public BasicMongoPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
|
||||
super(typeInformation, MongoPersistentPropertyComparator.INSTANCE);
|
||||
|
||||
this.parser = new SpelExpressionParser();
|
||||
this.context = new StandardEvaluationContext();
|
||||
|
||||
Class<?> rawType = typeInformation.getType();
|
||||
String fallback = MongoCollectionUtils.getPreferredCollectionName(rawType);
|
||||
|
||||
if (rawType.isAnnotationPresent(Document.class)) {
|
||||
Document d = rawType.getAnnotation(Document.class);
|
||||
this.collection = StringUtils.hasText(d.collection()) ? d.collection() : fallback;
|
||||
this.language = StringUtils.hasText(d.language()) ? d.language() : "";
|
||||
Document document = rawType.getAnnotation(Document.class);
|
||||
|
||||
this.expression = detectExpression(document);
|
||||
this.context = new StandardEvaluationContext();
|
||||
|
||||
if (document != null) {
|
||||
|
||||
this.collection = StringUtils.hasText(document.collection()) ? document.collection() : fallback;
|
||||
this.language = StringUtils.hasText(document.language()) ? document.language() : "";
|
||||
} else {
|
||||
this.collection = fallback;
|
||||
this.language = "";
|
||||
@@ -101,8 +107,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentEntity#getCollection()
|
||||
*/
|
||||
public String getCollection() {
|
||||
Expression expression = parser.parseExpression(collection, ParserContext.TEMPLATE_EXPRESSION);
|
||||
return expression.getValue(context, String.class);
|
||||
return expression == null ? collection : expression.getValue(context, String.class);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -236,6 +241,31 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SpEL {@link Expression} frór the collection String expressed in the given {@link Document} annotation if
|
||||
* present or {@literal null} otherwise. Will also return {@literal null} it the collection {@link String} evaluates
|
||||
* to a {@link LiteralExpression} (indicating that no subsequent evaluation is necessary).
|
||||
*
|
||||
* @param document can be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
private static Expression detectExpression(Document document) {
|
||||
|
||||
if (document == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String collection = document.collection();
|
||||
|
||||
if (!StringUtils.hasText(collection)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Expression expression = PARSER.parseExpression(document.collection(), ParserContext.TEMPLATE_EXPRESSION);
|
||||
|
||||
return expression instanceof LiteralExpression ? null : expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler to collect {@link MongoPersistentProperty} instances and check that each of them is mapped to a distinct
|
||||
* field name.
|
||||
@@ -284,7 +314,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
|
||||
private void potentiallyAssertLanguageType(MongoPersistentProperty persistentProperty) {
|
||||
|
||||
if (persistentProperty.isLanguageProperty()) {
|
||||
if (persistentProperty.isExplicitLanguageProperty()) {
|
||||
assertPropertyType(persistentProperty, String.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,8 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
}
|
||||
|
||||
// We need to support a wider range of ID types than just the ones that can be converted to an ObjectId
|
||||
return SUPPORTED_ID_PROPERTY_NAMES.contains(getName());
|
||||
// but still we need to check if there happens to be an explicit name set
|
||||
return SUPPORTED_ID_PROPERTY_NAMES.contains(getName()) && !hasExplicitFieldName();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -134,10 +135,8 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
}
|
||||
}
|
||||
|
||||
org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
|
||||
|
||||
if (annotation != null && StringUtils.hasText(annotation.value())) {
|
||||
return annotation.value();
|
||||
if (hasExplicitFieldName()) {
|
||||
return getAnnotatedFieldName();
|
||||
}
|
||||
|
||||
String fieldName = fieldNamingStrategy.getFieldName(this);
|
||||
@@ -150,6 +149,26 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if {@link org.springframework.data.mongodb.core.mapping.Field} having non blank
|
||||
* {@link org.springframework.data.mongodb.core.mapping.Field#value()} present.
|
||||
* @since 1.7
|
||||
*/
|
||||
protected boolean hasExplicitFieldName() {
|
||||
return StringUtils.hasText(getAnnotatedFieldName());
|
||||
}
|
||||
|
||||
private String getAnnotatedFieldName() {
|
||||
|
||||
org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
|
||||
|
||||
if (annotation != null && StringUtils.hasText(annotation.value())) {
|
||||
return annotation.value();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#getFieldOrder()
|
||||
@@ -190,9 +209,18 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
*/
|
||||
@Override
|
||||
public boolean isLanguageProperty() {
|
||||
return getFieldName().equals(LANGUAGE_FIELD_NAME) || isAnnotationPresent(Language.class);
|
||||
return getFieldName().equals(LANGUAGE_FIELD_NAME) || isExplicitLanguageProperty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#isExplicitLanguageProperty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isExplicitLanguageProperty() {
|
||||
return isAnnotationPresent(Language.class);
|
||||
};
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#isTextScoreProperty()
|
||||
|
||||
@@ -61,14 +61,22 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
|
||||
boolean isExplicitIdProperty();
|
||||
|
||||
/**
|
||||
* Returns whether the property indicates the documents language either by having a {@link #getFieldName()} equal to
|
||||
* {@literal language} or being annotated with {@link Language}.
|
||||
* Returns true whether the property indicates the documents language either by having a {@link #getFieldName()} equal
|
||||
* to {@literal language} or being annotated with {@link Language}.
|
||||
*
|
||||
* @return
|
||||
* @since 1.6
|
||||
*/
|
||||
boolean isLanguageProperty();
|
||||
|
||||
/**
|
||||
* Returns true when property being annotated with {@link Language}.
|
||||
*
|
||||
* @return
|
||||
* @since 1.6.1
|
||||
*/
|
||||
boolean isExplicitLanguageProperty();
|
||||
|
||||
/**
|
||||
* Returns whether the property holds the documents score calculated by text search. <br/>
|
||||
* It's marked with {@link TextScore}.
|
||||
|
||||
@@ -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.
|
||||
@@ -26,7 +26,8 @@ import com.mongodb.DBObject;
|
||||
* Collects the results of executing a group operation.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @param <T> The class in which the results are mapped onto, accessible via an interator.
|
||||
* @author Christoph Strobl
|
||||
* @param <T> The class in which the results are mapped onto, accessible via an {@link Iterator}.
|
||||
*/
|
||||
public class GroupByResults<T> implements Iterable<T> {
|
||||
|
||||
@@ -38,6 +39,7 @@ public class GroupByResults<T> implements Iterable<T> {
|
||||
private String serverUsed;
|
||||
|
||||
public GroupByResults(List<T> mappedResults, DBObject rawResults) {
|
||||
|
||||
Assert.notNull(mappedResults);
|
||||
Assert.notNull(rawResults);
|
||||
this.mappedResults = mappedResults;
|
||||
@@ -68,21 +70,24 @@ public class GroupByResults<T> implements Iterable<T> {
|
||||
}
|
||||
|
||||
private void parseCount() {
|
||||
|
||||
Object object = rawResults.get("count");
|
||||
if (object instanceof Double) {
|
||||
count = (Double) object;
|
||||
if (object instanceof Number) {
|
||||
count = ((Number) object).doubleValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void parseKeys() {
|
||||
|
||||
Object object = rawResults.get("keys");
|
||||
if (object instanceof Integer) {
|
||||
keys = (Integer) object;
|
||||
if (object instanceof Number) {
|
||||
keys = ((Number) object).intValue();
|
||||
}
|
||||
}
|
||||
|
||||
private void parseServerUsed() {
|
||||
|
||||
// "serverUsed" : "127.0.0.1:27017"
|
||||
Object object = rawResults.get("serverUsed");
|
||||
if (object instanceof String) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import static org.springframework.util.ObjectUtils.*;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
@@ -25,6 +27,7 @@ import com.mongodb.util.JSON;
|
||||
* @author Thomas Risberg
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class BasicQuery extends Query {
|
||||
|
||||
@@ -97,4 +100,42 @@ public class BasicQuery extends Query {
|
||||
protected void setFieldsObject(DBObject fieldsObject) {
|
||||
this.fieldsObject = fieldsObject;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.query.Query#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof BasicQuery)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BasicQuery that = (BasicQuery) o;
|
||||
|
||||
return querySettingsEquals(that) && //
|
||||
nullSafeEquals(fieldsObject, that.fieldsObject) && //
|
||||
nullSafeEquals(queryObject, that.queryObject) && //
|
||||
nullSafeEquals(sortObject, that.sortObject);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.query.Query#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + nullSafeHashCode(queryObject);
|
||||
result = 31 * result + nullSafeHashCode(fieldsObject);
|
||||
result = 31 * result + nullSafeHashCode(sortObject);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@ import org.springframework.data.geo.Shape;
|
||||
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
|
||||
import org.springframework.data.mongodb.core.geo.Sphere;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -515,8 +517,11 @@ public class Criteria implements CriteriaDefinition {
|
||||
* @see org.springframework.data.mongodb.core.query.CriteriaDefinition#getCriteriaObject()
|
||||
*/
|
||||
public DBObject getCriteriaObject() {
|
||||
|
||||
if (this.criteriaChain.size() == 1) {
|
||||
return criteriaChain.get(0).getSingleCriteriaObject();
|
||||
} else if (CollectionUtils.isEmpty(this.criteriaChain) && !CollectionUtils.isEmpty(this.criteria)) {
|
||||
return getSingleCriteriaObject();
|
||||
} else {
|
||||
DBObject criteriaObject = new BasicDBObject();
|
||||
for (Criteria c : this.criteriaChain) {
|
||||
@@ -550,6 +555,13 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
if (!StringUtils.hasText(this.key)) {
|
||||
if (not) {
|
||||
return new BasicDBObject("$not", dbo);
|
||||
}
|
||||
return dbo;
|
||||
}
|
||||
|
||||
DBObject queryCriteria = new BasicDBObject();
|
||||
|
||||
if (!NOT_SET.equals(isValue)) {
|
||||
|
||||
@@ -385,12 +385,21 @@ public class Query {
|
||||
return false;
|
||||
}
|
||||
|
||||
Query that = (Query) obj;
|
||||
return querySettingsEquals((Query) obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the settings of the given {@link Query} are equal to this query.
|
||||
*
|
||||
* @param that
|
||||
* @return
|
||||
*/
|
||||
protected boolean querySettingsEquals(Query that) {
|
||||
|
||||
boolean criteriaEqual = this.criteria.equals(that.criteria);
|
||||
boolean fieldsEqual = this.fieldSpec == null ? that.fieldSpec == null : this.fieldSpec.equals(that.fieldSpec);
|
||||
boolean sortEqual = this.sort == null ? that.sort == null : this.sort.equals(that.sort);
|
||||
boolean hintEqual = this.hint == null ? that.hint == null : this.hint.equals(that.hint);
|
||||
boolean fieldsEqual = nullSafeEquals(this.fieldSpec, that.fieldSpec);
|
||||
boolean sortEqual = nullSafeEquals(this.sort, that.sort);
|
||||
boolean hintEqual = nullSafeEquals(this.hint, that.hint);
|
||||
boolean skipEqual = this.skip == that.skip;
|
||||
boolean limitEqual = this.limit == that.limit;
|
||||
boolean metaEqual = nullSafeEquals(this.meta, that.meta);
|
||||
|
||||
@@ -163,7 +163,8 @@ public class Update {
|
||||
|
||||
/**
|
||||
* Update using {@code $push} modifier. <br/>
|
||||
* Allows creation of {@code $push} command for single or multiple (using {@code $each}) values.
|
||||
* Allows creation of {@code $push} command for single or multiple (using {@code $each}) values as well as using
|
||||
* {@code $position}.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/push/
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/each/
|
||||
@@ -308,6 +309,33 @@ public class Update {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply the value of given key by the given number.
|
||||
*
|
||||
* @see http://docs.mongodb.org/manual/reference/operator/update/mul/
|
||||
* @param key must not be {@literal null}.
|
||||
* @param multiplier must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public Update multiply(String key, Number multiplier) {
|
||||
|
||||
Assert.notNull(multiplier, "Multiplier must not be 'null'.");
|
||||
addMultiFieldOperation("$mul", key, multiplier.doubleValue());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The operator supports bitwise {@code and}, bitwise {@code or}, and bitwise {@code xor} operations.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public BitwiseOperatorBuilder bitwise(String key) {
|
||||
return new BitwiseOperatorBuilder(this, key);
|
||||
}
|
||||
|
||||
public DBObject getUpdateObject() {
|
||||
|
||||
DBObject dbo = new BasicDBObject();
|
||||
@@ -550,6 +578,31 @@ public class Update {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Modifier} implementation used to propagate {@code $position}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
private static class PositionModifier implements Modifier {
|
||||
|
||||
private final int position;
|
||||
|
||||
public PositionModifier(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return "$position";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for creating {@code $push} modifiers
|
||||
*
|
||||
@@ -578,6 +631,42 @@ public class Update {
|
||||
return Update.this.push(key, this.modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces values to be added at the given {@literal position}.
|
||||
*
|
||||
* @param position needs to be greater than or equal to zero.
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public PushOperatorBuilder atPosition(int position) {
|
||||
|
||||
if (position < 0) {
|
||||
throw new IllegalArgumentException("Position must be greater than or equal to zero.");
|
||||
}
|
||||
|
||||
this.modifiers.addModifier(new PositionModifier(position));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces values to be added at given {@literal position}.
|
||||
*
|
||||
* @param position can be {@literal null} which will be appended at the last position.
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public PushOperatorBuilder atPosition(Position position) {
|
||||
|
||||
if (position == null || Position.LAST.equals(position)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.modifiers.addModifier(new PositionModifier(0));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates {@link #value(Object)} to {@code $push}
|
||||
*
|
||||
@@ -667,4 +756,79 @@ public class Update {
|
||||
return Update.this.addToSet(this.key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public static class BitwiseOperatorBuilder {
|
||||
|
||||
private final String key;
|
||||
private final Update reference;
|
||||
private static final String BIT_OPERATOR = "$bit";
|
||||
|
||||
private enum BitwiseOperator {
|
||||
AND, OR, XOR;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString().toLowerCase();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BitwiseOperatorBuilder}.
|
||||
*
|
||||
* @param reference must not be {@literal null}
|
||||
* @param key must not be {@literal null}
|
||||
*/
|
||||
protected BitwiseOperatorBuilder(Update reference, String key) {
|
||||
|
||||
Assert.notNull(reference, "Reference must not be null!");
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
this.reference = reference;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates to the result of a bitwise and operation between the current value and the given one.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Update and(long value) {
|
||||
|
||||
addFieldOperation(BitwiseOperator.AND, value);
|
||||
return reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates to the result of a bitwise or operation between the current value and the given one.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Update or(long value) {
|
||||
|
||||
addFieldOperation(BitwiseOperator.OR, value);
|
||||
return reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates to the result of a bitwise xor operation between the current value and the given one.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public Update xor(long value) {
|
||||
|
||||
addFieldOperation(BitwiseOperator.XOR, value);
|
||||
return reference;
|
||||
}
|
||||
|
||||
private void addFieldOperation(BitwiseOperator operator, Number value) {
|
||||
reference.addMultiFieldOperation(BIT_OPERATOR, key, new BasicDBObject(operator.toString(), value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
public interface MongoRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
|
||||
@@ -48,4 +49,26 @@ public interface MongoRepository<T, ID extends Serializable> extends PagingAndSo
|
||||
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
|
||||
*/
|
||||
List<T> findAll(Sort sort);
|
||||
|
||||
/**
|
||||
* Inserts the given a given entity. Assumes the instance to be new to be able to apply insertion optimizations. Use
|
||||
* the returned instance for further operations as the save operation might have changed the entity instance
|
||||
* completely. Prefer using {@link #save(Object)} instead to avoid the usage of store-specific API.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return the saved entity
|
||||
* @since 1.7
|
||||
*/
|
||||
<S extends T> S insert(S entity);
|
||||
|
||||
/**
|
||||
* Inserts the given entities. Assumes the given entities to have not been persisted yet and thus will optimize the
|
||||
* insert over a call to {@link #save(Iterable)}. Prefer using {@link #save(Iterable)} to avoid the usage of store
|
||||
* specific API.
|
||||
*
|
||||
* @param entities must not be {@literal null}.
|
||||
* @return the saved entities
|
||||
* @since 1.7
|
||||
*/
|
||||
<S extends T> List<S> insert(Iterable<S> entities);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import javax.enterprise.inject.spi.BeanManager;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory;
|
||||
import org.springframework.data.repository.cdi.CdiRepositoryBean;
|
||||
import org.springframework.data.repository.config.CustomRepositoryImplementationDetector;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -44,13 +45,13 @@ public class MongoRepositoryBean<T> extends CdiRepositoryBean<T> {
|
||||
* @param qualifiers must not be {@literal null}.
|
||||
* @param repositoryType must not be {@literal null}.
|
||||
* @param beanManager must not be {@literal null}.
|
||||
* @param customImplementationBean the bean for the custom implementation of the
|
||||
* {@link org.springframework.data.repository.Repository}, can be {@literal null}.
|
||||
* @param detector detector for the custom {@link org.springframework.data.repository.Repository} implementations
|
||||
* {@link CustomRepositoryImplementationDetector}, can be {@literal null}.
|
||||
*/
|
||||
public MongoRepositoryBean(Bean<MongoOperations> operations, Set<Annotation> qualifiers, Class<T> repositoryType,
|
||||
BeanManager beanManager, Bean<?> customImplementationBean) {
|
||||
BeanManager beanManager, CustomRepositoryImplementationDetector detector) {
|
||||
|
||||
super(qualifiers, repositoryType, beanManager, customImplementationBean);
|
||||
super(qualifiers, repositoryType, beanManager, detector);
|
||||
|
||||
Assert.notNull(operations);
|
||||
this.operations = operations;
|
||||
|
||||
@@ -110,10 +110,8 @@ public class MongoRepositoryExtension extends CdiRepositoryExtensionSupport {
|
||||
MongoOperations.class.getName(), qualifiers));
|
||||
}
|
||||
|
||||
Bean<?> customImplementationBean = getCustomImplementationBean(repositoryType, beanManager, qualifiers);
|
||||
|
||||
// Construct and return the repository bean.
|
||||
return new MongoRepositoryBean<T>(mongoOperations, qualifiers, repositoryType, beanManager,
|
||||
customImplementationBean);
|
||||
getCustomImplementationDetector());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.List;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.SliceImpl;
|
||||
@@ -88,39 +87,25 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
|
||||
applyQueryMetaAttributesWhenPresent(query);
|
||||
|
||||
Object result = null;
|
||||
|
||||
if (isDeleteQuery()) {
|
||||
result = new DeleteExecution().execute(query);
|
||||
return new DeleteExecution().execute(query);
|
||||
} else if (method.isGeoNearQuery() && method.isPageQuery()) {
|
||||
|
||||
MongoParameterAccessor countAccessor = new MongoParametersParameterAccessor(method, parameters);
|
||||
Query countQuery = createCountQuery(new ConvertingParameterAccessor(operations.getConverter(), countAccessor));
|
||||
|
||||
result = new GeoNearExecution(accessor).execute(query, countQuery);
|
||||
return new GeoNearExecution(accessor).execute(query, countQuery);
|
||||
} else if (method.isGeoNearQuery()) {
|
||||
result = new GeoNearExecution(accessor).execute(query);
|
||||
return new GeoNearExecution(accessor).execute(query);
|
||||
} else if (method.isSliceQuery()) {
|
||||
result = new SlicedExecution(accessor.getPageable()).execute(query);
|
||||
return new SlicedExecution(accessor.getPageable()).execute(query);
|
||||
} else if (method.isCollectionQuery()) {
|
||||
result = new CollectionExecution(accessor.getPageable()).execute(query);
|
||||
return new CollectionExecution(accessor.getPageable()).execute(query);
|
||||
} else if (method.isPageQuery()) {
|
||||
result = new PagedExecution(accessor.getPageable()).execute(query);
|
||||
return new PagedExecution(accessor.getPageable()).execute(query);
|
||||
} else {
|
||||
result = new SingleEntityExecution(isCountQuery()).execute(query);
|
||||
return new SingleEntityExecution(isCountQuery()).execute(query);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Class<?> expectedReturnType = method.getReturnType().getType();
|
||||
|
||||
if (expectedReturnType.isAssignableFrom(result.getClass())) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return CONVERSION_SERVICE.convert(result, expectedReturnType);
|
||||
}
|
||||
|
||||
private Query applyQueryMetaAttributesWhenPresent(Query query) {
|
||||
@@ -211,6 +196,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
* {@link Execution} for {@link Slice} query methods.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @since 1.5
|
||||
*/
|
||||
|
||||
@@ -232,9 +218,11 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
|
||||
MongoEntityMetadata<?> metadata = method.getEntityInformation();
|
||||
int pageSize = pageable.getPageSize();
|
||||
Pageable slicePageable = new PageRequest(pageable.getPageNumber(), pageSize + 1, pageable.getSort());
|
||||
|
||||
List result = operations.find(query.with(slicePageable), metadata.getJavaType(), metadata.getCollectionName());
|
||||
// Apply Pageable but tweak limit to peek into next page
|
||||
Query modifiedQuery = query.with(pageable).limit(pageSize + 1);
|
||||
|
||||
List result = operations.find(modifiedQuery, metadata.getJavaType(), metadata.getCollectionName());
|
||||
|
||||
boolean hasNext = result.size() > pageSize;
|
||||
|
||||
@@ -317,7 +305,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
|
||||
MongoEntityMetadata<?> metadata = method.getEntityInformation();
|
||||
return countProjection ? operations.count(query, metadata.getJavaType()) : operations.findOne(query,
|
||||
metadata.getJavaType());
|
||||
metadata.getJavaType(), metadata.getCollectionName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +406,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
private Object deleteAndConvertResult(Query query, MongoEntityMetadata<?> metadata) {
|
||||
|
||||
if (method.isCollectionQuery()) {
|
||||
return operations.findAllAndRemove(query, metadata.getJavaType());
|
||||
return operations.findAllAndRemove(query, metadata.getJavaType(), metadata.getCollectionName());
|
||||
}
|
||||
|
||||
WriteResult writeResult = operations.remove(query, metadata.getJavaType(), metadata.getCollectionName());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2013 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.
|
||||
@@ -46,6 +46,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
|
||||
@@ -102,9 +103,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
|
||||
PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
|
||||
MongoPersistentProperty property = path.getLeafProperty();
|
||||
Criteria criteria = from(part, property,
|
||||
where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
|
||||
(PotentiallyConvertingIterator) iterator);
|
||||
Criteria criteria = from(part, property, where(path.toDotPath()), (PotentiallyConvertingIterator) iterator);
|
||||
|
||||
return criteria;
|
||||
}
|
||||
@@ -123,9 +122,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
|
||||
MongoPersistentProperty property = path.getLeafProperty();
|
||||
|
||||
return from(part, property,
|
||||
base.and(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
|
||||
(PotentiallyConvertingIterator) iterator);
|
||||
return from(part, property, base.and(path.toDotPath()), (PotentiallyConvertingIterator) iterator);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -194,7 +191,9 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
case STARTING_WITH:
|
||||
case ENDING_WITH:
|
||||
case CONTAINING:
|
||||
return addAppropriateLikeRegexTo(criteria, part, parameters.next().toString());
|
||||
return createContainingCriteria(part, property, criteria, parameters);
|
||||
case NOT_CONTAINING:
|
||||
return createContainingCriteria(part, property, criteria, parameters).not();
|
||||
case REGEX:
|
||||
return criteria.regex(parameters.next().toString());
|
||||
case EXISTS:
|
||||
@@ -288,6 +287,27 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
Arrays.asList(IgnoreCaseType.ALWAYS, IgnoreCaseType.WHEN_POSSIBLE), part.shouldIgnoreCase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* If the target property of the comparison is of type String, then the operator checks for match using regular
|
||||
* expression. If the target property of the comparison is a {@link Collection} then the operator evaluates to true if
|
||||
* it finds an exact match within any member of the {@link Collection}.
|
||||
*
|
||||
* @param part
|
||||
* @param property
|
||||
* @param criteria
|
||||
* @param parameters
|
||||
* @return
|
||||
*/
|
||||
private Criteria createContainingCriteria(Part part, MongoPersistentProperty property, Criteria criteria,
|
||||
PotentiallyConvertingIterator parameters) {
|
||||
|
||||
if (property.isCollectionLike()) {
|
||||
return criteria.in(nextAsArray(parameters, property));
|
||||
}
|
||||
|
||||
return addAppropriateLikeRegexTo(criteria, part, parameters.next().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an appropriate like-regex and appends it to the given criteria.
|
||||
*
|
||||
@@ -361,6 +381,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
source = source + "$";
|
||||
break;
|
||||
case CONTAINING:
|
||||
case NOT_CONTAINING:
|
||||
source = "*" + source + "*";
|
||||
break;
|
||||
case SIMPLE_PROPERTY:
|
||||
|
||||
@@ -128,8 +128,7 @@ public class MongoQueryMethod extends QueryMethod {
|
||||
MongoPersistentEntity<?> collectionEntity = domainClass.isAssignableFrom(returnedObjectType) ? returnedEntity
|
||||
: managedEntity;
|
||||
|
||||
this.metadata = new SimpleMongoEntityMetadata<Object>((Class<Object>) returnedEntity.getType(),
|
||||
collectionEntity.getCollection());
|
||||
this.metadata = new SimpleMongoEntityMetadata<Object>((Class<Object>) returnedEntity.getType(), collectionEntity);
|
||||
}
|
||||
|
||||
return this.metadata;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.query;
|
||||
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -25,21 +26,22 @@ import org.springframework.util.Assert;
|
||||
class SimpleMongoEntityMetadata<T> implements MongoEntityMetadata<T> {
|
||||
|
||||
private final Class<T> type;
|
||||
private final String collectionName;
|
||||
private final MongoPersistentEntity<?> collectionEntity;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SimpleMongoEntityMetadata} using the given type and collection name.
|
||||
* Creates a new {@link SimpleMongoEntityMetadata} using the given type and {@link MongoPersistentEntity} to use for
|
||||
* collection lookups.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param collectionName must not be {@literal null} or empty.
|
||||
* @param collectionEntity must not be {@literal null} or empty.
|
||||
*/
|
||||
public SimpleMongoEntityMetadata(Class<T> type, String collectionName) {
|
||||
public SimpleMongoEntityMetadata(Class<T> type, MongoPersistentEntity<?> collectionEntity) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
Assert.hasText(collectionName, "Collection name must not be null or empty!");
|
||||
Assert.notNull(collectionEntity, "Collection entity must not be null or empty!");
|
||||
|
||||
this.type = type;
|
||||
this.collectionName = collectionName;
|
||||
this.collectionEntity = collectionEntity;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -55,6 +57,6 @@ class SimpleMongoEntityMetadata<T> implements MongoEntityMetadata<T> {
|
||||
* @see org.springframework.data.mongodb.repository.query.MongoEntityMetadata#getCollectionName()
|
||||
*/
|
||||
public String getCollectionName() {
|
||||
return collectionName;
|
||||
return collectionEntity.getCollection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
/**
|
||||
@@ -199,6 +200,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
* {@link Collections#emptyList()}.
|
||||
*
|
||||
* @param input
|
||||
* @param conversionService must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public List<ParameterBinding> parseParameterBindingsFrom(String input) {
|
||||
@@ -229,14 +231,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
if (value instanceof String) {
|
||||
|
||||
String string = ((String) value).trim();
|
||||
|
||||
Matcher valueMatcher = PARSEABLE_BINDING_PATTERN.matcher(string);
|
||||
while (valueMatcher.find()) {
|
||||
int paramIndex = Integer.parseInt(valueMatcher.group(PARAMETER_INDEX_GROUP));
|
||||
boolean quoted = (string.startsWith("'") && string.endsWith("'"))
|
||||
|| (string.startsWith("\"") && string.endsWith("\""));
|
||||
bindings.add(new ParameterBinding(paramIndex, quoted));
|
||||
}
|
||||
potentiallyAddBinding(string, bindings);
|
||||
|
||||
} else if (value instanceof Pattern) {
|
||||
|
||||
@@ -255,15 +250,37 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
bindings.add(new ParameterBinding(paramIndex, quoted));
|
||||
}
|
||||
|
||||
} else if (value instanceof DBRef) {
|
||||
|
||||
DBRef dbref = (DBRef) value;
|
||||
|
||||
potentiallyAddBinding(dbref.getRef(), bindings);
|
||||
potentiallyAddBinding(dbref.getId().toString(), bindings);
|
||||
|
||||
} else if (value instanceof DBObject) {
|
||||
|
||||
DBObject dbo = (DBObject) value;
|
||||
|
||||
for (String field : dbo.keySet()) {
|
||||
collectParameterReferencesIntoBindings(bindings, field);
|
||||
collectParameterReferencesIntoBindings(bindings, dbo.get(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void potentiallyAddBinding(String source, List<ParameterBinding> bindings) {
|
||||
|
||||
Matcher valueMatcher = PARSEABLE_BINDING_PATTERN.matcher(source);
|
||||
|
||||
while (valueMatcher.find()) {
|
||||
|
||||
int paramIndex = Integer.parseInt(valueMatcher.group(PARAMETER_INDEX_GROUP));
|
||||
boolean quoted = (source.startsWith("'") && source.endsWith("'"))
|
||||
|| (source.startsWith("\"") && source.endsWith("\""));
|
||||
|
||||
bindings.add(new ParameterBinding(paramIndex, quoted));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,11 +17,9 @@ package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
|
||||
import org.springframework.data.repository.core.support.AbstractEntityInformation;
|
||||
import org.springframework.data.repository.core.support.PersistentEntityInformation;
|
||||
|
||||
/**
|
||||
* {@link MongoEntityInformation} implementation using a {@link MongoPersistentEntity} instance to lookup the necessary
|
||||
@@ -30,7 +28,7 @@ import org.springframework.data.repository.core.support.AbstractEntityInformatio
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MappingMongoEntityInformation<T, ID extends Serializable> extends AbstractEntityInformation<T, ID>
|
||||
public class MappingMongoEntityInformation<T, ID extends Serializable> extends PersistentEntityInformation<T, ID>
|
||||
implements MongoEntityInformation<T, ID> {
|
||||
|
||||
private final MongoPersistentEntity<T> entityMetadata;
|
||||
@@ -50,42 +48,16 @@ public class MappingMongoEntityInformation<T, ID extends Serializable> extends A
|
||||
* collection name.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param customCollectionName
|
||||
* @param customCollectionName can be {@literal null}.
|
||||
*/
|
||||
public MappingMongoEntityInformation(MongoPersistentEntity<T> entity, String customCollectionName) {
|
||||
super(entity.getType());
|
||||
|
||||
super(entity);
|
||||
|
||||
this.entityMetadata = entity;
|
||||
this.customCollectionName = customCollectionName;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.EntityInformation#getId(java.lang.Object)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ID getId(T entity) {
|
||||
|
||||
MongoPersistentProperty idProperty = entityMetadata.getIdProperty();
|
||||
|
||||
if (idProperty == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return (ID) BeanWrapper.create(entity, null).getProperty(idProperty);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.EntityInformation#getIdType()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<ID> getIdType() {
|
||||
return (Class<ID>) entityMetadata.getIdProperty().getType();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.MongoEntityInformation#getCollectionName()
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2012 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.
|
||||
@@ -27,8 +27,10 @@ import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
|
||||
import org.springframework.data.querydsl.EntityPathResolver;
|
||||
import org.springframework.data.querydsl.QSort;
|
||||
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
|
||||
import org.springframework.data.querydsl.SimpleEntityPathResolver;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.EntityMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -43,18 +45,21 @@ import com.mysema.query.types.path.PathBuilder;
|
||||
* Special QueryDsl based repository implementation that allows execution {@link Predicate}s in various forms.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleMongoRepository<T, ID> implements
|
||||
QueryDslPredicateExecutor<T> {
|
||||
|
||||
private final PathBuilder<T> builder;
|
||||
private final EntityInformation<T, ID> entityInformation;
|
||||
private final MongoOperations mongoOperations;
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryDslMongoRepository} for the given {@link EntityMetadata} and {@link MongoTemplate}. Uses
|
||||
* the {@link SimpleEntityPathResolver} to create an {@link EntityPath} for the given domain class.
|
||||
*
|
||||
* @param entityInformation
|
||||
* @param template
|
||||
* @param entityInformation must not be {@literal null}.
|
||||
* @param mongoOperations must not be {@literal null}.
|
||||
*/
|
||||
public QueryDslMongoRepository(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations) {
|
||||
this(entityInformation, mongoOperations, SimpleEntityPathResolver.INSTANCE);
|
||||
@@ -64,17 +69,21 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
|
||||
* Creates a new {@link QueryDslMongoRepository} for the given {@link MongoEntityInformation}, {@link MongoTemplate}
|
||||
* and {@link EntityPathResolver}.
|
||||
*
|
||||
* @param entityInformation
|
||||
* @param mongoOperations
|
||||
* @param resolver
|
||||
* @param entityInformation must not be {@literal null}.
|
||||
* @param mongoOperations must not be {@literal null}.
|
||||
* @param resolver must not be {@literal null}.
|
||||
*/
|
||||
public QueryDslMongoRepository(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations,
|
||||
EntityPathResolver resolver) {
|
||||
|
||||
super(entityInformation, mongoOperations);
|
||||
|
||||
Assert.notNull(resolver);
|
||||
EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
|
||||
|
||||
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
|
||||
this.entityInformation = entityInformation;
|
||||
this.mongoOperations = mongoOperations;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -98,10 +107,18 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
|
||||
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.Predicate, com.mysema.query.types.OrderSpecifier<?>[])
|
||||
*/
|
||||
public List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
|
||||
|
||||
return createQueryFor(predicate).orderBy(orders).list();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.OrderSpecifier[])
|
||||
*/
|
||||
@Override
|
||||
public Iterable<T> findAll(OrderSpecifier<?>... orders) {
|
||||
return createQuery().orderBy(orders).list();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.Predicate, org.springframework.data.domain.Pageable)
|
||||
@@ -114,6 +131,28 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
|
||||
return new PageImpl<T>(applyPagination(query, pageable).list(), pageable, countQuery.count());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.support.SimpleMongoRepository#findAll(org.springframework.data.domain.Pageable)
|
||||
*/
|
||||
@Override
|
||||
public Page<T> findAll(Pageable pageable) {
|
||||
|
||||
MongodbQuery<T> countQuery = createQuery();
|
||||
MongodbQuery<T> query = createQuery();
|
||||
|
||||
return new PageImpl<T>(applyPagination(query, pageable).list(), pageable, countQuery.count());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.support.SimpleMongoRepository#findAll(org.springframework.data.domain.Sort)
|
||||
*/
|
||||
@Override
|
||||
public List<T> findAll(Sort sort) {
|
||||
return applySorting(createQuery(), sort).list();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.mysema.query.types.Predicate)
|
||||
@@ -129,11 +168,16 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
|
||||
* @return
|
||||
*/
|
||||
private MongodbQuery<T> createQueryFor(Predicate predicate) {
|
||||
return createQuery().where(predicate);
|
||||
}
|
||||
|
||||
Class<T> domainType = getEntityInformation().getJavaType();
|
||||
|
||||
MongodbQuery<T> query = new SpringDataMongodbQuery<T>(getMongoOperations(), domainType);
|
||||
return query.where(predicate);
|
||||
/**
|
||||
* Creates a {@link MongodbQuery}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private MongodbQuery<T> createQuery() {
|
||||
return new SpringDataMongodbQuery<T>(mongoOperations, entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,6 +210,15 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
|
||||
return query;
|
||||
}
|
||||
|
||||
// TODO: find better solution than instanceof check
|
||||
if (sort instanceof QSort) {
|
||||
|
||||
List<OrderSpecifier<?>> orderSpecifiers = ((QSort) sort).getOrderSpecifiers();
|
||||
query.orderBy(orderSpecifiers.toArray(new OrderSpecifier<?>[orderSpecifiers.size()]));
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
for (Order order : sort) {
|
||||
query.orderBy(toOrder(order));
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -41,6 +42,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class SimpleMongoRepository<T, ID extends Serializable> implements MongoRepository<T, ID> {
|
||||
|
||||
@@ -48,7 +50,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
|
||||
private final MongoEntityInformation<T, ID> entityInformation;
|
||||
|
||||
/**
|
||||
* Creates a ew {@link SimpleMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoTemplate}.
|
||||
* Creates a new {@link SimpleMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoTemplate}.
|
||||
*
|
||||
* @param metadata must not be {@literal null}.
|
||||
* @param template must not be {@literal null}.
|
||||
@@ -70,7 +72,12 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null!");
|
||||
|
||||
mongoOperations.save(entity, entityInformation.getCollectionName());
|
||||
if (entityInformation.isNew(entity)) {
|
||||
mongoOperations.insert(entity, entityInformation.getCollectionName());
|
||||
} else {
|
||||
mongoOperations.save(entity, entityInformation.getCollectionName());
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -82,11 +89,22 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
|
||||
|
||||
Assert.notNull(entities, "The given Iterable of entities not be null!");
|
||||
|
||||
List<S> result = new ArrayList<S>();
|
||||
List<S> result = convertIterableToList(entities);
|
||||
boolean allNew = true;
|
||||
|
||||
for (S entity : entities) {
|
||||
save(entity);
|
||||
result.add(entity);
|
||||
if (allNew && !entityInformation.isNew(entity)) {
|
||||
allNew = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (allNew) {
|
||||
mongoOperations.insertAll(result);
|
||||
} else {
|
||||
|
||||
for (S entity : result) {
|
||||
save(entity);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -181,7 +199,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
|
||||
*/
|
||||
public Iterable<T> findAll(Iterable<ID> ids) {
|
||||
|
||||
Set<ID> parameters = new HashSet<ID>();
|
||||
Set<ID> parameters = new HashSet<ID>(tryDetermineRealSizeOrReturn(ids, 10));
|
||||
for (ID id : ids) {
|
||||
parameters.add(id);
|
||||
}
|
||||
@@ -209,6 +227,38 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
|
||||
return findAll(new Query().with(sort));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.MongoRepository#insert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> S insert(S entity) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null!");
|
||||
|
||||
mongoOperations.insert(entity, entityInformation.getCollectionName());
|
||||
return entity;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.MongoRepository#insert(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> List<S> insert(Iterable<S> entities) {
|
||||
|
||||
Assert.notNull(entities, "The given Iterable of entities not be null!");
|
||||
|
||||
List<S> list = convertIterableToList(entities);
|
||||
|
||||
if (list.isEmpty()) {
|
||||
return list;
|
||||
}
|
||||
|
||||
mongoOperations.insertAll(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<T> findAll(Query query) {
|
||||
|
||||
if (query == null) {
|
||||
@@ -218,20 +268,27 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
|
||||
return mongoOperations.find(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying {@link MongoOperations} instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected MongoOperations getMongoOperations() {
|
||||
return this.mongoOperations;
|
||||
private static <T> List<T> convertIterableToList(Iterable<T> entities) {
|
||||
|
||||
if (entities instanceof List) {
|
||||
return (List<T>) entities;
|
||||
}
|
||||
|
||||
int capacity = tryDetermineRealSizeOrReturn(entities, 10);
|
||||
|
||||
if (capacity == 0 || entities == null) {
|
||||
return Collections.<T> emptyList();
|
||||
}
|
||||
|
||||
List<T> list = new ArrayList<T>(capacity);
|
||||
for (T entity : entities) {
|
||||
list.add(entity);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the entityInformation
|
||||
*/
|
||||
protected MongoEntityInformation<T, ID> getEntityInformation() {
|
||||
return entityInformation;
|
||||
private static int tryDetermineRealSizeOrReturn(Iterable<?> iterable, int defaultSize) {
|
||||
return iterable == null ? 0 : (iterable instanceof Collection) ? ((Collection<?>) iterable).size() : defaultSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
@@ -41,7 +44,17 @@ import com.mysema.query.types.PathType;
|
||||
*/
|
||||
class SpringDataMongodbSerializer extends MongodbSerializer {
|
||||
|
||||
private final String ID_KEY = "_id";
|
||||
private static final String ID_KEY = "_id";
|
||||
private static final Set<PathType> PATH_TYPES;
|
||||
|
||||
static {
|
||||
|
||||
Set<PathType> pathTypes = new HashSet<PathType>();
|
||||
pathTypes.add(PathType.VARIABLE);
|
||||
pathTypes.add(PathType.PROPERTY);
|
||||
|
||||
PATH_TYPES = Collections.unmodifiableSet(pathTypes);
|
||||
}
|
||||
|
||||
private final MongoConverter converter;
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
@@ -138,7 +151,7 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
|
||||
|
||||
Path<?> parent = path.getMetadata().getParent();
|
||||
|
||||
if (parent == null) {
|
||||
if (parent == null || !PATH_TYPES.contains(path.getMetadata().getPathType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2012-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.
|
||||
@@ -23,6 +23,7 @@ import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -49,11 +50,17 @@ public class ServerAddressPropertyEditorUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-454
|
||||
* @see DATAMONGO-1062
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsAddressConfigWithoutASingleParsableServerAddress() {
|
||||
public void rejectsAddressConfigWithoutASingleParsableAndResolvableServerAddress() {
|
||||
|
||||
editor.setAsText("foo, bar");
|
||||
String unknownHost1 = "gugu.nonexistant.example.org";
|
||||
String unknownHost2 = "gaga.nonexistant.example.org";
|
||||
|
||||
assertUnresolveableHostnames(unknownHost1, unknownHost2);
|
||||
|
||||
editor.setAsText(unknownHost1 + "," + unknownHost2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,4 +200,16 @@ public class ServerAddressPropertyEditorUnitTests {
|
||||
assertThat(addresses, hasItem(new ServerAddress(InetAddress.getByName(hostAddress), port)));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertUnresolveableHostnames(String... hostnames) {
|
||||
|
||||
for (String hostname : hostnames) {
|
||||
try {
|
||||
InetAddress.getByName(hostname);
|
||||
Assert.fail("Supposedly unresolveable hostname '" + hostname + "' can be resolved.");
|
||||
} catch (UnknownHostException expected) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,8 @@ public class MongoTemplateTests {
|
||||
|
||||
private static final org.springframework.data.util.Version TWO_DOT_FOUR = org.springframework.data.util.Version
|
||||
.parse("2.4");
|
||||
private static final org.springframework.data.util.Version TWO_DOT_EIGHT = org.springframework.data.util.Version
|
||||
.parse("2.8");
|
||||
|
||||
@Autowired MongoTemplate template;
|
||||
@Autowired MongoDbFactory factory;
|
||||
@@ -115,7 +117,6 @@ public class MongoTemplateTests {
|
||||
@Rule public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Autowired
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setMongo(Mongo mongo) throws Exception {
|
||||
|
||||
CustomConversions conversions = new CustomConversions(Arrays.asList(DateToDateTimeConverter.INSTANCE,
|
||||
@@ -235,7 +236,7 @@ public class MongoTemplateTests {
|
||||
template.insert(person);
|
||||
fail("Expected DataIntegrityViolationException!");
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$_id_ dup key:"));
|
||||
assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$_id_"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,8 +290,7 @@ public class MongoTemplateTests {
|
||||
template.save(person);
|
||||
fail("Expected DataIntegrityViolationException!");
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
assertThat(e.getMessage(),
|
||||
containsString("E11000 duplicate key error index: database.person.$firstName_-1 dup key:"));
|
||||
assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$firstName_-1"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +318,7 @@ public class MongoTemplateTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void testEnsureIndex() throws Exception {
|
||||
|
||||
Person p1 = new Person("Oliver");
|
||||
@@ -339,19 +340,29 @@ public class MongoTemplateTests {
|
||||
if ("age_-1".equals(ix.get("name"))) {
|
||||
indexKey = ix.get("key").toString();
|
||||
unique = (Boolean) ix.get("unique");
|
||||
dropDupes = (Boolean) ix.get("dropDups");
|
||||
if (mongoVersion.isLessThan(TWO_DOT_EIGHT)) {
|
||||
dropDupes = (Boolean) ix.get("dropDups");
|
||||
assertThat(dropDupes, is(true));
|
||||
} else {
|
||||
assertThat(ix.get("dropDups"), is(nullValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
assertThat(indexKey, is("{ \"age\" : -1}"));
|
||||
assertThat(unique, is(true));
|
||||
assertThat(dropDupes, is(true));
|
||||
|
||||
List<IndexInfo> indexInfoList = template.indexOps(Person.class).getIndexInfo();
|
||||
|
||||
assertThat(indexInfoList.size(), is(2));
|
||||
IndexInfo ii = indexInfoList.get(1);
|
||||
assertThat(ii.isUnique(), is(true));
|
||||
assertThat(ii.isDropDuplicates(), is(true));
|
||||
|
||||
if (mongoVersion.isLessThan(TWO_DOT_EIGHT)) {
|
||||
assertThat(ii.isDropDuplicates(), is(true));
|
||||
} else {
|
||||
assertThat(ii.isDropDuplicates(), is(false));
|
||||
}
|
||||
|
||||
assertThat(ii.isSparse(), is(false));
|
||||
|
||||
List<IndexField> indexFields = ii.getIndexFields();
|
||||
@@ -677,8 +688,8 @@ public class MongoTemplateTests {
|
||||
Query q = new Query(Criteria.where("text").regex("^Hello.*"));
|
||||
Message found1 = template.findAndRemove(q, Message.class);
|
||||
Message found2 = template.findAndRemove(q, Message.class);
|
||||
// Message notFound = template.findAndRemove(q, Message.class);
|
||||
DBObject notFound = template.getCollection("").findAndRemove(q.getQueryObject());
|
||||
|
||||
Message notFound = template.findAndRemove(q, Message.class);
|
||||
assertThat(found1, notNullValue());
|
||||
assertThat(found2, notNullValue());
|
||||
assertThat(notFound, nullValue());
|
||||
@@ -2791,6 +2802,7 @@ public class MongoTemplateTests {
|
||||
|
||||
@Id public String id;
|
||||
|
||||
@Field("db_ref_list")/** @see DATAMONGO-1058 */
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
public List<Sample> dbRefAnnotatedList;
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ import org.springframework.data.annotation.AccessType;
|
||||
import org.springframework.data.annotation.AccessType.Type;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.MongoExceptionTranslator;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.Person;
|
||||
@@ -505,6 +505,7 @@ public class DbRefMappingMongoConverterUnitTests {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(ClassWithLazyDbRefs.class);
|
||||
MongoPersistentProperty property = entity.getPersistentProperty("dbRefToConcreteType");
|
||||
MongoPersistentEntity<?> propertyEntity = mappingContext.getPersistentEntity(property);
|
||||
|
||||
String idValue = new ObjectId().toString();
|
||||
DBRef dbRef = converter.toDBRef(new LazyDbRefTarget(idValue), property);
|
||||
@@ -513,10 +514,10 @@ public class DbRefMappingMongoConverterUnitTests {
|
||||
|
||||
ClassWithLazyDbRefs result = converter.read(ClassWithLazyDbRefs.class, object);
|
||||
|
||||
BeanWrapper<LazyDbRefTarget> wrapper = BeanWrapper.create(result.dbRefToConcreteType, null);
|
||||
PersistentPropertyAccessor accessor = propertyEntity.getPropertyAccessor(result.dbRefToConcreteType);
|
||||
MongoPersistentProperty idProperty = mappingContext.getPersistentEntity(LazyDbRefTarget.class).getIdProperty();
|
||||
|
||||
assertThat(wrapper.getProperty(idProperty), is(notNullValue()));
|
||||
assertThat(accessor.getProperty(idProperty), is(notNullValue()));
|
||||
assertProxyIsResolved(result.dbRefToConcreteType, false);
|
||||
}
|
||||
|
||||
@@ -541,6 +542,26 @@ public class DbRefMappingMongoConverterUnitTests {
|
||||
assertProxyIsResolved(proxy, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1076
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotTriggerResolvingOfLazyLoadedProxyWhenFinalizeMethodIsInvoked() throws Exception {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(WithObjectMethodOverrideLazyDbRefs.class);
|
||||
MongoPersistentProperty property = entity.getPersistentProperty("dbRefToConcreteTypeWithPropertyAccess");
|
||||
|
||||
String idValue = new ObjectId().toString();
|
||||
DBRef dbRef = converter.toDBRef(new LazyDbRefTargetPropertyAccess(idValue), property);
|
||||
|
||||
WithObjectMethodOverrideLazyDbRefs result = converter.read(WithObjectMethodOverrideLazyDbRefs.class,
|
||||
new BasicDBObject("dbRefToPlainObject", dbRef));
|
||||
|
||||
ReflectionTestUtils.invokeMethod(result.dbRefToPlainObject, "finalize");
|
||||
|
||||
assertProxyIsResolved(result.dbRefToPlainObject, false);
|
||||
}
|
||||
|
||||
private Object transport(Object result) {
|
||||
return SerializationUtils.deserialize(SerializationUtils.serialize(result));
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -45,9 +46,12 @@ import org.hamcrest.Matchers;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -80,6 +84,7 @@ import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
@@ -100,6 +105,8 @@ public class MappingMongoConverterUnitTests {
|
||||
@Mock ApplicationContext context;
|
||||
@Mock DbRefResolver resolver;
|
||||
|
||||
public @Rule ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
@@ -1826,6 +1833,135 @@ public class MappingMongoConverterUnitTests {
|
||||
verify(mock, times(1)).getTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1034
|
||||
*/
|
||||
@Test
|
||||
public void rejectsBasicDbListToBeConvertedIntoComplexType() {
|
||||
|
||||
BasicDBList inner = new BasicDBList();
|
||||
inner.add("key");
|
||||
inner.add("value");
|
||||
|
||||
BasicDBList outer = new BasicDBList();
|
||||
outer.add(inner);
|
||||
outer.add(inner);
|
||||
|
||||
BasicDBObject source = new BasicDBObject("attributes", outer);
|
||||
|
||||
exception.expect(MappingException.class);
|
||||
exception.expectMessage(Item.class.getName());
|
||||
exception.expectMessage(BasicDBList.class.getName());
|
||||
|
||||
converter.read(Item.class, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1058
|
||||
*/
|
||||
@Test
|
||||
public void readShouldRespectExplicitFieldNameForDbRef() {
|
||||
|
||||
BasicDBObject source = new BasicDBObject();
|
||||
source.append("explict-name-for-db-ref", new DBRef(mock(DB.class), "foo", "1"));
|
||||
|
||||
converter.read(ClassWithExplicitlyNamedDBRefProperty.class, source);
|
||||
|
||||
verify(resolver, times(1)).resolveDbRef(Mockito.any(MongoPersistentProperty.class), Mockito.any(DBRef.class),
|
||||
Mockito.any(DbRefResolverCallback.class), Mockito.any(DbRefProxyHandler.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1050
|
||||
*/
|
||||
@Test
|
||||
public void writeShouldUseExplicitFieldnameForIdPropertyWhenAnnotated() {
|
||||
|
||||
RootForClassWithExplicitlyRenamedIdField source = new RootForClassWithExplicitlyRenamedIdField();
|
||||
source.id = "rootId";
|
||||
source.nested = new ClassWithExplicitlyRenamedField();
|
||||
source.nested.id = "nestedId";
|
||||
|
||||
DBObject sink = new BasicDBObject();
|
||||
converter.write(source, sink);
|
||||
|
||||
assertThat((String) sink.get("_id"), is("rootId"));
|
||||
assertThat((DBObject) sink.get("nested"), is(new BasicDBObjectBuilder().add("id", "nestedId").get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1050
|
||||
*/
|
||||
@Test
|
||||
public void readShouldUseExplicitFieldnameForIdPropertyWhenAnnotated() {
|
||||
|
||||
DBObject source = new BasicDBObjectBuilder().add("_id", "rootId")
|
||||
.add("nested", new BasicDBObject("id", "nestedId")).get();
|
||||
|
||||
RootForClassWithExplicitlyRenamedIdField sink = converter.read(RootForClassWithExplicitlyRenamedIdField.class,
|
||||
source);
|
||||
|
||||
assertThat(sink.id, is("rootId"));
|
||||
assertThat(sink.nested, notNullValue());
|
||||
assertThat(sink.nested.id, is("nestedId"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1050
|
||||
*/
|
||||
@Test
|
||||
public void namedIdFieldShouldExtractValueFromUnderscoreIdField() {
|
||||
|
||||
DBObject dbo = new BasicDBObjectBuilder().add("_id", "A").add("id", "B").get();
|
||||
|
||||
ClassWithNamedIdField withNamedIdField = converter.read(ClassWithNamedIdField.class, dbo);
|
||||
|
||||
assertThat(withNamedIdField.id, is("A"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1050
|
||||
*/
|
||||
@Test
|
||||
public void explicitlyRenamedIfFieldShouldExtractValueFromIdField() {
|
||||
|
||||
DBObject dbo = new BasicDBObjectBuilder().add("_id", "A").add("id", "B").get();
|
||||
|
||||
ClassWithExplicitlyRenamedField withExplicitlyRenamedField = converter.read(ClassWithExplicitlyRenamedField.class,
|
||||
dbo);
|
||||
|
||||
assertThat(withExplicitlyRenamedField.id, is("B"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1050
|
||||
*/
|
||||
@Test
|
||||
public void annotatedIdFieldShouldExtractValueFromUnderscoreIdField() {
|
||||
|
||||
DBObject dbo = new BasicDBObjectBuilder().add("_id", "A").add("id", "B").get();
|
||||
|
||||
ClassWithAnnotatedIdField withAnnotatedIdField = converter.read(ClassWithAnnotatedIdField.class, dbo);
|
||||
|
||||
assertThat(withAnnotatedIdField.key, is("A"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1102
|
||||
*/
|
||||
@Test
|
||||
public void convertsJava8DateTimeTypesToDateAndBack() {
|
||||
|
||||
TypeWithLocalDateTime source = new TypeWithLocalDateTime();
|
||||
LocalDateTime reference = source.date;
|
||||
BasicDBObject result = new BasicDBObject();
|
||||
|
||||
converter.write(source, result);
|
||||
|
||||
assertThat(result.get("date"), is(instanceOf(Date.class)));
|
||||
assertThat(converter.read(TypeWithLocalDateTime.class, result).date, is(reference));
|
||||
}
|
||||
|
||||
static class GenericType<T> {
|
||||
T content;
|
||||
}
|
||||
@@ -2075,4 +2211,51 @@ public class MappingMongoConverterUnitTests {
|
||||
|
||||
@TextScore Float score;
|
||||
}
|
||||
|
||||
class ClassWithExplicitlyNamedDBRefProperty {
|
||||
|
||||
@Field("explict-name-for-db-ref")//
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef//
|
||||
ClassWithIntId dbRefProperty;
|
||||
|
||||
public ClassWithIntId getDbRefProperty() {
|
||||
return dbRefProperty;
|
||||
}
|
||||
}
|
||||
|
||||
static class RootForClassWithExplicitlyRenamedIdField {
|
||||
|
||||
@Id String id;
|
||||
ClassWithExplicitlyRenamedField nested;
|
||||
}
|
||||
|
||||
static class ClassWithExplicitlyRenamedField {
|
||||
|
||||
@Field("id") String id;
|
||||
}
|
||||
|
||||
static class RootForClassWithNamedIdField {
|
||||
|
||||
String id;
|
||||
ClassWithNamedIdField nested;
|
||||
}
|
||||
|
||||
static class ClassWithNamedIdField {
|
||||
|
||||
String id;
|
||||
}
|
||||
|
||||
static class ClassWithAnnotatedIdField {
|
||||
|
||||
@Id String key;
|
||||
}
|
||||
|
||||
static class TypeWithLocalDateTime {
|
||||
|
||||
LocalDateTime date;
|
||||
|
||||
TypeWithLocalDateTime() {
|
||||
this.date = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,6 +658,50 @@ public class QueryMapperUnitTests {
|
||||
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("_id", 1).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1070
|
||||
*/
|
||||
@Test
|
||||
public void mapsIdReferenceToDBRefCorrectly() {
|
||||
|
||||
ObjectId id = new ObjectId();
|
||||
|
||||
DBObject query = new BasicDBObject("reference.id", new com.mongodb.DBRef(null, "reference", id.toString()));
|
||||
DBObject result = mapper.getMappedObject(query, context.getPersistentEntity(WithDBRef.class));
|
||||
|
||||
assertThat(result.containsField("reference"), is(true));
|
||||
com.mongodb.DBRef reference = getTypedValue(result, "reference", com.mongodb.DBRef.class);
|
||||
assertThat(reference.getId(), is(instanceOf(ObjectId.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1050
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseExplicitlySetFieldnameForIdPropertyCandidates() {
|
||||
|
||||
Query query = query(where("nested.id").is("bar"));
|
||||
|
||||
DBObject dbo = mapper.getMappedObject(query.getQueryObject(),
|
||||
context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class));
|
||||
|
||||
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("nested.id", "bar").get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1050
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseExplicitlySetFieldnameForIdPropertyCandidatesUsedInSortClause() {
|
||||
|
||||
Query query = new Query().with(new Sort("nested.id"));
|
||||
|
||||
DBObject dbo = mapper.getMappedSort(query.getSortObject(),
|
||||
context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class));
|
||||
|
||||
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("nested.id", 1).get()));
|
||||
}
|
||||
|
||||
@Document
|
||||
public class Foo {
|
||||
@Id private ObjectId id;
|
||||
@@ -739,4 +783,15 @@ public class QueryMapperUnitTests {
|
||||
@Id String id;
|
||||
@TextScore @Field("score") Float textScore;
|
||||
}
|
||||
|
||||
static class RootForClassWithExplicitlyRenamedIdField {
|
||||
|
||||
@Id String id;
|
||||
ClassWithExplicitlyRenamedField nested;
|
||||
}
|
||||
|
||||
static class ClassWithExplicitlyRenamedField {
|
||||
|
||||
@Field("id") String id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ 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.data.mongodb.core.query.Update.Position;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -293,6 +294,76 @@ public class UpdateMapperUnitTests {
|
||||
assertThat(getAsDBObject(push, "type").containsField("$each"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-943
|
||||
*/
|
||||
@Test
|
||||
public void updatePushEachAtPositionWorksCorrectlyWhenGivenPositiveIndexParameter() {
|
||||
|
||||
Update update = new Update().push("key").atPosition(2).each(Arrays.asList("Arya", "Arry", "Weasel"));
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
|
||||
|
||||
DBObject push = getAsDBObject(mappedObject, "$push");
|
||||
DBObject key = getAsDBObject(push, "key");
|
||||
|
||||
assertThat(key.containsField("$position"), is(true));
|
||||
assertThat((Integer) key.get("$position"), is(2));
|
||||
assertThat(getAsDBObject(push, "key").containsField("$each"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-943
|
||||
*/
|
||||
@Test
|
||||
public void updatePushEachAtPositionWorksCorrectlyWhenGivenPositionFirst() {
|
||||
|
||||
Update update = new Update().push("key").atPosition(Position.FIRST).each(Arrays.asList("Arya", "Arry", "Weasel"));
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
|
||||
|
||||
DBObject push = getAsDBObject(mappedObject, "$push");
|
||||
DBObject key = getAsDBObject(push, "key");
|
||||
|
||||
assertThat(key.containsField("$position"), is(true));
|
||||
assertThat((Integer) key.get("$position"), is(0));
|
||||
assertThat(getAsDBObject(push, "key").containsField("$each"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-943
|
||||
*/
|
||||
@Test
|
||||
public void updatePushEachAtPositionWorksCorrectlyWhenGivenPositionLast() {
|
||||
|
||||
Update update = new Update().push("key").atPosition(Position.LAST).each(Arrays.asList("Arya", "Arry", "Weasel"));
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
|
||||
|
||||
DBObject push = getAsDBObject(mappedObject, "$push");
|
||||
DBObject key = getAsDBObject(push, "key");
|
||||
|
||||
assertThat(key.containsField("$position"), is(false));
|
||||
assertThat(getAsDBObject(push, "key").containsField("$each"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-943
|
||||
*/
|
||||
@Test
|
||||
public void updatePushEachAtPositionWorksCorrectlyWhenGivenPositionNull() {
|
||||
|
||||
Update update = new Update().push("key").atPosition(null).each(Arrays.asList("Arya", "Arry", "Weasel"));
|
||||
|
||||
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
|
||||
|
||||
DBObject push = getAsDBObject(mappedObject, "$push");
|
||||
DBObject key = getAsDBObject(push, "key");
|
||||
|
||||
assertThat(key.containsField("$position"), is(false));
|
||||
assertThat(getAsDBObject(push, "key").containsField("$each"), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-410
|
||||
*/
|
||||
@@ -508,6 +579,23 @@ public class UpdateMapperUnitTests {
|
||||
assertThat(list, equalTo(new BasicDBObjectBuilder().add("_id", "1").get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1077
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotRemovePositionalParameter() {
|
||||
|
||||
Update update = new Update();
|
||||
update.unset("dbRefAnnotatedList.$");
|
||||
|
||||
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
|
||||
context.getPersistentEntity(DocumentWithDBRefCollection.class));
|
||||
|
||||
DBObject $unset = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$unset");
|
||||
|
||||
assertThat($unset, equalTo(new BasicDBObjectBuilder().add("dbRefAnnotatedList.$", 1).get()));
|
||||
}
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.Document(collection = "DocumentWithReferenceToInterface")
|
||||
static interface DocumentWithReferenceToInterface {
|
||||
|
||||
|
||||
@@ -163,8 +163,8 @@ public class MongoPersistentEntityIndexCreatorUnitTests {
|
||||
new MongoPersistentEntityIndexCreator(mappingContext, factory);
|
||||
|
||||
assertThat(keysCaptor.getValue(), equalTo(new BasicDBObjectBuilder().add("company.address.location", "2d").get()));
|
||||
assertThat(optionsCaptor.getValue(), equalTo(new BasicDBObjectBuilder().add("name", "location").add("min", -180)
|
||||
.add("max", 180).add("bits", 26).get()));
|
||||
assertThat(optionsCaptor.getValue(), equalTo(new BasicDBObjectBuilder().add("name", "company.address.location")
|
||||
.add("min", -180).add("max", 180).add("bits", 26).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,25 +15,18 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.index;
|
||||
|
||||
import static org.hamcrest.collection.IsCollectionWithSize.*;
|
||||
import static org.hamcrest.collection.IsEmptyCollection.*;
|
||||
import static org.hamcrest.core.IsEqual.*;
|
||||
import static org.hamcrest.core.IsInstanceOf.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.collection.IsEmptyCollection;
|
||||
import org.hamcrest.core.IsEqual;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mongodb.core.DBObjectTestUtils;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.IndexDefinitionHolder;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.CompoundIndexResolutionTests;
|
||||
@@ -41,14 +34,15 @@ import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexRes
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.IndexResolutionTests;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.MixedIndexResolutionTests;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.TextIndexedResolutionTests;
|
||||
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.Language;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntityTestDummy.MongoPersistentEntityDummyBuilder;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
@@ -374,22 +368,6 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
assertIndexPathAndCollection(new String[] { "foo", "bar" }, "CompoundIndexOnLevelZero", indexDefinitions.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-963
|
||||
*/
|
||||
@Test
|
||||
public void compoundIndexShouldIncludeTTLWhenConsistingOfOnlyOneKey() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(CompoundIndexWithOnlyOneKeyAndTTL.class);
|
||||
|
||||
IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition();
|
||||
assertThat(
|
||||
indexDefinition.getIndexOptions(),
|
||||
equalTo(new BasicDBObjectBuilder().add("unique", true).add("dropDups", true).add("sparse", true)
|
||||
.add("background", true).add("expireAfterSeconds", 10L).get()));
|
||||
assertThat(indexDefinition.getIndexKeys(), equalTo(new BasicDBObjectBuilder().add("foo", 1).get()));
|
||||
}
|
||||
|
||||
@Document(collection = "CompoundIndexOnLevelOne")
|
||||
static class CompoundIndexOnLevelOne {
|
||||
|
||||
@@ -404,16 +382,16 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
|
||||
@Document(collection = "CompoundIndexOnLevelZero")
|
||||
@CompoundIndexes({ @CompoundIndex(name = "compound_index", def = "{'foo': 1, 'bar': -1}", background = true,
|
||||
dropDups = true, expireAfterSeconds = 10, sparse = true, unique = true) })
|
||||
dropDups = true, sparse = true, unique = true) })
|
||||
static class CompoundIndexOnLevelZero {}
|
||||
|
||||
@CompoundIndexes({ @CompoundIndex(name = "compound_index", background = true, dropDups = true,
|
||||
expireAfterSeconds = 10, sparse = true, unique = true) })
|
||||
@CompoundIndexes({ @CompoundIndex(name = "compound_index", background = true, dropDups = true, sparse = true,
|
||||
unique = true) })
|
||||
static class CompoundIndexOnLevelZeroWithEmptyIndexDef {}
|
||||
|
||||
@Document(collection = "CompoundIndexOnLevelZero")
|
||||
@CompoundIndex(name = "compound_index", def = "{'foo': 1, 'bar': -1}", background = true, dropDups = true,
|
||||
expireAfterSeconds = 10, sparse = true, unique = true)
|
||||
sparse = true, unique = true)
|
||||
static class SingleCompoundIndex {}
|
||||
|
||||
static class IndexDefinedOnSuperClass extends CompoundIndexOnLevelZero {
|
||||
@@ -422,17 +400,11 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
|
||||
@Document(collection = "ComountIndexWithAutogeneratedName")
|
||||
@CompoundIndexes({ @CompoundIndex(useGeneratedName = true, def = "{'foo': 1, 'bar': -1}", background = true,
|
||||
dropDups = true, expireAfterSeconds = 10, sparse = true, unique = true) })
|
||||
dropDups = true, sparse = true, unique = true) })
|
||||
static class ComountIndexWithAutogeneratedName {
|
||||
|
||||
}
|
||||
|
||||
@Document(collection = "CompoundIndexWithOnlyOneKeyAndTTL")
|
||||
@CompoundIndex(def = "{'foo': 1}", background = true, dropDups = true, expireAfterSeconds = 10, sparse = true,
|
||||
unique = true)
|
||||
static class CompoundIndexWithOnlyOneKeyAndTTL {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class TextIndexedResolutionTests {
|
||||
@@ -483,7 +455,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
indexDefinitions.get(0));
|
||||
|
||||
DBObject weights = DBObjectTestUtils.getAsDBObject(indexDefinitions.get(0).getIndexOptions(), "weights");
|
||||
assertThat(weights.get("nested.foo"), IsEqual.<Object> equalTo(5F));
|
||||
assertThat(weights.get("nested.foo"), is((Object) 5F));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -498,8 +470,8 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
"textIndexOnNestedWithMostSpecificValueRoot", indexDefinitions.get(0));
|
||||
|
||||
DBObject weights = DBObjectTestUtils.getAsDBObject(indexDefinitions.get(0).getIndexOptions(), "weights");
|
||||
assertThat(weights.get("nested.foo"), IsEqual.<Object> equalTo(5F));
|
||||
assertThat(weights.get("nested.bar"), IsEqual.<Object> equalTo(10F));
|
||||
assertThat(weights.get("nested.foo"), is((Object) 5F));
|
||||
assertThat(weights.get("nested.bar"), is((Object) 10F));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -509,17 +481,57 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
public void shouldSetDefaultLanguageCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithDefaultLanguage.class);
|
||||
assertThat(indexDefinitions.get(0).getIndexOptions().get("default_language"), IsEqual.<Object> equalTo("spanish"));
|
||||
assertThat(indexDefinitions.get(0).getIndexOptions().get("default_language"), is((Object) "spanish"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-937
|
||||
* @see DATAMONGO-937, DATAMONGO-1049
|
||||
*/
|
||||
@Test
|
||||
public void shouldResolveTextIndexLanguageOverrideCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithLanguageOverrideOnNestedElementRoot.class);
|
||||
assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), IsEqual.<Object> equalTo("lang"));
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithLanguageOverride.class);
|
||||
assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), is((Object) "lang"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1049
|
||||
*/
|
||||
@Test
|
||||
public void shouldIgnoreTextIndexLanguageOverrideOnNestedElements() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithLanguageOverrideOnNestedElement.class);
|
||||
assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1049
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotCreateIndexDefinitionWhenOnlyLanguageButNoTextIndexPresent() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNoTextIndexPropertyButReservedFieldLanguage.class);
|
||||
assertThat(indexDefinitions, is(empty()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1049
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotCreateIndexDefinitionWhenOnlyAnnotatedLanguageButNoTextIndexPresent() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNoTextIndexPropertyButReservedFieldLanguageAnnotated.class);
|
||||
assertThat(indexDefinitions, is(empty()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1049
|
||||
*/
|
||||
@Test
|
||||
public void shouldPreferExplicitlyAnnotatedLanguageProperty() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithOverlappingLanguageProps.class);
|
||||
assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), is((Object) "lang"));
|
||||
}
|
||||
|
||||
@Document
|
||||
@@ -549,14 +561,12 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
static class TextIndexOnNested {
|
||||
|
||||
String foo;
|
||||
|
||||
}
|
||||
|
||||
@Document
|
||||
static class TextIndexOnNestedWithWeightRoot {
|
||||
|
||||
@TextIndexed(weight = 5) TextIndexOnNested nested;
|
||||
|
||||
}
|
||||
|
||||
@Document
|
||||
@@ -576,18 +586,39 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithLanguageOverrideOnNestedElementRoot {
|
||||
static class DocumentWithLanguageOverrideOnNestedElement {
|
||||
|
||||
DocumentWithLanguageOverrideOnNestedElement nested;
|
||||
DocumentWithLanguageOverride nested;
|
||||
}
|
||||
|
||||
static class DocumentWithLanguageOverrideOnNestedElement {
|
||||
@Document
|
||||
static class DocumentWithLanguageOverride {
|
||||
|
||||
@TextIndexed String foo;
|
||||
|
||||
@Language String lang;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNoTextIndexPropertyButReservedFieldLanguage {
|
||||
|
||||
String language;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNoTextIndexPropertyButReservedFieldLanguageAnnotated {
|
||||
|
||||
@Field("language") String lang;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithOverlappingLanguageProps {
|
||||
|
||||
@TextIndexed String foo;
|
||||
String language;
|
||||
@Language String lang;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class MixedIndexResolutionTests {
|
||||
@@ -692,7 +723,7 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
public void shouldDetectSelfCycleViaCollectionTypeCorrectly() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(SelfCyclingViaCollectionType.class);
|
||||
assertThat(indexDefinitions, IsEmptyCollection.empty());
|
||||
assertThat(indexDefinitions, empty());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -702,46 +733,109 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
public void shouldNotDetectCycleWhenTypeIsUsedMoreThanOnce() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(MultipleObjectsOfSameType.class);
|
||||
assertThat(indexDefinitions, IsEmptyCollection.empty());
|
||||
assertThat(indexDefinitions, empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-962
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void shouldCatchCyclicReferenceExceptionOnRoot() {
|
||||
|
||||
Document documentDummy = new Document() {
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return Document.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collection() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String language() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
MongoPersistentEntity entity = new BasicMongoPersistentEntity<Object>(ClassTypeInformation.from(Object.class));
|
||||
|
||||
MongoPersistentProperty propertyMock = mock(MongoPersistentProperty.class);
|
||||
when(propertyMock.isEntity()).thenReturn(true);
|
||||
when(propertyMock.getOwner()).thenReturn(
|
||||
(PersistentEntity) MongoPersistentEntityDummyBuilder.forClass(Object.class).build());
|
||||
when(propertyMock.getOwner()).thenReturn(entity);
|
||||
when(propertyMock.getActualType()).thenThrow(
|
||||
new MongoPersistentEntityIndexResolver.CyclicPropertyReferenceException("foo", Object.class, "bar"));
|
||||
|
||||
MongoPersistentEntity<SelfCyclingViaCollectionType> dummy = MongoPersistentEntityDummyBuilder
|
||||
.forClass(SelfCyclingViaCollectionType.class).withCollection("foo").and(propertyMock).and(documentDummy)
|
||||
.build();
|
||||
MongoPersistentEntity<SelfCyclingViaCollectionType> selfCyclingEntity = new BasicMongoPersistentEntity<SelfCyclingViaCollectionType>(
|
||||
ClassTypeInformation.from(SelfCyclingViaCollectionType.class));
|
||||
|
||||
new MongoPersistentEntityIndexResolver(prepareMappingContext(SelfCyclingViaCollectionType.class))
|
||||
.resolveIndexForEntity(dummy);
|
||||
.resolveIndexForEntity(selfCyclingEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1025
|
||||
*/
|
||||
@Test
|
||||
public void shouldUsePathIndexAsIndexNameForDocumentsHavingNamedNestedCompoundIndexFixedOnCollection() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedDocumentHavingNamedCompoundIndex.class);
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"),
|
||||
equalTo("propertyOfTypeHavingNamedCompoundIndex.c_index"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1025
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseIndexNameForNestedTypesWithNamedCompoundIndexDefinition() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedTypeHavingNamedCompoundIndex.class);
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"),
|
||||
equalTo("propertyOfTypeHavingNamedCompoundIndex.c_index"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1025
|
||||
*/
|
||||
@Test
|
||||
public void shouldUsePathIndexAsIndexNameForDocumentsHavingNamedNestedIndexFixedOnCollection() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedDocumentHavingNamedIndex.class);
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"),
|
||||
equalTo("propertyOfTypeHavingNamedIndex.property_index"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1025
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseIndexNameForNestedTypesWithNamedIndexDefinition() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNestedTypeHavingNamedIndex.class);
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"),
|
||||
equalTo("propertyOfTypeHavingNamedIndex.property_index"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1025
|
||||
*/
|
||||
@Test
|
||||
public void shouldUseIndexNameOnRootLevel() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(DocumentWithNamedIndex.class);
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), equalTo("property_index"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1087
|
||||
*/
|
||||
@Test
|
||||
public void shouldAllowMultiplePropertiesOfSameTypeWithMatchingStartLettersOnRoot() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(MultiplePropertiesOfSameTypeWithMatchingStartLetters.class);
|
||||
|
||||
assertThat(indexDefinitions, hasSize(2));
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), equalTo("name.component"));
|
||||
assertThat((String) indexDefinitions.get(1).getIndexOptions().get("name"), equalTo("nameLast.component"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1087
|
||||
*/
|
||||
@Test
|
||||
public void shouldAllowMultiplePropertiesOfSameTypeWithMatchingStartLettersOnNestedProperty() {
|
||||
|
||||
List<IndexDefinitionHolder> indexDefinitions = prepareMappingContextAndResolveIndexForType(MultiplePropertiesOfSameTypeWithMatchingStartLettersOnNestedProperty.class);
|
||||
|
||||
assertThat(indexDefinitions, hasSize(2));
|
||||
assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), equalTo("component.nameLast"));
|
||||
assertThat((String) indexDefinitions.get(1).getIndexOptions().get("name"), equalTo("component.name"));
|
||||
}
|
||||
|
||||
@Document
|
||||
@@ -836,6 +930,78 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
List<SelfCyclingViaCollectionType> cyclic;
|
||||
|
||||
}
|
||||
|
||||
@Document
|
||||
@CompoundIndex(name = "c_index", def = "{ foo:1, bar:1 }")
|
||||
static class DocumentWithNamedCompoundIndex {
|
||||
|
||||
String property;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNamedIndex {
|
||||
|
||||
@Indexed(name = "property_index") String property;
|
||||
}
|
||||
|
||||
static class TypeWithNamedIndex {
|
||||
|
||||
@Indexed(name = "property_index") String property;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNestedDocumentHavingNamedCompoundIndex {
|
||||
|
||||
DocumentWithNamedCompoundIndex propertyOfTypeHavingNamedCompoundIndex;
|
||||
}
|
||||
|
||||
@CompoundIndex(name = "c_index", def = "{ foo:1, bar:1 }")
|
||||
static class TypeWithNamedCompoundIndex {
|
||||
String property;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNestedTypeHavingNamedCompoundIndex {
|
||||
|
||||
TypeWithNamedCompoundIndex propertyOfTypeHavingNamedCompoundIndex;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNestedDocumentHavingNamedIndex {
|
||||
|
||||
DocumentWithNamedIndex propertyOfTypeHavingNamedIndex;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class DocumentWithNestedTypeHavingNamedIndex {
|
||||
|
||||
TypeWithNamedIndex propertyOfTypeHavingNamedIndex;
|
||||
}
|
||||
|
||||
@Document
|
||||
public class MultiplePropertiesOfSameTypeWithMatchingStartLetters {
|
||||
|
||||
public class NameComponent {
|
||||
|
||||
@Indexed String component;
|
||||
}
|
||||
|
||||
NameComponent name;
|
||||
NameComponent nameLast;
|
||||
}
|
||||
|
||||
@Document
|
||||
public class MultiplePropertiesOfSameTypeWithMatchingStartLettersOnNestedProperty {
|
||||
|
||||
public class NameComponent {
|
||||
|
||||
@Indexed String nameLast;
|
||||
@Indexed String name;
|
||||
}
|
||||
|
||||
NameComponent component;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static List<IndexDefinitionHolder> prepareMappingContextAndResolveIndexForType(Class<?> type) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
|
||||
/**
|
||||
@@ -36,6 +37,7 @@ import org.springframework.data.util.ClassTypeInformation;
|
||||
public class BasicMongoPersistentEntityUnitTests {
|
||||
|
||||
@Mock ApplicationContext context;
|
||||
@Mock MongoPersistentProperty propertyMock;
|
||||
|
||||
@Test
|
||||
public void subclassInheritsAtDocumentAnnotation() {
|
||||
@@ -53,6 +55,9 @@ public class BasicMongoPersistentEntityUnitTests {
|
||||
assertThat(entity.getCollection(), is("35"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-65, DATAMONGO-1108
|
||||
*/
|
||||
@Test
|
||||
public void collectionAllowsReferencingSpringBean() {
|
||||
|
||||
@@ -67,6 +72,9 @@ public class BasicMongoPersistentEntityUnitTests {
|
||||
entity.setApplicationContext(context);
|
||||
|
||||
assertThat(entity.getCollection(), is("reference"));
|
||||
|
||||
provider.collectionName = "otherReference";
|
||||
assertThat(entity.getCollection(), is("otherReference"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,6 +88,61 @@ public class BasicMongoPersistentEntityUnitTests {
|
||||
assertThat(entity.getLanguage(), is("spanish"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1053
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Test(expected = MappingException.class)
|
||||
public void verifyShouldThrowExceptionForInvalidTypeOfExplicitLanguageProperty() {
|
||||
|
||||
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
|
||||
ClassTypeInformation.from(AnyDocument.class));
|
||||
|
||||
when(propertyMock.isExplicitLanguageProperty()).thenReturn(true);
|
||||
when(propertyMock.getActualType()).thenReturn((Class) Number.class);
|
||||
|
||||
entity.addPersistentProperty(propertyMock);
|
||||
entity.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1053
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Test
|
||||
public void verifyShouldPassForStringAsExplicitLanguageProperty() {
|
||||
|
||||
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
|
||||
ClassTypeInformation.from(AnyDocument.class));
|
||||
when(propertyMock.isExplicitLanguageProperty()).thenReturn(true);
|
||||
when(propertyMock.getActualType()).thenReturn((Class) String.class);
|
||||
entity.addPersistentProperty(propertyMock);
|
||||
|
||||
entity.verify();
|
||||
|
||||
verify(propertyMock, times(1)).isExplicitLanguageProperty();
|
||||
verify(propertyMock, times(1)).getActualType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1053
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Test
|
||||
public void verifyShouldIgnoreNonExplicitLanguageProperty() {
|
||||
|
||||
BasicMongoPersistentEntity<AnyDocument> entity = new BasicMongoPersistentEntity<AnyDocument>(
|
||||
ClassTypeInformation.from(AnyDocument.class));
|
||||
when(propertyMock.isExplicitLanguageProperty()).thenReturn(false);
|
||||
when(propertyMock.getActualType()).thenReturn((Class) Number.class);
|
||||
entity.addPersistentProperty(propertyMock);
|
||||
|
||||
entity.verify();
|
||||
|
||||
verify(propertyMock, times(1)).isExplicitLanguageProperty();
|
||||
verify(propertyMock, never()).getActualType();
|
||||
}
|
||||
|
||||
@Document(collection = "contacts")
|
||||
class Contact {
|
||||
|
||||
@@ -111,4 +174,8 @@ public class BasicMongoPersistentEntityUnitTests {
|
||||
static class DocumentWithLanguage {
|
||||
|
||||
}
|
||||
|
||||
static class AnyDocument {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,10 +130,7 @@ public class BasicMongoPersistentPropertyUnitTests {
|
||||
@Test
|
||||
public void shouldDetectAnnotatedLanguagePropertyCorrectly() {
|
||||
|
||||
BasicMongoPersistentEntity<DocumentWithLanguageProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithLanguageProperty>(
|
||||
ClassTypeInformation.from(DocumentWithLanguageProperty.class));
|
||||
|
||||
MongoPersistentProperty property = getPropertyFor(persistentEntity, "lang");
|
||||
MongoPersistentProperty property = getPropertyFor(DocumentWithLanguageProperty.class, "lang");
|
||||
assertThat(property.isLanguageProperty(), is(true));
|
||||
}
|
||||
|
||||
@@ -143,10 +140,7 @@ public class BasicMongoPersistentPropertyUnitTests {
|
||||
@Test
|
||||
public void shouldDetectIplicitLanguagePropertyCorrectly() {
|
||||
|
||||
BasicMongoPersistentEntity<DocumentWithImplicitLanguageProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithImplicitLanguageProperty>(
|
||||
ClassTypeInformation.from(DocumentWithImplicitLanguageProperty.class));
|
||||
|
||||
MongoPersistentProperty property = getPropertyFor(persistentEntity, "language");
|
||||
MongoPersistentProperty property = getPropertyFor(DocumentWithImplicitLanguageProperty.class, "language");
|
||||
assertThat(property.isLanguageProperty(), is(true));
|
||||
}
|
||||
|
||||
@@ -156,10 +150,7 @@ public class BasicMongoPersistentPropertyUnitTests {
|
||||
@Test
|
||||
public void shouldDetectTextScorePropertyCorrectly() {
|
||||
|
||||
BasicMongoPersistentEntity<DocumentWithTextScoreProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithTextScoreProperty>(
|
||||
ClassTypeInformation.from(DocumentWithTextScoreProperty.class));
|
||||
|
||||
MongoPersistentProperty property = getPropertyFor(persistentEntity, "score");
|
||||
MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score");
|
||||
assertThat(property.isTextScoreProperty(), is(true));
|
||||
}
|
||||
|
||||
@@ -169,17 +160,39 @@ public class BasicMongoPersistentPropertyUnitTests {
|
||||
@Test
|
||||
public void shouldDetectTextScoreAsReadOnlyProperty() {
|
||||
|
||||
BasicMongoPersistentEntity<DocumentWithTextScoreProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithTextScoreProperty>(
|
||||
ClassTypeInformation.from(DocumentWithTextScoreProperty.class));
|
||||
|
||||
MongoPersistentProperty property = getPropertyFor(persistentEntity, "score");
|
||||
MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score");
|
||||
assertThat(property.isWritable(), is(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1050
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotConsiderExplicitlyNameFieldAsIdProperty() {
|
||||
|
||||
MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdProperty.class, "id");
|
||||
assertThat(property.isIdProperty(), is(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1050
|
||||
*/
|
||||
@Test
|
||||
public void shouldConsiderPropertyAsIdWhenExplicitlyAnnotatedWithIdEvenWhenExplicitlyNamePresent() {
|
||||
|
||||
MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation.class,
|
||||
"id");
|
||||
assertThat(property.isIdProperty(), is(true));
|
||||
}
|
||||
|
||||
private MongoPersistentProperty getPropertyFor(Field field) {
|
||||
return getPropertyFor(entity, field);
|
||||
}
|
||||
|
||||
private <T> MongoPersistentProperty getPropertyFor(Class<T> type, String fieldname) {
|
||||
return getPropertyFor(new BasicMongoPersistentEntity<T>(ClassTypeInformation.from(type)), fieldname);
|
||||
}
|
||||
|
||||
private MongoPersistentProperty getPropertyFor(MongoPersistentEntity<?> persistentEntity, String fieldname) {
|
||||
return getPropertyFor(persistentEntity, ReflectionUtils.findField(persistentEntity.getType(), fieldname));
|
||||
}
|
||||
@@ -230,4 +243,14 @@ public class BasicMongoPersistentPropertyUnitTests {
|
||||
static class DocumentWithTextScoreProperty {
|
||||
@TextScore Float score;
|
||||
}
|
||||
|
||||
static class DocumentWithExplicitlyRenamedIdProperty {
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.Field("id") String id;
|
||||
}
|
||||
|
||||
static class DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation {
|
||||
|
||||
@Id @org.springframework.data.mongodb.core.mapping.Field("id") String id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ public class MongoMappingContextUnitTests {
|
||||
exception.expectMessage("firstname");
|
||||
exception.expectMessage("lastname");
|
||||
exception.expectMessage("foo");
|
||||
exception.expectMessage("@Field");
|
||||
|
||||
MongoMappingContext context = new MongoMappingContext();
|
||||
context.setApplicationContext(applicationContext);
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
/*
|
||||
* 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.mapping;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PreferredConstructor;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.SimpleAssociationHandler;
|
||||
import org.springframework.data.mapping.SimplePropertyHandler;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* Trivial dummy implementation of {@link MongoPersistentEntity} to be used in tests.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @param <T>
|
||||
*/
|
||||
public class MongoPersistentEntityTestDummy<T> implements MongoPersistentEntity<T> {
|
||||
|
||||
private Map<Class<?>, Annotation> annotations = new HashMap<Class<?>, Annotation>();
|
||||
private Collection<MongoPersistentProperty> properties = new ArrayList<MongoPersistentProperty>();
|
||||
private String collection;
|
||||
private String name;
|
||||
private Class<T> type;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreferredConstructor<T, MongoPersistentProperty> getPersistenceConstructor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConstructorArgument(PersistentProperty<?> property) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdProperty(PersistentProperty<?> property) {
|
||||
return property != null ? property.isIdProperty() : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVersionProperty(PersistentProperty<?> property) {
|
||||
return property != null ? property.isIdProperty() : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getIdProperty() {
|
||||
return getPersistentProperty(Id.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getVersionProperty() {
|
||||
return getPersistentProperty(Version.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getPersistentProperty(String name) {
|
||||
|
||||
for (MongoPersistentProperty p : this.properties) {
|
||||
if (p.getName().equals(name)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getPersistentProperty(Class<? extends Annotation> annotationType) {
|
||||
|
||||
for (MongoPersistentProperty p : this.properties) {
|
||||
if (p.isAnnotationPresent(annotationType)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdProperty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVersionProperty() {
|
||||
return getVersionProperty() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTypeAlias() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeInformation<T> getTypeInformation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithProperties(PropertyHandler<MongoPersistentProperty> handler) {
|
||||
|
||||
for (MongoPersistentProperty p : this.properties) {
|
||||
handler.doWithPersistentProperty(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithProperties(SimplePropertyHandler handler) {
|
||||
|
||||
for (MongoPersistentProperty p : this.properties) {
|
||||
handler.doWithPersistentProperty(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithAssociations(AssociationHandler<MongoPersistentProperty> handler) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithAssociations(SimpleAssociationHandler handler) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
|
||||
return (A) this.annotations.get(annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollection() {
|
||||
return this.collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple builder to create {@link MongoPersistentEntityTestDummy} with defined properties.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @param <T>
|
||||
*/
|
||||
public static class MongoPersistentEntityDummyBuilder<T> {
|
||||
|
||||
private MongoPersistentEntityTestDummy<T> instance;
|
||||
|
||||
private MongoPersistentEntityDummyBuilder(Class<T> type) {
|
||||
this.instance = new MongoPersistentEntityTestDummy<T>();
|
||||
this.instance.type = type;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static <T> MongoPersistentEntityDummyBuilder<T> forClass(Class<T> type) {
|
||||
return new MongoPersistentEntityDummyBuilder(type);
|
||||
}
|
||||
|
||||
public MongoPersistentEntityDummyBuilder<T> withName(String name) {
|
||||
this.instance.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MongoPersistentEntityDummyBuilder<T> and(MongoPersistentProperty property) {
|
||||
this.instance.properties.add(property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MongoPersistentEntityDummyBuilder<T> withCollection(String collection) {
|
||||
this.instance.collection = collection;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MongoPersistentEntityDummyBuilder<T> and(Annotation annotation) {
|
||||
this.instance.annotations.put(annotation.annotationType(), annotation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MongoPersistentEntityTestDummy<T> build() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoPersistentProperty getTextScoreProperty() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTextScoreProperty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ import com.mongodb.Mongo;
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
@@ -60,7 +61,6 @@ public class GroupByTests {
|
||||
MongoTemplate mongoTemplate;
|
||||
|
||||
@Autowired
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setMongo(Mongo mongo) throws Exception {
|
||||
|
||||
MongoMappingContext mappingContext = new MongoMappingContext();
|
||||
@@ -116,7 +116,7 @@ public class GroupByTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SimpleGroup() {
|
||||
public void simpleGroupFunction() {
|
||||
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results = mongoTemplate.group(
|
||||
@@ -128,7 +128,7 @@ public class GroupByTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SimpleGroupWithKeyFunction() {
|
||||
public void simpleGroupWithKeyFunction() {
|
||||
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results = mongoTemplate.group(
|
||||
@@ -140,7 +140,7 @@ public class GroupByTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SimpleGroupWithFunctionsAsResources() {
|
||||
public void simpleGroupWithFunctionsAsResources() {
|
||||
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results = mongoTemplate.group(
|
||||
@@ -152,7 +152,7 @@ public class GroupByTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SimpleGroupWithQueryAndFunctionsAsResources() {
|
||||
public void simpleGroupWithQueryAndFunctionsAsResources() {
|
||||
|
||||
createGroupByData();
|
||||
GroupByResults<XObject> results = mongoTemplate.group(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2012 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.
|
||||
@@ -18,6 +18,8 @@ package org.springframework.data.mongodb.core.query;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import nl.jqno.equalsverifier.EqualsVerifier;
|
||||
import nl.jqno.equalsverifier.Warning;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
@@ -29,6 +31,7 @@ import com.mongodb.DBObject;
|
||||
* Unit tests for {@link BasicQuery}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class BasicQueryUnitTests {
|
||||
|
||||
@@ -58,4 +61,80 @@ public class BasicQueryUnitTests {
|
||||
sortReference.put("lastname", 1);
|
||||
assertThat(query.getSortObject(), is(sortReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1093
|
||||
*/
|
||||
@Test
|
||||
public void equalsContract() {
|
||||
|
||||
BasicQuery query1 = new BasicQuery("{ \"name\" : \"Thomas\"}", "{\"name\":1, \"age\":1}");
|
||||
query1.setSortObject(new BasicDBObject("name", -1));
|
||||
|
||||
BasicQuery query2 = new BasicQuery("{ \"name\" : \"Oliver\"}", "{\"name\":1, \"address\":1}");
|
||||
query2.setSortObject(new BasicDBObject("name", 1));
|
||||
|
||||
EqualsVerifier.forExamples(query1, query2) //
|
||||
.withRedefinedSuperclass() //
|
||||
.suppress(Warning.NONFINAL_FIELDS, Warning.NULL_FIELDS, Warning.STRICT_INHERITANCE) //
|
||||
.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1093
|
||||
*/
|
||||
@Test
|
||||
public void handlesEqualsAndHashCodeCorrectlyForExactCopies() {
|
||||
|
||||
String qry = "{ \"name\" : \"Thomas\"}";
|
||||
String fields = "{\"name\":1, \"age\":1}";
|
||||
|
||||
BasicQuery query1 = new BasicQuery(qry, fields);
|
||||
query1.setSortObject(new BasicDBObject("name", -1));
|
||||
|
||||
BasicQuery query2 = new BasicQuery(qry, fields);
|
||||
query2.setSortObject(new BasicDBObject("name", -1));
|
||||
|
||||
assertThat(query1, is(equalTo(query1)));
|
||||
assertThat(query1, is(equalTo(query2)));
|
||||
assertThat(query1.hashCode(), is(query2.hashCode()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1093
|
||||
*/
|
||||
@Test
|
||||
public void handlesEqualsAndHashCodeCorrectlyWhenBasicQuerySettingsDiffer() {
|
||||
|
||||
String qry = "{ \"name\" : \"Thomas\"}";
|
||||
String fields = "{\"name\":1, \"age\":1}";
|
||||
|
||||
BasicQuery query1 = new BasicQuery(qry, fields);
|
||||
query1.setSortObject(new BasicDBObject("name", -1));
|
||||
|
||||
BasicQuery query2 = new BasicQuery(qry, fields);
|
||||
query2.setSortObject(new BasicDBObject("name", 1));
|
||||
|
||||
assertThat(query1, is(not(equalTo(query2))));
|
||||
assertThat(query1.hashCode(), is(not(query2.hashCode())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1093
|
||||
*/
|
||||
@Test
|
||||
public void handlesEqualsAndHashCodeCorrectlyWhenQuerySettingsDiffer() {
|
||||
|
||||
String qry = "{ \"name\" : \"Thomas\"}";
|
||||
String fields = "{\"name\":1, \"age\":1}";
|
||||
|
||||
BasicQuery query1 = new BasicQuery(qry, fields);
|
||||
query1.getMeta().setComment("foo");
|
||||
|
||||
BasicQuery query2 = new BasicQuery(qry, fields);
|
||||
query2.getMeta().setComment("bar");
|
||||
|
||||
assertThat(query1, is(not(equalTo(query2))));
|
||||
assertThat(query1.hashCode(), is(not(query2.hashCode())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2013 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.
|
||||
@@ -22,12 +22,14 @@ import org.junit.Test;
|
||||
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class CriteriaTests {
|
||||
|
||||
@Test
|
||||
@@ -72,50 +74,94 @@ public class CriteriaTests {
|
||||
assertThat(left, is(not(right)));
|
||||
assertThat(right, is(not(left)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-507
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExceptionWhenTryingToNegateAndOperation() {
|
||||
|
||||
new Criteria() //
|
||||
.not() //
|
||||
.andOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-507
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExceptionWhenTryingToNegateOrOperation() {
|
||||
|
||||
new Criteria() //
|
||||
.not() //
|
||||
.orOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-507
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExceptionWhenTryingToNegateNorOperation() {
|
||||
|
||||
new Criteria() //
|
||||
.not() //
|
||||
.norOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-507
|
||||
*/
|
||||
@Test
|
||||
public void shouldNegateFollowingSimpleExpression() {
|
||||
|
||||
Criteria c = Criteria.where("age").not().gt(18).and("status").is("student");
|
||||
DBObject co = c.getCriteriaObject();
|
||||
|
||||
assertThat(co, is(notNullValue()));
|
||||
assertThat(co.toString(), is("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-507
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExceptionWhenTryingToNegateAndOperation() {
|
||||
|
||||
new Criteria() //
|
||||
.not() //
|
||||
.andOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-507
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExceptionWhenTryingToNegateOrOperation() {
|
||||
|
||||
new Criteria() //
|
||||
.not() //
|
||||
.orOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-507
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExceptionWhenTryingToNegateNorOperation() {
|
||||
|
||||
new Criteria() //
|
||||
.not() //
|
||||
.norOperator(Criteria.where("delete").is(true).and("_id").is(42)); //
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-507
|
||||
*/
|
||||
@Test
|
||||
public void shouldNegateFollowingSimpleExpression() {
|
||||
|
||||
Criteria c = Criteria.where("age").not().gt(18).and("status").is("student");
|
||||
DBObject co = c.getCriteriaObject();
|
||||
|
||||
assertThat(co, is(notNullValue()));
|
||||
assertThat(co.toString(), is("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1068
|
||||
*/
|
||||
@Test
|
||||
public void getCriteriaObjectShouldReturnEmptyDBOWhenNoCriteriaSpecified() {
|
||||
|
||||
DBObject dbo = new Criteria().getCriteriaObject();
|
||||
|
||||
assertThat(dbo, equalTo(new BasicDBObjectBuilder().get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1068
|
||||
*/
|
||||
@Test
|
||||
public void getCriteriaObjectShouldUseCritieraValuesWhenNoKeyIsPresent() {
|
||||
|
||||
DBObject dbo = new Criteria().lt("foo").getCriteriaObject();
|
||||
|
||||
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("$lt", "foo").get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1068
|
||||
*/
|
||||
@Test
|
||||
public void getCriteriaObjectShouldUseCritieraValuesWhenNoKeyIsPresentButMultipleCriteriasPresent() {
|
||||
|
||||
DBObject dbo = new Criteria().lt("foo").gt("bar").getCriteriaObject();
|
||||
|
||||
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("$lt", "foo").add("$gt", "bar").get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1068
|
||||
*/
|
||||
@Test
|
||||
public void getCriteriaObjectShouldRespectNotWhenNoKeyPresent() {
|
||||
|
||||
DBObject dbo = new Criteria().lt("foo").not().getCriteriaObject();
|
||||
|
||||
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("$not", new BasicDBObject("$lt", "foo")).get()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,4 +420,68 @@ public class UpdateTests {
|
||||
Update update = new Update().addToSet("key", new DateTime());
|
||||
assertThat(update.toString(), is(notNullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1097
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void multiplyShouldThrowExceptionForNullMultiplier() {
|
||||
new Update().multiply("key", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1097
|
||||
*/
|
||||
@Test
|
||||
public void multiplyShouldAddMultiplierAsItsDoubleValue() {
|
||||
|
||||
Update update = new Update().multiply("key", 10);
|
||||
|
||||
assertThat(update.getUpdateObject(), equalTo(new BasicDBObjectBuilder().add("$mul", new BasicDBObject("key", 10D))
|
||||
.get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1101
|
||||
*/
|
||||
@Test
|
||||
public void getUpdateObjectShouldReturnCorrectRepresentationForBitwiseAnd() {
|
||||
|
||||
Update update = new Update().bitwise("key").and(10L);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new BasicDBObjectBuilder().add("$bit", new BasicDBObject("key", new BasicDBObject("and", 10L))).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1101
|
||||
*/
|
||||
@Test
|
||||
public void getUpdateObjectShouldReturnCorrectRepresentationForBitwiseOr() {
|
||||
|
||||
Update update = new Update().bitwise("key").or(10L);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new BasicDBObjectBuilder().add("$bit", new BasicDBObject("key", new BasicDBObject("or", 10L))).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1101
|
||||
*/
|
||||
@Test
|
||||
public void getUpdateObjectShouldReturnCorrectRepresentationForBitwiseXor() {
|
||||
|
||||
Update update = new Update().bitwise("key").xor(10L);
|
||||
|
||||
assertThat(update.getUpdateObject(),
|
||||
equalTo(new BasicDBObjectBuilder().add("$bit", new BasicDBObject("key", new BasicDBObject("xor", 10L))).get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-943
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void pushShouldThrowExceptionWhenGivenNegativePosition() {
|
||||
new Update().push("foo").atPosition(-1).each("booh");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@ import static java.util.Arrays.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@@ -46,6 +48,7 @@ import org.springframework.data.geo.Polygon;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.repository.Person.Sex;
|
||||
import org.springframework.data.querydsl.QSort;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
@@ -1011,4 +1014,138 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
public void shouldFindPersonsWhenUsingQueryDslPerdicatedOnIdProperty() {
|
||||
assertThat(repository.findAll(person.id.in(Arrays.asList(dave.id, carter.id))), containsInAnyOrder(dave, carter));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1030
|
||||
*/
|
||||
@Test
|
||||
public void executesSingleEntityQueryWithProjectionCorrectly() {
|
||||
|
||||
PersonSummary result = repository.findSummaryByLastname("Beauford");
|
||||
|
||||
assertThat(result, is(notNullValue()));
|
||||
assertThat(result.firstname, is("Carter"));
|
||||
assertThat(result.lastname, is("Beauford"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1057
|
||||
*/
|
||||
@Test
|
||||
public void sliceShouldTraverseElementsWithoutSkippingOnes() {
|
||||
|
||||
repository.deleteAll();
|
||||
|
||||
List<Person> persons = new ArrayList<Person>(100);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
// format firstname to assert sorting retains proper order
|
||||
persons.add(new Person(String.format("%03d", i), "ln" + 1, 100));
|
||||
}
|
||||
|
||||
repository.save(persons);
|
||||
|
||||
Slice<Person> slice = repository.findByAgeGreaterThan(50, new PageRequest(0, 20, Direction.ASC, "firstname"));
|
||||
assertThat(slice, contains(persons.subList(0, 20).toArray()));
|
||||
|
||||
slice = repository.findByAgeGreaterThan(50, slice.nextPageable());
|
||||
assertThat(slice, contains(persons.subList(20, 40).toArray()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1072
|
||||
*/
|
||||
@Test
|
||||
public void shouldBindPlaceholdersUsedAsKeysCorrectly() {
|
||||
|
||||
List<Person> persons = repository.findByKeyValue("firstname", alicia.getFirstname());
|
||||
|
||||
assertThat(persons, hasSize(1));
|
||||
assertThat(persons, hasItem(alicia));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1105
|
||||
*/
|
||||
@Test
|
||||
public void returnsOrderedResultsForQuerydslOrderSpecifier() {
|
||||
|
||||
Iterable<Person> result = repository.findAll(person.firstname.asc());
|
||||
|
||||
assertThat(result, contains(alicia, boyd, carter, dave, leroi, oliver, stefan));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1085
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportSortingByQueryDslOrderSpecifier() {
|
||||
|
||||
repository.deleteAll();
|
||||
|
||||
List<Person> persons = new ArrayList<Person>();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Person person = new Person(String.format("Siggi %s", i), "Bar", 30);
|
||||
person.setAddress(new Address(String.format("Street %s", i), "12345", "SinCity"));
|
||||
persons.add(person);
|
||||
}
|
||||
|
||||
repository.save(persons);
|
||||
|
||||
QPerson person = QPerson.person;
|
||||
|
||||
Iterable<Person> result = repository.findAll(person.firstname.isNotNull(), person.address.street.desc());
|
||||
|
||||
assertThat(result, is(Matchers.<Person> iterableWithSize(persons.size())));
|
||||
assertThat(result.iterator().next().getFirstname(), is(persons.get(2).getFirstname()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1085
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportSortingWithQSortByQueryDslOrderSpecifier() throws Exception {
|
||||
|
||||
repository.deleteAll();
|
||||
|
||||
List<Person> persons = new ArrayList<Person>();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Person person = new Person(String.format("Siggi %s", i), "Bar", 30);
|
||||
person.setAddress(new Address(String.format("Street %s", i), "12345", "SinCity"));
|
||||
persons.add(person);
|
||||
}
|
||||
|
||||
repository.save(persons);
|
||||
|
||||
PageRequest pageRequest = new PageRequest(0, 2, new QSort(person.address.street.desc()));
|
||||
Iterable<Person> result = repository.findAll(pageRequest);
|
||||
|
||||
assertThat(result, is(Matchers.<Person> iterableWithSize(2)));
|
||||
assertThat(result.iterator().next().getFirstname(), is("Siggi 2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1085
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportSortingWithQSort() throws Exception {
|
||||
|
||||
repository.deleteAll();
|
||||
|
||||
List<Person> persons = new ArrayList<Person>();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Person person = new Person(String.format("Siggi %s", i), "Bar", 30);
|
||||
person.setAddress(new Address(String.format("Street %s", i), "12345", "SinCity"));
|
||||
persons.add(person);
|
||||
}
|
||||
|
||||
repository.save(persons);
|
||||
|
||||
Iterable<Person> result = repository.findAll(new QSort(person.address.street.desc()));
|
||||
|
||||
assertThat(result, is(Matchers.<Person> iterableWithSize(persons.size())));
|
||||
assertThat(result.iterator().next().getFirstname(), is("Siggi 2"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
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.Configuration;
|
||||
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class ComplexIdRepositoryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@EnableMongoRepositories
|
||||
static class Config extends AbstractMongoConfiguration {
|
||||
|
||||
@Override
|
||||
protected String getDatabaseName() {
|
||||
return "complexIdTest";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mongo mongo() throws Exception {
|
||||
return new MongoClient();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Autowired UserWithComplexIdRepository repo;
|
||||
@Autowired MongoTemplate template;
|
||||
|
||||
MyId id;
|
||||
UserWithComplexId userWithId;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
repo.deleteAll();
|
||||
|
||||
id = new MyId();
|
||||
id.val1 = "v1";
|
||||
id.val2 = "v2";
|
||||
|
||||
userWithId = new UserWithComplexId();
|
||||
userWithId.firstname = "foo";
|
||||
userWithId.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1078
|
||||
*/
|
||||
@Test
|
||||
public void annotatedFindQueryShouldWorkWhenUsingComplexId() {
|
||||
|
||||
repo.save(userWithId);
|
||||
|
||||
assertThat(repo.getUserByComplexId(id), is(userWithId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1078
|
||||
*/
|
||||
@Test
|
||||
public void annotatedFindQueryShouldWorkWhenUsingComplexIdWithinCollection() {
|
||||
|
||||
repo.save(userWithId);
|
||||
|
||||
List<UserWithComplexId> loaded = repo.findByUserIds(Collections.singleton(id));
|
||||
|
||||
assertThat(loaded, hasSize(1));
|
||||
assertThat(loaded, contains(userWithId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1078
|
||||
*/
|
||||
@Test
|
||||
public void findOneShouldWorkWhenUsingComplexId() {
|
||||
|
||||
repo.save(userWithId);
|
||||
|
||||
assertThat(repo.findOne(id), is(userWithId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1078
|
||||
*/
|
||||
@Test
|
||||
public void findAllShouldWorkWhenUsingComplexId() {
|
||||
|
||||
repo.save(userWithId);
|
||||
|
||||
Iterable<UserWithComplexId> loaded = repo.findAll(Collections.singleton(id));
|
||||
|
||||
assertThat(loaded, is(Matchers.<UserWithComplexId> iterableWithSize(1)));
|
||||
assertThat(loaded, contains(userWithId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MyId implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -7129201311241750831L;
|
||||
|
||||
String val1;
|
||||
String val2;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 31;
|
||||
|
||||
result += 17 * ObjectUtils.nullSafeHashCode(val1);
|
||||
result += 17 * ObjectUtils.nullSafeHashCode(val2);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof MyId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MyId that = (MyId) obj;
|
||||
|
||||
return ObjectUtils.nullSafeEquals(this.val1, that.val1) && ObjectUtils.nullSafeEquals(this.val2, that.val2);
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,8 @@ public class Person extends Contact {
|
||||
@SuppressWarnings("unused") private Sex sex;
|
||||
Date createdAt;
|
||||
|
||||
List<String> skills;
|
||||
|
||||
@GeoSpatialIndexed private Point location;
|
||||
|
||||
private Address address;
|
||||
@@ -271,6 +273,14 @@ public class Person extends Contact {
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
public void setSkills(List<String> skills) {
|
||||
this.skills = skills;
|
||||
}
|
||||
|
||||
public List<String> getSkills() {
|
||||
return skills;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
||||
@@ -313,4 +313,11 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
*/
|
||||
Page<Person> findTop3ByLastnameStartingWith(String lastname, Pageable pageRequest);
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1030
|
||||
*/
|
||||
PersonSummary findSummaryByLastname(String lastname);
|
||||
|
||||
@Query("{ ?0 : ?1 }")
|
||||
List<Person> findByKeyValue(String key, String value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PersonSummary {
|
||||
|
||||
String firstname;
|
||||
String lastname;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Document
|
||||
public class UserWithComplexId {
|
||||
|
||||
@Id MyId id;
|
||||
String firstname;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 31;
|
||||
|
||||
result += 17 * ObjectUtils.nullSafeHashCode(id);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof UserWithComplexId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UserWithComplexId that = (UserWithComplexId) obj;
|
||||
|
||||
return ObjectUtils.nullSafeEquals(this.id, that.id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface UserWithComplexIdRepository extends CrudRepository<UserWithComplexId, MyId> {
|
||||
|
||||
@Query("{'_id': {$in: ?0}}")
|
||||
List<UserWithComplexId> findByUserIds(Collection<MyId> ids);
|
||||
|
||||
@Query("{'_id': ?0}")
|
||||
UserWithComplexId getUserByComplexId(MyId id);
|
||||
}
|
||||
@@ -17,12 +17,14 @@ package org.springframework.data.mongodb.repository.query;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.hamcrest.core.Is;
|
||||
@@ -32,10 +34,13 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.Person;
|
||||
@@ -50,6 +55,8 @@ import org.springframework.data.mongodb.repository.Meta;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.WriteResult;
|
||||
|
||||
/**
|
||||
@@ -101,6 +108,7 @@ public class AbstractMongoQueryUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-566
|
||||
* @see DATAMONGO-1040
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
@@ -111,7 +119,8 @@ public class AbstractMongoQueryUnitTests {
|
||||
|
||||
createQueryForMethod("deleteByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" });
|
||||
|
||||
verify(this.mongoOperationsMock, times(1)).findAllAndRemove(Matchers.any(Query.class), Matchers.eq(Person.class));
|
||||
verify(this.mongoOperationsMock, times(1)).findAllAndRemove(Matchers.any(Query.class), Matchers.eq(Person.class),
|
||||
Matchers.eq("persons"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,6 +218,88 @@ public class AbstractMongoQueryUnitTests {
|
||||
assertThat(captor.getValue().getMeta().getComment(), is("comment"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1057
|
||||
*/
|
||||
@Test
|
||||
public void slicedExecutionShouldRetainNrOfElementsToSkip() {
|
||||
|
||||
MongoQueryFake query = createQueryForMethod("findByLastname", String.class, Pageable.class);
|
||||
Pageable page1 = new PageRequest(0, 10);
|
||||
Pageable page2 = page1.next();
|
||||
|
||||
query.execute(new Object[] { "fake", page1 });
|
||||
query.execute(new Object[] { "fake", page2 });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
|
||||
verify(this.mongoOperationsMock, times(2))
|
||||
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
|
||||
|
||||
assertThat(captor.getAllValues().get(0).getSkip(), is(0));
|
||||
assertThat(captor.getAllValues().get(1).getSkip(), is(10));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1057
|
||||
*/
|
||||
@Test
|
||||
public void slicedExecutionShouldIncrementLimitByOne() {
|
||||
|
||||
MongoQueryFake query = createQueryForMethod("findByLastname", String.class, Pageable.class);
|
||||
Pageable page1 = new PageRequest(0, 10);
|
||||
Pageable page2 = page1.next();
|
||||
|
||||
query.execute(new Object[] { "fake", page1 });
|
||||
query.execute(new Object[] { "fake", page2 });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
|
||||
verify(this.mongoOperationsMock, times(2))
|
||||
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
|
||||
|
||||
assertThat(captor.getAllValues().get(0).getLimit(), is(11));
|
||||
assertThat(captor.getAllValues().get(1).getLimit(), is(11));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1057
|
||||
*/
|
||||
@Test
|
||||
public void slicedExecutionShouldRetainSort() {
|
||||
|
||||
MongoQueryFake query = createQueryForMethod("findByLastname", String.class, Pageable.class);
|
||||
Pageable page1 = new PageRequest(0, 10, Sort.Direction.DESC, "bar");
|
||||
Pageable page2 = page1.next();
|
||||
|
||||
query.execute(new Object[] { "fake", page1 });
|
||||
query.execute(new Object[] { "fake", page2 });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
|
||||
verify(this.mongoOperationsMock, times(2))
|
||||
.find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons"));
|
||||
|
||||
DBObject expectedSortObject = new BasicDBObjectBuilder().add("bar", -1).get();
|
||||
assertThat(captor.getAllValues().get(0).getSortObject(), is(expectedSortObject));
|
||||
assertThat(captor.getAllValues().get(1).getSortObject(), is(expectedSortObject));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1080
|
||||
*/
|
||||
@Test
|
||||
public void doesNotTryToPostProcessQueryResultIntoWrapperType() {
|
||||
|
||||
Person reference = new Person();
|
||||
when(mongoOperationsMock.findOne(Mockito.any(Query.class), eq(Person.class), eq("persons"))).//
|
||||
thenReturn(reference);
|
||||
|
||||
AbstractMongoQuery query = createQueryForMethod("findByLastname", String.class);
|
||||
|
||||
assertThat(query.execute(new Object[] { "lastname" }), is((Object) reference));
|
||||
}
|
||||
|
||||
private MongoQueryFake createQueryForMethod(String methodName, Class<?>... paramTypes) {
|
||||
|
||||
try {
|
||||
@@ -270,5 +361,9 @@ public class AbstractMongoQueryUnitTests {
|
||||
@org.springframework.data.mongodb.repository.Query("{}")
|
||||
Page<Person> findByAnnotatedQuery(String firstnanme, Pageable pageable);
|
||||
|
||||
/** @see DATAMONGO-1057 */
|
||||
Slice<Person> findByLastname(String lastname, Pageable page);
|
||||
|
||||
Optional<Person> findByLastname(String lastname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
* Copyright 2011-2014 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,23 +36,31 @@ import org.springframework.data.mongodb.repository.support.MappingMongoEntityInf
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MappingMongoEntityInformationUnitTests {
|
||||
|
||||
@Mock
|
||||
MongoPersistentEntity<Person> info;
|
||||
@Mock MongoPersistentEntity<Person> info;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
when(info.getType()).thenReturn(Person.class);
|
||||
when(info.getCollection()).thenReturn("Person");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-248
|
||||
*/
|
||||
@Test
|
||||
public void usesEntityCollectionIfNoCustomOneGiven() {
|
||||
|
||||
MongoEntityInformation<Person, Long> information = new MappingMongoEntityInformation<Person, Long>(info);
|
||||
assertThat(information.getCollectionName(), is("Person"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-248
|
||||
*/
|
||||
@Test
|
||||
public void usesCustomCollectionIfGiven() {
|
||||
|
||||
MongoEntityInformation<Person, Long> information = new MappingMongoEntityInformation<Person, Long>(info, "foobar");
|
||||
assertThat(information.getCollectionName(), is("foobar"));
|
||||
}
|
||||
|
||||
@@ -167,19 +167,6 @@ public class MongoQueryCreatorUnitTests {
|
||||
assertThat(creator.createQuery(), is(reference));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-291
|
||||
*/
|
||||
@Test
|
||||
public void honoursMappingInformationForPropertyPaths() {
|
||||
|
||||
PartTree partTree = new PartTree("findByUsername", User.class);
|
||||
|
||||
MongoQueryCreator creator = new MongoQueryCreator(partTree, getAccessor(converter, "Oliver"), context);
|
||||
Query reference = query(where("foo").is("Oliver"));
|
||||
assertThat(creator.createQuery(), is(reference));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-338
|
||||
*/
|
||||
@@ -268,7 +255,7 @@ public class MongoQueryCreatorUnitTests {
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Matt"), context);
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertThat(query, is(query(where("foo").regex("^Matt"))));
|
||||
assertThat(query, is(query(where("username").regex("^Matt"))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,7 +268,7 @@ public class MongoQueryCreatorUnitTests {
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "ews"), context);
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertThat(query, is(query(where("foo").regex("ews$"))));
|
||||
assertThat(query, is(query(where("username").regex("ews$"))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,7 +281,7 @@ public class MongoQueryCreatorUnitTests {
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context);
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertThat(query, is(query(where("foo").regex(".*thew.*"))));
|
||||
assertThat(query, is(query(where("username").regex(".*thew.*"))));
|
||||
}
|
||||
|
||||
private void assertBindsDistanceToQuery(Point point, Distance distance, Query reference) throws Exception {
|
||||
@@ -438,6 +425,47 @@ public class MongoQueryCreatorUnitTests {
|
||||
assertThat(query, is(query(where("firstName").regex("^dave$", "i").and("age").is(42))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1075
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreateInClauseWhenUsingContainsOnCollectionLikeProperty() {
|
||||
|
||||
PartTree tree = new PartTree("findByEmailAddressesContaining", User.class);
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context);
|
||||
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertThat(query, is(query(where("emailAddresses").in("dave"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1075
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreateInClauseWhenUsingNotContainsOnCollectionLikeProperty() {
|
||||
|
||||
PartTree tree = new PartTree("findByEmailAddressesNotContaining", User.class);
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context);
|
||||
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertThat(query, is(query(where("emailAddresses").not().in("dave"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1075
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreateRegexWhenUsingNotContainsOnStringProperty() {
|
||||
|
||||
PartTree tree = new PartTree("findByUsernameNotContaining", User.class);
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context);
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertThat(query, is(query(where("username").regex(".*thew.*").not())));
|
||||
}
|
||||
|
||||
interface PersonRepository extends Repository<Person, Long> {
|
||||
|
||||
List<Person> findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);
|
||||
@@ -448,5 +476,7 @@ public class MongoQueryCreatorUnitTests {
|
||||
@Field("foo") String username;
|
||||
|
||||
@DBRef User creator;
|
||||
|
||||
List<String> emailAddresses;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mongodb.core.DBObjectTestUtils;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
|
||||
@@ -42,7 +43,9 @@ import org.springframework.data.mongodb.repository.Query;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.DBRef;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link StringBasedMongoQuery}.
|
||||
@@ -255,6 +258,37 @@ public class StringBasedMongoQueryUnitTests {
|
||||
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1070
|
||||
*/
|
||||
@Test
|
||||
public void parsesDbRefDeclarationsCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("methodWithManuallyDefinedDbRef", String.class);
|
||||
ConvertingParameterAccessor parameterAccessor = StubParameterAccessor.getAccessor(converter, "myid");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(parameterAccessor);
|
||||
|
||||
DBRef dbRef = DBObjectTestUtils.getTypedValue(query.getQueryObject(), "reference", DBRef.class);
|
||||
assertThat(dbRef.getId(), is((Object) "myid"));
|
||||
assertThat(dbRef.getRef(), is("reference"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1072
|
||||
*/
|
||||
@Test
|
||||
public void shouldParseJsonKeyReplacementCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("methodWithPlaceholderInKeyOfJsonStructure", String.class,
|
||||
String.class);
|
||||
ConvertingParameterAccessor parameterAccessor = StubParameterAccessor.getAccessor(converter, "key", "value");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(parameterAccessor);
|
||||
|
||||
assertThat(query.getQueryObject(), is(new BasicDBObjectBuilder().add("key", "value").get()));
|
||||
}
|
||||
|
||||
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod(name, parameters);
|
||||
@@ -291,7 +325,14 @@ public class StringBasedMongoQueryUnitTests {
|
||||
@Query("{'title': { $regex : '^?0', $options : 'i'}}")
|
||||
List<DBObject> findByTitleBeginsWithExplicitQuoting(String title);
|
||||
|
||||
@Query(value = "{$where: 'return this.date.getUTCMonth() == ?2 && this.date.getUTCDay() == ?3;'}")
|
||||
@Query("{$where: 'return this.date.getUTCMonth() == ?2 && this.date.getUTCDay() == ?3;'}")
|
||||
List<DBObject> findByQueryWithParametersInExpression(int param1, int param2, int param3, int param4);
|
||||
|
||||
@Query("{ 'reference' : { $ref : 'reference', $id : ?0 }}")
|
||||
Object methodWithManuallyDefinedDbRef(String id);
|
||||
|
||||
@Query("{ ?0 : ?1}")
|
||||
Object methodWithPlaceholderInKeyOfJsonStructure(String keyReplacement, String valueReplacement);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -18,6 +18,8 @@ package org.springframework.data.mongodb.repository.support;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -35,17 +37,18 @@ import com.mysema.query.mongodb.MongodbQuery;
|
||||
* Unit tests for {@link QuerydslRepositorySupport}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
public class QuerydslRepositorySupportUnitTests {
|
||||
public class QuerydslRepositorySupportTests {
|
||||
|
||||
@Autowired
|
||||
MongoOperations operations;
|
||||
@Autowired MongoOperations operations;
|
||||
Person person;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
operations.remove(new Query(), Person.class);
|
||||
person = new Person("Dave", "Matthews");
|
||||
operations.save(person);
|
||||
@@ -53,10 +56,28 @@ public class QuerydslRepositorySupportUnitTests {
|
||||
|
||||
@Test
|
||||
public void providesMongoQuery() {
|
||||
|
||||
QPerson p = QPerson.person;
|
||||
QuerydslRepositorySupport support = new QuerydslRepositorySupport(operations) {
|
||||
};
|
||||
QuerydslRepositorySupport support = new QuerydslRepositorySupport(operations) {};
|
||||
MongodbQuery<Person> query = support.from(p).where(p.lastname.eq("Matthews"));
|
||||
assertThat(query.uniqueResult(), is(person));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1063
|
||||
*/
|
||||
@Test
|
||||
public void shouldAllowAny() {
|
||||
|
||||
person.setSkills(Arrays.asList("vocalist", "songwriter", "guitarist"));
|
||||
|
||||
operations.save(person);
|
||||
|
||||
QPerson p = QPerson.person;
|
||||
QuerydslRepositorySupport support = new QuerydslRepositorySupport(operations) {};
|
||||
|
||||
MongodbQuery<Person> query = support.from(p).where(p.skills.any().in("guitarist"));
|
||||
|
||||
assertThat(query.uniqueResult(), is(person));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2012 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.
|
||||
@@ -16,10 +16,16 @@
|
||||
package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -34,13 +40,13 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:kowsercse@gmail.com">A. B. M. Kowser</a>
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
public class SimpleMongoRepositoryTests {
|
||||
|
||||
@Autowired
|
||||
private MongoTemplate template;
|
||||
@Autowired private MongoTemplate template;
|
||||
|
||||
private Person oliver, dave, carter, boyd, stefan, leroi, alicia;
|
||||
private List<Person> all;
|
||||
@@ -69,7 +75,7 @@ public class SimpleMongoRepositoryTests {
|
||||
List<Person> result = repository.findAll();
|
||||
assertThat(result, hasSize(all.size()));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void findOneFromCustomCollectionName() {
|
||||
Person result = repository.findOne(dave.getId());
|
||||
@@ -94,6 +100,74 @@ public class SimpleMongoRepositoryTests {
|
||||
assertThat(result, not(hasItem(dave)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1054
|
||||
*/
|
||||
@Test
|
||||
public void shouldInsertSingle() {
|
||||
|
||||
String randomId = UUID.randomUUID().toString();
|
||||
|
||||
Person person1 = new Person("First1" + randomId, "Last2" + randomId, 42);
|
||||
person1 = repository.insert(person1);
|
||||
|
||||
Person saved = repository.findOne(person1.getId());
|
||||
|
||||
assertThat(saved, is(equalTo(person1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1054
|
||||
*/
|
||||
@Test
|
||||
public void shouldInsertMutlipleFromList() {
|
||||
|
||||
String randomId = UUID.randomUUID().toString();
|
||||
Map<String, Person> idToPerson = new HashMap<String, Person>();
|
||||
List<Person> persons = new ArrayList<Person>();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Person person = new Person("First" + i + randomId, "Last" + randomId + i, 42 + i);
|
||||
idToPerson.put(person.getId(), person);
|
||||
persons.add(person);
|
||||
}
|
||||
|
||||
List<Person> saved = repository.insert(persons);
|
||||
|
||||
assertThat(saved, hasSize(persons.size()));
|
||||
assertThatAllReferencePersonsWereStoredCorrectly(idToPerson, saved);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1054
|
||||
*/
|
||||
@Test
|
||||
public void shouldInsertMutlipleFromSet() {
|
||||
|
||||
String randomId = UUID.randomUUID().toString();
|
||||
Map<String, Person> idToPerson = new HashMap<String, Person>();
|
||||
Set<Person> persons = new HashSet<Person>();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Person person = new Person("First" + i + randomId, "Last" + i + randomId, 42 + i);
|
||||
idToPerson.put(person.getId(), person);
|
||||
persons.add(person);
|
||||
}
|
||||
|
||||
List<Person> saved = repository.insert(persons);
|
||||
|
||||
assertThat(saved, hasSize(persons.size()));
|
||||
assertThatAllReferencePersonsWereStoredCorrectly(idToPerson, saved);
|
||||
}
|
||||
|
||||
private void assertThatAllReferencePersonsWereStoredCorrectly(Map<String, Person> references, List<Person> saved) {
|
||||
|
||||
for (Person person : saved) {
|
||||
Person reference = references.get(person.getId());
|
||||
assertThat(person, is(equalTo(reference)));
|
||||
}
|
||||
}
|
||||
|
||||
private static class CustomizedPersonInformation implements MongoEntityInformation<Person, String> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* 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.test.util;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* {@link CleanMongoDB} is a junit {@link TestRule} implementation to be used as for wiping data from MongoDB instance.
|
||||
* MongoDB specific system databases like {@literal admin} and {@literal local} remain untouched. The rule will apply
|
||||
* <strong>after</strong> the base {@link Statement}. <br />
|
||||
* Use as {@link org.junit.ClassRule} to wipe data after finishing all tests within a class or as {@link org.junit.Rule}
|
||||
* to do so after each {@link org.junit.Test}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
public class CleanMongoDB implements TestRule {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CleanMongoDB.class);
|
||||
|
||||
/**
|
||||
* Defines contents of MongoDB.
|
||||
*/
|
||||
public enum Struct {
|
||||
DATABASE, COLLECTION, INDEX;
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")//
|
||||
private Set<String> preserveDatabases = new HashSet<String>() {
|
||||
{
|
||||
add("admin");
|
||||
add("local");
|
||||
}
|
||||
};
|
||||
|
||||
private Set<String> dbNames = new HashSet<String>();
|
||||
private Set<String> collectionNames = new HashSet<String>();
|
||||
private Set<Struct> types = new HashSet<CleanMongoDB.Struct>();
|
||||
private MongoClient client;
|
||||
|
||||
/**
|
||||
* Create new instance using an internal {@link MongoClient}.
|
||||
*/
|
||||
public CleanMongoDB() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new instance using an internal {@link MongoClient} connecting to specified instance running at host:port.
|
||||
*
|
||||
* @param host
|
||||
* @param port
|
||||
* @throws UnknownHostException
|
||||
*/
|
||||
public CleanMongoDB(String host, int port) throws UnknownHostException {
|
||||
this(new MongoClient(host, port));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new instance using the given client.
|
||||
*
|
||||
* @param client
|
||||
*/
|
||||
public CleanMongoDB(MongoClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes everything by dropping every single {@link DB}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static CleanMongoDB everything() {
|
||||
|
||||
CleanMongoDB cleanMongoDB = new CleanMongoDB();
|
||||
cleanMongoDB.clean(Struct.DATABASE);
|
||||
return cleanMongoDB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes everything from the databases with given name by dropping the according {@link DB}.
|
||||
*
|
||||
* @param dbNames
|
||||
* @return
|
||||
*/
|
||||
public static CleanMongoDB databases(String... dbNames) {
|
||||
|
||||
CleanMongoDB cleanMongoDB = new CleanMongoDB();
|
||||
cleanMongoDB.clean(Struct.DATABASE);
|
||||
cleanMongoDB.useDatabases(dbNames);
|
||||
return cleanMongoDB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the {@link DBCollection} with given names from every single {@link DB} containing them.
|
||||
*
|
||||
* @param collectionNames
|
||||
* @return
|
||||
*/
|
||||
public static CleanMongoDB collections(String... collectionNames) {
|
||||
return collections("", Arrays.asList(collectionNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the {@link DBCollection} with given names from the named {@link DB}.
|
||||
*
|
||||
* @param dbName
|
||||
* @param collectionNames
|
||||
* @return
|
||||
*/
|
||||
public static CleanMongoDB collections(String dbName, Collection<String> collectionNames) {
|
||||
|
||||
CleanMongoDB cleanMongoDB = new CleanMongoDB();
|
||||
cleanMongoDB.clean(Struct.COLLECTION);
|
||||
cleanMongoDB.useCollections(dbName, collectionNames);
|
||||
return cleanMongoDB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops all index structures from every single {@link DBCollection}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static CleanMongoDB indexes() {
|
||||
return indexes(Collections.<String> emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops all index structures from every single {@link DBCollection}.
|
||||
*
|
||||
* @param collectionNames
|
||||
* @return
|
||||
*/
|
||||
public static CleanMongoDB indexes(Collection<String> collectionNames) {
|
||||
|
||||
CleanMongoDB cleanMongoDB = new CleanMongoDB();
|
||||
cleanMongoDB.clean(Struct.INDEX);
|
||||
cleanMongoDB.useCollections(collectionNames);
|
||||
return cleanMongoDB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define {@link Struct} to be cleaned.
|
||||
*
|
||||
* @param types
|
||||
* @return
|
||||
*/
|
||||
public CleanMongoDB clean(Struct... types) {
|
||||
|
||||
this.types.addAll(Arrays.asList(types));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the {@link DB}s to be used. <br />
|
||||
* Impact along with {@link CleanMongoDB#clean(Struct...)}:
|
||||
* <ul>
|
||||
* <li>{@link Struct#DATABASE}: Forces drop of named databases.</li>
|
||||
* <li>{@link Struct#COLLECTION}: Forces drop of collections within named databases.</li>
|
||||
* <li>{@link Struct#INDEX}: Removes index within collections of named databases.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param dbNames
|
||||
* @return
|
||||
*/
|
||||
public CleanMongoDB useDatabases(String... dbNames) {
|
||||
|
||||
this.dbNames.addAll(Arrays.asList(dbNames));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes the given {@link DB}s from being processed.
|
||||
*
|
||||
* @param dbNames
|
||||
* @return
|
||||
*/
|
||||
public CleanMongoDB preserveDatabases(String... dbNames) {
|
||||
this.preserveDatabases.addAll(Arrays.asList(dbNames));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the {@link DBCollection}s to be used. <br />
|
||||
* Impact along with {@link CleanMongoDB#clean(Struct...)}:
|
||||
* <ul>
|
||||
* <li>{@link Struct#COLLECTION}: Forces drop of named collections.</li>
|
||||
* <li>{@link Struct#INDEX}: Removes index within named collections.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param collectionNames
|
||||
* @return
|
||||
*/
|
||||
public CleanMongoDB useCollections(String... collectionNames) {
|
||||
return useCollections(Arrays.asList(collectionNames));
|
||||
}
|
||||
|
||||
private CleanMongoDB useCollections(Collection<String> collectionNames) {
|
||||
return useCollections("", collectionNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the {@link DBCollection}s and {@link DB} to be used. <br />
|
||||
* Impact along with {@link CleanMongoDB#clean(Struct...)}:
|
||||
* <ul>
|
||||
* <li>{@link Struct#COLLECTION}: Forces drop of named collections in given db.</li>
|
||||
* <li>{@link Struct#INDEX}: Removes index within named collections in given db.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param collectionNames
|
||||
* @return
|
||||
*/
|
||||
public CleanMongoDB useCollections(String db, Collection<String> collectionNames) {
|
||||
|
||||
if (StringUtils.hasText(db)) {
|
||||
this.dbNames.add(db);
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(collectionNames)) {
|
||||
this.collectionNames.addAll(collectionNames);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Statement apply() {
|
||||
return apply(null, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.junit.rules.TestRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description)
|
||||
*/
|
||||
public Statement apply(Statement base, Description description) {
|
||||
return new MongoCleanStatement(base);
|
||||
}
|
||||
|
||||
private void doClean() {
|
||||
|
||||
Collection<String> dbNamesToUse = initDbNames();
|
||||
|
||||
for (String dbName : dbNamesToUse) {
|
||||
|
||||
if (isPreserved(dbName) || dropDbIfRequired(dbName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DB db = client.getDB(dbName);
|
||||
dropCollectionsOrIndexIfRequried(db, initCollectionNames(db));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean dropDbIfRequired(String dbName) {
|
||||
|
||||
if (!types.contains(Struct.DATABASE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
client.dropDatabase(dbName);
|
||||
LOGGER.debug("Dropping DB '{}'. ", dbName);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void dropCollectionsOrIndexIfRequried(DB db, Collection<String> collectionsToUse) {
|
||||
|
||||
for (String collectionName : collectionsToUse) {
|
||||
|
||||
if (db.collectionExists(collectionName)) {
|
||||
|
||||
DBCollection collection = db.getCollectionFromString(collectionName);
|
||||
if (collection != null) {
|
||||
|
||||
if (types.contains(Struct.COLLECTION)) {
|
||||
collection.drop();
|
||||
LOGGER.debug("Dropping collection '{}' for DB '{}'. ", collectionName, db.getName());
|
||||
} else if (types.contains(Struct.INDEX)) {
|
||||
collection.dropIndexes();
|
||||
LOGGER.debug("Dropping indexes in collection '{}' for DB '{}'. ", collectionName, db.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPreserved(String dbName) {
|
||||
return preserveDatabases.contains(dbName.toLowerCase());
|
||||
}
|
||||
|
||||
private Collection<String> initDbNames() {
|
||||
|
||||
Collection<String> dbNamesToUse = dbNames;
|
||||
if (dbNamesToUse.isEmpty()) {
|
||||
dbNamesToUse = client.getDatabaseNames();
|
||||
}
|
||||
return dbNamesToUse;
|
||||
}
|
||||
|
||||
private Collection<String> initCollectionNames(DB db) {
|
||||
|
||||
Collection<String> collectionsToUse = collectionNames;
|
||||
if (CollectionUtils.isEmpty(collectionsToUse)) {
|
||||
collectionsToUse = db.getCollectionNames();
|
||||
}
|
||||
return collectionsToUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
private class MongoCleanStatement extends Statement {
|
||||
|
||||
private final Statement base;
|
||||
|
||||
public MongoCleanStatement(Statement base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
|
||||
if (base != null) {
|
||||
base.evaluate();
|
||||
}
|
||||
|
||||
boolean isInternal = false;
|
||||
if (client == null) {
|
||||
client = new MongoClient();
|
||||
isInternal = true;
|
||||
}
|
||||
|
||||
doClean();
|
||||
|
||||
if (isInternal) {
|
||||
client.close();
|
||||
client = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.test.util;
|
||||
|
||||
import org.junit.runner.Result;
|
||||
import org.junit.runner.notification.RunListener;
|
||||
import org.springframework.data.mongodb.test.util.CleanMongoDB.Struct;
|
||||
|
||||
/**
|
||||
* {@link RunListener} implementation to be used for wiping MongoDB index structures after all test runs have finished.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.6
|
||||
*/
|
||||
public class CleanMongoDBJunitRunListener extends RunListener {
|
||||
|
||||
@Override
|
||||
public void testRunFinished(Result result) throws Exception {
|
||||
|
||||
super.testRunFinished(result);
|
||||
try {
|
||||
new CleanMongoDB().clean(Struct.INDEX).apply().evaluate();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.test.util;
|
||||
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mongodb.test.util.CleanMongoDB.Struct;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.MongoClient;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CleanMongoDBTests {
|
||||
|
||||
private CleanMongoDB cleaner;
|
||||
|
||||
// JUnit internals
|
||||
private @Mock Statement baseStatementMock;
|
||||
private @Mock Description descriptionMock;
|
||||
|
||||
// MongoClient in use
|
||||
private @Mock MongoClient mongoClientMock;
|
||||
|
||||
// Some Mock DBs
|
||||
private @Mock DB db1mock, db2mock;
|
||||
private @Mock DBCollection db1collection1mock, db1collection2mock, db2collection1mock;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
// DB setup
|
||||
when(mongoClientMock.getDatabaseNames()).thenReturn(Arrays.asList("admin", "db1", "db2"));
|
||||
when(mongoClientMock.getDB(eq("db1"))).thenReturn(db1mock);
|
||||
when(mongoClientMock.getDB(eq("db2"))).thenReturn(db2mock);
|
||||
|
||||
// collections have to exist
|
||||
when(db1mock.collectionExists(anyString())).thenReturn(true);
|
||||
when(db2mock.collectionExists(anyString())).thenReturn(true);
|
||||
|
||||
// init collection names per database
|
||||
when(db1mock.getCollectionNames()).thenReturn(new HashSet<String>() {
|
||||
{
|
||||
add("db1collection1");
|
||||
add("db1collection2");
|
||||
}
|
||||
});
|
||||
when(db2mock.getCollectionNames()).thenReturn(Collections.singleton("db2collection1"));
|
||||
|
||||
// return collections according to names
|
||||
when(db1mock.getCollectionFromString(eq("db1collection1"))).thenReturn(db1collection1mock);
|
||||
when(db1mock.getCollectionFromString(eq("db1collection2"))).thenReturn(db1collection2mock);
|
||||
when(db2mock.getCollectionFromString(eq("db2collection1"))).thenReturn(db2collection1mock);
|
||||
|
||||
cleaner = new CleanMongoDB(mongoClientMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preservesSystemDBsCorrectlyWhenCleaningDatabase() throws Throwable {
|
||||
|
||||
cleaner.clean(Struct.DATABASE);
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("admin"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preservesNamedDBsCorrectlyWhenCleaningDatabase() throws Throwable {
|
||||
|
||||
cleaner.clean(Struct.DATABASE);
|
||||
cleaner.preserveDatabases("db1");
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("db1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dropsAllDBsCorrectlyWhenCleaingDatabaseAndNotExplictDBNamePresent() throws Throwable {
|
||||
|
||||
cleaner.clean(Struct.DATABASE);
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(mongoClientMock, times(1)).dropDatabase(eq("db1"));
|
||||
verify(mongoClientMock, times(1)).dropDatabase(eq("db2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dropsSpecifiedDBsCorrectlyWhenExplicitNameSet() throws Throwable {
|
||||
|
||||
cleaner.clean(Struct.DATABASE);
|
||||
cleaner.useDatabases("db2");
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(mongoClientMock, times(1)).dropDatabase(eq("db2"));
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("db1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotRemoveAnyDBwhenCleaningCollections() throws Throwable {
|
||||
|
||||
cleaner.clean(Struct.COLLECTION);
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("db1"));
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("db2"));
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("admin"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotDropCollectionsFromPreservedDBs() throws Throwable {
|
||||
|
||||
cleaner.clean(Struct.COLLECTION);
|
||||
cleaner.preserveDatabases("db1");
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(db1collection1mock, never()).drop();
|
||||
verify(db1collection2mock, never()).drop();
|
||||
verify(db2collection1mock, times(1)).drop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removesAllCollectionsFromAllDatabasesWhenNotLimitedToSpecificOnes() throws Throwable {
|
||||
|
||||
cleaner.clean(Struct.COLLECTION);
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(db1collection1mock, times(1)).drop();
|
||||
verify(db1collection2mock, times(1)).drop();
|
||||
verify(db2collection1mock, times(1)).drop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removesOnlyNamedCollectionsWhenSpecified() throws Throwable {
|
||||
|
||||
cleaner.clean(Struct.COLLECTION);
|
||||
cleaner.useCollections("db1collection2");
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(db1collection1mock, never()).drop();
|
||||
verify(db2collection1mock, never()).drop();
|
||||
verify(db1collection2mock, times(1)).drop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removesIndexesCorrectly() throws Throwable {
|
||||
|
||||
cleaner.clean(Struct.INDEX);
|
||||
|
||||
cleaner.apply(baseStatementMock, descriptionMock).evaluate();
|
||||
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("db1"));
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("db2"));
|
||||
verify(mongoClientMock, never()).dropDatabase(eq("admin"));
|
||||
|
||||
verify(db1collection1mock, times(1)).dropIndexes();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,27 @@
|
||||
= Spring Data MongoDB - Reference Documentation
|
||||
Mark Pollack, Thomas Risberg, Oliver Gierke, Costin Leau, Jon Brisbin, Thomas Darimont, Christoph Strobl
|
||||
Mark Pollack; Thomas Risberg; Oliver Gierke; Costin Leau; Jon Brisbin; Thomas Darimont; Christoph Strobl
|
||||
:revnumber: {version}
|
||||
:revdate: {localdate}
|
||||
:toc:
|
||||
:toc-placement!:
|
||||
:spring-data-commons-docs: https://raw.githubusercontent.com/spring-projects/spring-data-commons/master/src/main/asciidoc
|
||||
|
||||
{version}
|
||||
|
||||
© 2008-2014 The original authors.
|
||||
(C) 2008-2014 The original authors.
|
||||
|
||||
NOTE: _Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically._
|
||||
|
||||
toc::[]
|
||||
|
||||
include::preface.adoc[]
|
||||
include::introduction/introduction.adoc[]
|
||||
|
||||
:leveloffset: 1
|
||||
include::introduction/requirements.adoc[]
|
||||
include::introduction/getting-started.adoc[]
|
||||
:leveloffset: +1
|
||||
include::{spring-data-commons-docs}/repositories.adoc[]
|
||||
:leveloffset: -1
|
||||
|
||||
:leveloffset: 0
|
||||
[[reference]]
|
||||
= Reference Documentation
|
||||
|
||||
:leveloffset: 1
|
||||
:leveloffset: +1
|
||||
include::reference/introduction.adoc[]
|
||||
include::reference/mongodb.adoc[]
|
||||
include::reference/mongo-repositories.adoc[]
|
||||
@@ -30,12 +30,14 @@ include::reference/mapping.adoc[]
|
||||
include::reference/cross-store.adoc[]
|
||||
include::reference/logging.adoc[]
|
||||
include::reference/jmx.adoc[]
|
||||
:leveloffset: -1
|
||||
|
||||
:leveloffset: 0
|
||||
[[appendix]]
|
||||
= Appendix
|
||||
|
||||
:numbered!:
|
||||
:leveloffset: 1
|
||||
include::{spring-data-commons-docs}/repository-namespace-reference.adoc[]
|
||||
:leveloffset: +1
|
||||
include::{spring-data-commons-docs}/repository-namespace-reference.adoc[]
|
||||
include::{spring-data-commons-docs}/repository-populator-namespace-reference.adoc[]
|
||||
include::{spring-data-commons-docs}/repository-query-keywords-reference.adoc[]
|
||||
:leveloffset: -1
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
[[get-started]]
|
||||
= Additional Help Resources
|
||||
|
||||
Learning a new framework is not always straight forward. In this section, we try to provide what we think is an easy to follow guide for starting with Spring Data Document module. However, if you encounter issues or you are just looking for an advice, feel free to use one of the links below:
|
||||
|
||||
[[get-started:help]]
|
||||
== Support
|
||||
|
||||
There are a few support options available:
|
||||
|
||||
[[get-started:help:community]]
|
||||
=== Community Forum
|
||||
|
||||
Spring Data on Stackoverflow http://stackoverflow.com/questions/tagged/spring-data[Stackoverflow ] is a tag for all Spring Data (not just Document) users to share information and help each other. Note that registration is needed *only* for posting.
|
||||
|
||||
[[get-started:help:professional]]
|
||||
=== Professional Support
|
||||
|
||||
Professional, from-the-source support, with guaranteed response time, is available from http://gopivotal.com/[Pivotal Sofware, Inc.], the company behind Spring Data and Spring.
|
||||
|
||||
[[get-started:up-to-date]]
|
||||
== Following Development
|
||||
|
||||
For information on the Spring Data Mongo source code repository, nightly builds and snapshot artifacts please see the http://projects.spring.io/spring-data-mongodb/[Spring Data Mongo homepage]. You can help make Spring Data best serve the needs of the Spring community by interacting with developers through the Community on http://stackoverflow.com/questions/tagged/spring-data[Stackoverflow]. To follow developer activity look for the mailing list information on the Spring Data Mongo homepage. If you encounter a bug or want to suggest an improvement, please create a ticket on the Spring Data issue https://jira.spring.io/browse/DATAMONGO[tracker]. To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community http://spring.io[Portal]. Lastly, you can follow the SpringSource Data http://spring.io/blog[blog ]or the project team on Twitter (http://twitter.com/SpringData[SpringData]).
|
||||
@@ -1,26 +0,0 @@
|
||||
[[introduction]]
|
||||
= Introduction
|
||||
|
||||
This document is the reference guide for Spring Data - Document Support. It explains Document module concepts and semantics and the syntax for various stores namespaces.
|
||||
|
||||
This section provides some basic introduction to Spring and Document database. The rest of the document refers only to Spring Data Document features and assumes the user is familiar with document databases such as MongoDB and CouchDB as well as Spring concepts.
|
||||
|
||||
[[get-started:first-steps:spring]]
|
||||
== Knowing Spring
|
||||
Spring Data uses Spring framework's http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/spring-core.html[core] functionality, such as the http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html[IoC] container, http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/validation.html#core-convert[type conversion system], http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/expressions.html[expression language], http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/jmx.html[JMX integration], and portable http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/dao.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar for whatever IoC container you choose to use.
|
||||
|
||||
The core functionality of the MongoDB and CouchDB support can be used directly, with no need to invoke the IoC services of the Spring Container. This is much like `JdbcTemplate` which can be used 'standalone' without any other services of the Spring container. To leverage all the features of Spring Data document, such as the repository support, you will need to configure some parts of the library using Spring.
|
||||
|
||||
To learn more about Spring, you can refer to the comprehensive (and sometimes disarming) documentation that explains in detail the Spring Framework. There are a lot of articles, blog entries and books on the matter - take a look at the Spring framework http://spring.io/docs[home page ] for more information.
|
||||
|
||||
[[get-started:first-steps:nosql]]
|
||||
== Knowing NoSQL and Document databases
|
||||
NoSQL stores have taken the storage world by storm. It is a vast domain with a plethora of solutions, terms and patterns (to make things worth even the term itself has multiple http://www.google.com/search?q=nosoql+acronym[meanings]). While some of the principles are common, it is crucial that the user is familiar to some degree with the stores supported by DATADOC. The best way to get acquainted to this solutions is to read their documentation and follow their examples - it usually doesn't take more then 5-10 minutes to go through them and if you are coming from an RDMBS-only background many times these exercises can be an eye opener.
|
||||
|
||||
The jumping off ground for learning about MongoDB is http://www.mongodb.org/[www.mongodb.org]. Here is a list of other useful resources:
|
||||
|
||||
* The http://docs.mongodb.org/manual/[manual] introduces MongoDB and contains links to getting started guides, reference documentation and tutorials.
|
||||
* The http://try.mongodb.org/[online shell] provides a convenient way to interact with a MongoDB instance in combination with the online http://docs.mongodb.org/manual/tutorial/getting-started/[tutorial.]
|
||||
* MongoDB http://docs.mongodb.org/ecosystem/drivers/java/[Java Language Center]
|
||||
* Several http://www.mongodb.org/books[books] available for purchase
|
||||
* Karl Seguin's online book: http://openmymind.net/mongodb.pdf[The Little MongoDB Book]
|
||||
@@ -1,6 +0,0 @@
|
||||
[[requirements]]
|
||||
= Requirements
|
||||
|
||||
Spring Data MongoDB 1.x binaries requires JDK level 6.0 and above, and http://spring.io/docs[Spring Framework] 3.2.x and above.
|
||||
|
||||
In terms of document stores, http://www.mongodb.org/[MongoDB] preferably version 2.4.
|
||||
@@ -1,5 +1,59 @@
|
||||
[[preface]]
|
||||
[preface]
|
||||
= Preface
|
||||
|
||||
The Spring Data MongoDB project applies core Spring concepts to the development of solutions using the MongoDB document style data store. We provide a "template" as a high-level abstraction for storing and querying documents. You will notice similarities to the JDBC support in the Spring Framework.
|
||||
|
||||
This document is the reference guide for Spring Data - Document Support. It explains Document module concepts and semantics and the syntax for various stores namespaces.
|
||||
|
||||
This section provides some basic introduction to Spring and Document database. The rest of the document refers only to Spring Data Document features and assumes the user is familiar with document databases such as MongoDB and CouchDB as well as Spring concepts.
|
||||
|
||||
[[get-started:first-steps:spring]]
|
||||
== Knowing Spring
|
||||
Spring Data uses Spring framework's http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/spring-core.html[core] functionality, such as the http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html[IoC] container, http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/validation.html#core-convert[type conversion system], http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/expressions.html[expression language], http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/jmx.html[JMX integration], and portable http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/dao.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar for whatever IoC container you choose to use.
|
||||
|
||||
The core functionality of the MongoDB and CouchDB support can be used directly, with no need to invoke the IoC services of the Spring Container. This is much like `JdbcTemplate` which can be used 'standalone' without any other services of the Spring container. To leverage all the features of Spring Data document, such as the repository support, you will need to configure some parts of the library using Spring.
|
||||
|
||||
To learn more about Spring, you can refer to the comprehensive (and sometimes disarming) documentation that explains in detail the Spring Framework. There are a lot of articles, blog entries and books on the matter - take a look at the Spring framework http://spring.io/docs[home page ] for more information.
|
||||
|
||||
[[get-started:first-steps:nosql]]
|
||||
== Knowing NoSQL and Document databases
|
||||
NoSQL stores have taken the storage world by storm. It is a vast domain with a plethora of solutions, terms and patterns (to make things worth even the term itself has multiple http://www.google.com/search?q=nosoql+acronym[meanings]). While some of the principles are common, it is crucial that the user is familiar to some degree with the stores supported by DATADOC. The best way to get acquainted to this solutions is to read their documentation and follow their examples - it usually doesn't take more then 5-10 minutes to go through them and if you are coming from an RDMBS-only background many times these exercises can be an eye opener.
|
||||
|
||||
The jumping off ground for learning about MongoDB is http://www.mongodb.org/[www.mongodb.org]. Here is a list of other useful resources:
|
||||
|
||||
* The http://docs.mongodb.org/manual/[manual] introduces MongoDB and contains links to getting started guides, reference documentation and tutorials.
|
||||
* The http://try.mongodb.org/[online shell] provides a convenient way to interact with a MongoDB instance in combination with the online http://docs.mongodb.org/manual/tutorial/getting-started/[tutorial.]
|
||||
* MongoDB http://docs.mongodb.org/ecosystem/drivers/java/[Java Language Center]
|
||||
* Several http://www.mongodb.org/books[books] available for purchase
|
||||
* Karl Seguin's online book: http://openmymind.net/mongodb.pdf[The Little MongoDB Book]
|
||||
|
||||
[[requirements]]
|
||||
== Requirements
|
||||
|
||||
Spring Data MongoDB 1.x binaries requires JDK level 6.0 and above, and http://spring.io/docs[Spring Framework] 3.2.x and above.
|
||||
|
||||
In terms of document stores, http://www.mongodb.org/[MongoDB] preferably version 2.4.
|
||||
|
||||
== Additional Help Resources
|
||||
|
||||
Learning a new framework is not always straight forward. In this section, we try to provide what we think is an easy to follow guide for starting with Spring Data Document module. However, if you encounter issues or you are just looking for an advice, feel free to use one of the links below:
|
||||
|
||||
[[get-started:help]]
|
||||
=== Support
|
||||
|
||||
There are a few support options available:
|
||||
|
||||
[[get-started:help:community]]
|
||||
==== Community Forum
|
||||
|
||||
Spring Data on Stackoverflow http://stackoverflow.com/questions/tagged/spring-data[Stackoverflow ] is a tag for all Spring Data (not just Document) users to share information and help each other. Note that registration is needed *only* for posting.
|
||||
|
||||
[[get-started:help:professional]]
|
||||
==== Professional Support
|
||||
|
||||
Professional, from-the-source support, with guaranteed response time, is available from http://gopivotal.com/[Pivotal Sofware, Inc.], the company behind Spring Data and Spring.
|
||||
|
||||
[[get-started:up-to-date]]
|
||||
=== Following Development
|
||||
|
||||
For information on the Spring Data Mongo source code repository, nightly builds and snapshot artifacts please see the http://projects.spring.io/spring-data-mongodb/[Spring Data Mongo homepage]. You can help make Spring Data best serve the needs of the Spring community by interacting with developers through the Community on http://stackoverflow.com/questions/tagged/spring-data[Stackoverflow]. To follow developer activity look for the mailing list information on the Spring Data Mongo homepage. If you encounter a bug or want to suggest an improvement, please create a ticket on the Spring Data issue https://jira.spring.io/browse/DATAMONGO[tracker]. To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community http://spring.io[Portal]. Lastly, you can follow the SpringSource Data http://spring.io/blog[blog ]or the project team on Twitter (http://twitter.com/SpringData[SpringData]).
|
||||
@@ -8,3 +8,4 @@ This part of the reference documentation explains the core functionality offered
|
||||
<<mongo.core>> introduces the MongoDB module feature set.
|
||||
|
||||
<<mongo.repositories>> introduces the repository support for MongoDB.
|
||||
|
||||
|
||||
@@ -1,6 +1,85 @@
|
||||
Spring Data MongoDB Changelog
|
||||
=============================
|
||||
|
||||
Changes in version 1.7.0.M1 (2014-12-01)
|
||||
----------------------------------------
|
||||
* DATAMONGO-1108 - BasicMongoPersistentEntity doesn't need to parse expression on every invocation.
|
||||
* DATAMONGO-1106 - Release 1.7 M1.
|
||||
* DATAMONGO-1105 - Add implementation for new QueryDslPredicateExecutor.findAll(OrderSpecifier<?>... orders).
|
||||
* DATAMONGO-1102 - Auto-register JSR-310 converters to support JDK 8 date/time types.
|
||||
* DATAMONGO-1101 - Add support for $bit to Update.
|
||||
* DATAMONGO-1100 - Adapt to new PersistentPropertyAccessor API.
|
||||
* DATAMONGO-1097 - Add support for $mul to Update.
|
||||
* DATAMONGO-1096 - RuntimeExceptions during debug query printing in MongoTemplate.
|
||||
* DATAMONGO-1094 - Wrong reference to @DocumentField in error message.
|
||||
* DATAMONGO-1093 - BasicQuery missing hashCode() and equals(…) methods.
|
||||
* DATAMONGO-1092 - Ensure compatibility with MongoDB 2.8.0.rc0 and java driver 2.13.0-rc0.
|
||||
* DATAMONGO-1087 - Incorrect warning for MongoPersistentEntityIndexResolver$CyclicPropertyReferenceException: Found cycle for field…
|
||||
* DATAMONGO-1085 - Sort can not use the metamodel classes generated by QueryDSL.
|
||||
* DATAMONGO-1080 - AbstractMongoQuery must not eagerly post-process results.
|
||||
* DATAMONGO-1078 - @Query annotated repository query fails to map complex Id structure.
|
||||
* DATAMONGO-1077 - Update removes positional operator $ in key when used on DBRef property.
|
||||
* DATAMONGO-1076 - Finalizer hit db on lazy dbrefs.
|
||||
* DATAMONGO-1075 - Correctly evaluate CONTAINS keyword on collection properties.
|
||||
* DATAMONGO-1072 - Query placeholders in keys no longer correctly substituted.
|
||||
* DATAMONGO-1070 - Query annotation with $oid leads to a parse error.
|
||||
* DATAMONGO-1068 - elemMatch of Class Criteria fails to build special cirteria.
|
||||
* DATAMONGO-1063 - IllegalStateException using any().in().
|
||||
* DATAMONGO-1062 - Fix failing test in ServerAddressPropertyEditorUnitTests.
|
||||
* DATAMONGO-1058 - Using @Field("foo") with @Dbref breaking behavior.
|
||||
* DATAMONGO-1057 - AbstractMongoQuery.SlicedExecution#execute() skips every nth element.
|
||||
* DATAMONGO-1054 - Improve performance of saving entities by using insert(…) if possible.
|
||||
* DATAMONGO-1053 - In 1.6, any field in a mapped object named "language" will fail to map if it is a type other than String.
|
||||
* DATAMONGO-1050 - SimpleMongoRepository.findById(id, class) don't return ids for nested documents.
|
||||
* DATAMONGO-1049 - Reserved field name 'language' causes trouble.
|
||||
* DATAMONGO-1043 - SpEL Expressions in @Document annotations are not re-evaluated for query executions.
|
||||
* DATAMONGO-943 - Add support for $position to Update $push $each.
|
||||
|
||||
|
||||
Changes in version 1.6.1.RELEASE (2014-10-30)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1080 - AbstractMongoQuery must not eagerly post-process results.
|
||||
* DATAMONGO-1079 - Release 1.6.1.
|
||||
* DATAMONGO-1077 - Update removes positional operator $ in key when used on DBRef property.
|
||||
* DATAMONGO-1076 - Finalizer hit db on lazy dbrefs.
|
||||
* DATAMONGO-1072 - Query placeholders in keys no longer correctly substituted.
|
||||
* DATAMONGO-1070 - Query annotation with $oid leads to a parse error.
|
||||
* DATAMONGO-1068 - elemMatch of Class Criteria fails to build special cirteria.
|
||||
* DATAMONGO-1063 - IllegalStateException using any().in().
|
||||
* DATAMONGO-1062 - Fix failing test in ServerAddressPropertyEditorUnitTests.
|
||||
* DATAMONGO-1058 - Using @Field("foo") with @Dbref breaking behavior.
|
||||
* DATAMONGO-1057 - AbstractMongoQuery.SlicedExecution#execute() skips every nth element.
|
||||
* DATAMONGO-1053 - In 1.6, any field in a mapped object named "language" will fail to map if it is a type other than String.
|
||||
* DATAMONGO-1049 - Reserved field name 'language' causes trouble.
|
||||
|
||||
|
||||
Changes in version 1.6.0.RELEASE (2014-09-05)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1046 - Release 1.6 GA.
|
||||
* DATAMONGO-1045 - Make sure Spring Data MongoDB can build against Spring 4.1.
|
||||
* DATAMONGO-1040 - deleteAll repository query don't use EntityMetadata collection name.
|
||||
* DATAMONGO-1039 - Polish implementation for cleaning up after tests.
|
||||
* DATAMONGO-1038 - Assert Mongo instances cleand up properly after test runs.
|
||||
* DATAMONGO-1036 - Custom repository implementations are not picked up when using CDI.
|
||||
* DATAMONGO-1034 - Improve error message when trying to convert incompatible types.
|
||||
* DATAMONGO-1032 - Polish Asciidoctor documentation.
|
||||
* DATAMONGO-1030 - Query methods retuning a single entity does not work with projecting types.
|
||||
* DATAMONGO-1027 - Collection inherits complex index from embedded class/object.
|
||||
* DATAMONGO-1025 - Duplicate index creation on embedded documents.
|
||||
|
||||
|
||||
Changes in version 1.5.4.RELEASE (2014-08-27)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-1038 - Assert Mongo instances cleand up properly after test runs.
|
||||
* DATAMONGO-1034 - Improve error message when trying to convert incompatible types.
|
||||
* DATAMONGO-1033 - Release 1.5.4.
|
||||
* DATAMONGO-1030 - Query methods retuning a single entity does not work with projecting types.
|
||||
* DATAMONGO-1027 - Collection inherits complex index from embedded class/object.
|
||||
* DATAMONGO-1025 - Duplicate index creation on embedded documents.
|
||||
* DATAMONGO-1020 - LimitOperator should be a public class.
|
||||
* DATAMONGO-1008 - IndexOperations fail, when "2dsphere" index is present.
|
||||
|
||||
|
||||
Changes in version 1.6.0.RC1 (2014-08-13)
|
||||
-----------------------------------------
|
||||
* DATAMONGO-1024 - Upgrade to Java driver 2.12.3.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data MongoDB 1.6 RC1
|
||||
Spring Data MongoDB 1.7 M1
|
||||
Copyright (c) [2010-2014] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
Reference in New Issue
Block a user