diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index a2213d637..eb3ddb955 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -44,44 +44,70 @@
${data.commons.version}
-
-
- org.slf4j
- slf4j-api
-
-
- org.slf4j
- jcl-over-slf4j
- compile
-
-
- org.slf4j
- slf4j-log4j12
- runtime
-
-
- log4j
- log4j
-
-
- javax.mail
- mail
-
-
- javax.jms
- jms
-
-
- com.sun.jdmk
- jmxtools
-
-
- com.sun.jmx
- jmxri
-
-
- runtime
-
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ jcl-over-slf4j
+ compile
+
+
+ org.slf4j
+ slf4j-log4j12
+ runtime
+
+
+ log4j
+ log4j
+
+
+ javax.mail
+ mail
+
+
+ javax.jms
+ jms
+
+
+ com.sun.jdmk
+ jmxtools
+
+
+ com.sun.jmx
+ jmxri
+
+
+ runtime
+
+
+
+ com.mysema.querydsl
+ querydsl-mongodb
+ 2.1.1
+ true
+
+
+ com.google.code.morphia
+ morphia
+
+
+
+
+
+ com.mysema.querydsl
+ querydsl-apt
+ 2.1.1
+ provided
+
+
+
+ javax.annotation
+ jsr250-api
+ true
+
javax.annotation
@@ -94,8 +120,9 @@
persistence-api
1.0
-
-
+
+
+
org.mockito
mockito-all
test
@@ -107,27 +134,49 @@
1.1
test
-
+
junit
junit
+ test
-
-
-
- org.mongodb
- mongo-java-driver
- 2.3
-
-
-
-
-
-
- com.springsource.bundlor
- com.springsource.bundlor.maven
-
-
-
+
+
+
+
+ com.springsource.bundlor
+ com.springsource.bundlor.maven
+
+
+
+ com.mysema.maven
+ maven-apt-plugin
+ 1.0
+
+
+ generate-test-sources
+
+ test-process
+
+
+ target/generated-sources/test-annotations
+ org.springframework.data.document.mongodb.repository.MongoAnnotationProcessor
+
+
+
+
+
+
+
+
+
+ querydsl
+ Mysema QueryDsl
+ http://source.mysema.com/maven2/releases
+
+ false
+
+
+
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoAnnotationProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoAnnotationProcessor.java
new file mode 100644
index 000000000..951c34217
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoAnnotationProcessor.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2011 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.document.mongodb.repository;
+
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+import com.mysema.query.annotations.QueryEmbeddable;
+import com.mysema.query.annotations.QueryEmbedded;
+import com.mysema.query.annotations.QueryEntities;
+import com.mysema.query.annotations.QueryEntity;
+import com.mysema.query.annotations.QuerySupertype;
+import com.mysema.query.annotations.QueryTransient;
+import com.mysema.query.apt.DefaultConfiguration;
+import com.mysema.query.apt.Processor;
+
+
+/**
+ * Annotation processor to create Querydsl query types for QueryDsl annoated
+ * classes
+ *
+ * @author Oliver Gierke
+ */
+@SupportedAnnotationTypes({ "com.mysema.query.annotations.*" })
+@SupportedSourceVersion(SourceVersion.RELEASE_6)
+public class MongoAnnotationProcessor extends AbstractProcessor {
+
+ private Class extends Annotation> entities, entity, embedded, skip;
+
+
+ @Override
+ public boolean process(Set extends TypeElement> annotations,
+ RoundEnvironment roundEnv) {
+
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
+ "Running " + getClass().getSimpleName());
+
+ DefaultConfiguration configuration =
+ new DefaultConfiguration(roundEnv, processingEnv.getOptions(),
+ Collections. emptySet(), QueryEntities.class,
+ QueryEntity.class, QuerySupertype.class,
+ QueryEmbeddable.class, QueryEmbedded.class,
+ QueryTransient.class);
+
+ Processor processor =
+ new Processor(processingEnv, roundEnv, configuration);
+ processor.process();
+ return true;
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryMethod.java
index b92963033..09dc10959 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryMethod.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryMethod.java
@@ -25,7 +25,7 @@ import org.springframework.util.StringUtils;
/**
* TODO - Extract methods for {@link #getAnnotatedQuery()} into superclass as it is currently copied from Spring Data
* JPA
- *
+ *
* @author Oliver Gierke
*/
class MongoQueryMethod extends QueryMethod {
@@ -35,7 +35,7 @@ class MongoQueryMethod extends QueryMethod {
/**
* Creates a new {@link MongoQueryMethod} from the given {@link Method}.
- *
+ *
* @param method
*/
public MongoQueryMethod(Method method, Class> domainClass) {
@@ -44,10 +44,9 @@ class MongoQueryMethod extends QueryMethod {
this.entityInformation = new MongoEntityInformation(ClassUtils.getReturnedDomainClass(method));
}
-
/**
* Returns whether the method has an annotated query.
- *
+ *
* @return
*/
boolean hasAnnotatedQuery() {
@@ -55,9 +54,9 @@ class MongoQueryMethod extends QueryMethod {
}
/**
- * Returns the query string declared in a {@link Query} annotation or {@literal null} if neither the annotation
- * found nor the attribute was specified.
- *
+ * Returns the query string declared in a {@link Query} annotation or {@literal null} if neither the annotation found
+ * nor the attribute was specified.
+ *
* @return
*/
String getAnnotatedQuery() {
@@ -68,7 +67,7 @@ class MongoQueryMethod extends QueryMethod {
/**
* Returns the field specification to be used for the query.
- *
+ *
* @return
*/
String getFieldSpecification() {
@@ -77,7 +76,6 @@ class MongoQueryMethod extends QueryMethod {
return StringUtils.hasText(value) ? value : null;
}
-
/* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryMethod#getEntityMetadata()
*/
@@ -89,7 +87,7 @@ class MongoQueryMethod extends QueryMethod {
/**
* Returns the {@link Query} annotation that is applied to the method or {@code null} if none available.
- *
+ *
* @return
*/
private Query getQueryAnnotation() {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java
index 25267b9cf..fdbd55734 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java
@@ -40,17 +40,19 @@ import org.springframework.util.StringUtils;
/**
* {@link org.springframework.beans.factory.FactoryBean} to create {@link MongoRepository} instances.
- *
+ *
* @author Oliver Gierke
*/
-public class MongoRepositoryFactoryBean, S, ID extends Serializable> extends RepositoryFactoryBeanSupport {
+public class MongoRepositoryFactoryBean, S, ID extends Serializable> extends
+ RepositoryFactoryBeanSupport {
private MongoTemplate template;
/**
* Configures the {@link MongoTemplate} to be used.
- *
- * @param template the template to set
+ *
+ * @param template
+ * the template to set
*/
public void setTemplate(MongoTemplate template) {
@@ -58,10 +60,12 @@ public class MongoRepositoryFactoryBean, S, ID
}
/*
- * (non-Javadoc)
- *
- * @see org.springframework.data.repository.support.RepositoryFactoryBeanSupport #createRepositoryFactory()
- */
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.repository.support.RepositoryFactoryBeanSupport
+ * #createRepositoryFactory()
+ */
@Override
protected RepositoryFactorySupport createRepositoryFactory() {
@@ -71,10 +75,12 @@ public class MongoRepositoryFactoryBean, S, ID
}
/*
- * (non-Javadoc)
- *
- * @see org.springframework.data.repository.support.RepositoryFactoryBeanSupport #afterPropertiesSet()
- */
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.repository.support.RepositoryFactoryBeanSupport
+ * #afterPropertiesSet()
+ */
@Override
public void afterPropertiesSet() {
@@ -84,16 +90,19 @@ public class MongoRepositoryFactoryBean, S, ID
/**
* Repository to create {@link MongoRepository} instances.
- *
+ *
* @author Oliver Gierke
*/
public static class MongoRepositoryFactory extends RepositoryFactorySupport {
+ private static final boolean QUERY_DSL_PRESENT = org.springframework.util.ClassUtils.isPresent(
+ "com.mysema.query.types.Predicate", MongoRepositoryFactory.class.getClassLoader());
+
private final MongoTemplate template;
/**
* Creates a new {@link MongoRepositoryFactory} fwith the given {@link MongoTemplate}.
- *
+ *
* @param template
*/
public MongoRepositoryFactory(MongoTemplate template) {
@@ -101,17 +110,54 @@ public class MongoRepositoryFactoryBean, S, ID
this.template = template;
}
-
/*
- * (non-Javadoc)
- *
- * @see org.springframework.data.repository.support.RepositoryFactorySupport#getRepositoryBaseClass()
- */
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.repository.support.RepositoryFactorySupport
+ * #getRepositoryBaseClass()
+ */
@Override
protected Class> getRepositoryBaseClass(Class> repositoryInterface) {
- return SimpleMongoRepository.class;
+
+ return isQueryDslRepository(repositoryInterface) ? QueryDslMongoRepository.class : SimpleMongoRepository.class;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.repository.support.RepositoryFactorySupport
+ * #getTargetRepository
+ * (org.springframework.data.repository.support.RepositoryMetadata)
+ */
+ @Override
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected Object getTargetRepository(RepositoryMetadata metadata) {
+
+ Class> repositoryInterface = metadata.getRepositoryInterface();
+ MongoEntityInformation, Serializable> entityInformation = getEntityInformation(metadata.getDomainClass());
+
+ if (isQueryDslRepository(repositoryInterface)) {
+ return new QueryDslMongoRepository(entityInformation, template);
+ } else {
+ return new SimpleMongoRepository(entityInformation, template);
+ }
+ }
+
+ private static boolean isQueryDslRepository(Class> repositoryInterface) {
+
+ return QUERY_DSL_PRESENT && QueryDslPredicateExecutor.class.isAssignableFrom(repositoryInterface);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.repository.support.RepositoryFactorySupport
+ * #getQueryLookupStrategy
+ * (org.springframework.data.repository.query.QueryLookupStrategy.Key)
+ */
@Override
protected QueryLookupStrategy getQueryLookupStrategy(Key key) {
@@ -120,11 +166,18 @@ public class MongoRepositoryFactoryBean, S, ID
/**
* {@link QueryLookupStrategy} to create {@link PartTreeMongoQuery} instances.
- *
+ *
* @author Oliver Gierke
*/
private class MongoQueryLookupStrategy implements QueryLookupStrategy {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.repository.query.QueryLookupStrategy
+ * #resolveQuery(java.lang.reflect.Method, java.lang.Class)
+ */
public RepositoryQuery resolveQuery(Method method, Class> domainClass) {
MongoQueryMethod queryMethod = new MongoQueryMethod(method, domainClass);
@@ -138,11 +191,12 @@ public class MongoRepositoryFactoryBean, S, ID
}
/*
- * (non-Javadoc)
- *
- * @see org.springframework.data.repository.support.RepositoryFactorySupport#validate(java.lang.Class,
- * java.lang.Object)
- */
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.repository.support.RepositoryFactorySupport
+ * #validate(java.lang.Class, java.lang.Object)
+ */
@Override
protected void validate(RepositoryMetadata metadata, Object customImplementation) {
@@ -155,36 +209,24 @@ public class MongoRepositoryFactoryBean, S, ID
super.validate(metadata, customImplementation);
}
-
- /* (non-Javadoc)
- * @see org.springframework.data.repository.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
- */
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.repository.support.RepositoryFactorySupport
+ * #getEntityInformation(java.lang.Class)
+ */
@Override
- public MongoEntityInformation getEntityInformation(
- Class domainClass) {
+ public MongoEntityInformation getEntityInformation(Class domainClass) {
return new MongoEntityInformation(domainClass);
}
-
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.repository.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.support.RepositoryMetadata)
- */
- @Override
- @SuppressWarnings({"unchecked", "rawtypes"})
- protected Object getTargetRepository(RepositoryMetadata metadata) {
-
- MongoEntityInformation, ?> info = getEntityInformation(
- metadata.getDomainClass());
- return new SimpleMongoRepository(info, template);
- }
}
/**
* {@link QueryCreationListener} inspecting {@link PartTreeMongoQuery}s and creating an index for the properties it
* refers to.
- *
+ *
* @author Oliver Gierke
*/
private static class IndexEnsuringQueryCreationListener implements QueryCreationListener {
@@ -193,16 +235,18 @@ public class MongoRepositoryFactoryBean, S, ID
private final MongoOperations operations;
public IndexEnsuringQueryCreationListener(MongoOperations operations) {
+
this.operations = operations;
}
/*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.data.repository.support.QueryCreationListener#onCreation(org.springframework.data.repository
- * .query.RepositoryQuery)
- */
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.repository.support.QueryCreationListener
+ * #onCreation(org.springframework.data.repository
+ * .query.RepositoryQuery)
+ */
public void onCreation(PartTreeMongoQuery query) {
PartTree tree = query.getTree();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryDslMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryDslMongoRepository.java
new file mode 100644
index 000000000..98f023f8f
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/QueryDslMongoRepository.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2011 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.document.mongodb.repository;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+import org.apache.commons.collections15.Transformer;
+import org.springframework.data.document.mongodb.MongoOperations;
+import org.springframework.data.document.mongodb.MongoTemplate;
+import org.springframework.data.document.mongodb.convert.MongoConverter;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Order;
+import org.springframework.data.repository.support.EntityMetadata;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ReflectionUtils;
+
+import com.mongodb.DBObject;
+import com.mysema.query.mongodb.MongodbQuery;
+import com.mysema.query.mongodb.MongodbSerializer;
+import com.mysema.query.types.EntityPath;
+import com.mysema.query.types.Expression;
+import com.mysema.query.types.OrderSpecifier;
+import com.mysema.query.types.Predicate;
+import com.mysema.query.types.path.PathBuilder;
+
+
+/**
+ * Special QueryDsl based repository implementation that allows execution
+ * {@link Predicate}s in various forms. TODO: Extract {@link EntityPathResolver}
+ * into Spring Data Commons TODO: Refactor Spring Data JPA to use this common
+ * infrastructure
+ *
+ * @author Oliver Gierke
+ */
+public class QueryDslMongoRepository extends
+ SimpleMongoRepository implements QueryDslPredicateExecutor {
+
+ private final MongoConverterTransformer transformer;
+ private final MongodbSerializer serializer;
+ private final PathBuilder builder;
+
+
+ /**
+ * 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
+ */
+ public QueryDslMongoRepository(
+ MongoEntityInformation entityInformation, MongoTemplate template) {
+
+ this(entityInformation, template, SimpleEntityPathResolver.INSTANCE);
+ }
+
+
+ /**
+ * Creates a new {@link QueryDslMongoRepository} for the given domain class,
+ * {@link MongoTemplate} and {@link EntityPathResolver}.
+ *
+ * @param entityInformation
+ * @param template
+ * @param resolver
+ */
+ public QueryDslMongoRepository(
+ MongoEntityInformation entityInformation,
+ MongoTemplate template, EntityPathResolver resolver) {
+
+ super(entityInformation, template);
+ this.transformer = new MongoConverterTransformer(template.getConverter());
+ this.serializer = new MongodbSerializer();
+
+ EntityPath path = resolver.createPath(entityInformation.getJavaType());
+ this.builder = new PathBuilder(path.getType(), path.getMetadata());
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.document.mongodb.repository.QueryDslExecutor
+ * #findOne(com.mysema.query.types.Predicate)
+ */
+ public T findOne(Predicate predicate) {
+
+ return createQueryFor(predicate).uniqueResult();
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.document.mongodb.repository.QueryDslExecutor
+ * #findAll(com.mysema.query.types.Predicate)
+ */
+ public List findAll(Predicate predicate) {
+
+ return createQueryFor(predicate).list();
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.document.mongodb.repository.QueryDslExecutor
+ * #findAll(com.mysema.query.types.Predicate,
+ * com.mysema.query.types.OrderSpecifier>[])
+ */
+ public List findAll(Predicate predicate, OrderSpecifier>... orders) {
+
+ return createQueryFor(predicate).orderBy(orders).list();
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.document.mongodb.repository.QueryDslExecutor
+ * #findAll(com.mysema.query.types.Predicate,
+ * org.springframework.data.domain.Pageable)
+ */
+ public Page findAll(Predicate predicate, Pageable pageable) {
+
+ MongodbQuery countQuery = createQueryFor(predicate);
+ MongodbQuery query = createQueryFor(predicate);
+
+ return new PageImpl(applyPagination(query, pageable).list(),
+ pageable, countQuery.count());
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.data.document.mongodb.repository.QueryDslExecutor
+ * #count(com.mysema.query.types.Predicate)
+ */
+ public Long count(Predicate predicate) {
+
+ return createQueryFor(predicate).count();
+ }
+
+
+ /**
+ * Creates a {@link MongodbQuery} for the given {@link Predicate}.
+ *
+ * @param predicate
+ * @return
+ */
+ private MongodbQuery createQueryFor(Predicate predicate) {
+
+ MongodbQuery query = new MongoTemplateQuery(getMongoOperations());
+ return query.where(predicate);
+ }
+
+
+ /**
+ * Applies the given {@link Pageable} to the given {@link MongodbQuery}.
+ *
+ * @param query
+ * @param pageable
+ * @return
+ */
+ private MongodbQuery applyPagination(MongodbQuery query,
+ Pageable pageable) {
+
+ if (pageable == null) {
+ return query;
+ }
+
+ query =
+ query.offset(pageable.getOffset())
+ .limit(pageable.getPageSize());
+ return applySorting(query, pageable.getSort());
+ }
+
+
+ /**
+ * Applies the given {@link Sort} to the given {@link MongodbQuery}.
+ *
+ * @param query
+ * @param sort
+ * @return
+ */
+ private MongodbQuery applySorting(MongodbQuery query, Sort sort) {
+
+ if (sort == null) {
+ return query;
+ }
+
+ for (Order order : sort) {
+ query.orderBy(toOrder(order));
+ }
+
+ return query;
+ }
+
+
+ /**
+ * Transforms a plain {@link Order} into a QueryDsl specific
+ * {@link OrderSpecifier}.
+ *
+ * @param order
+ * @return
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private OrderSpecifier> toOrder(Order order) {
+
+ Expression