DATADOC-24 - Added support for using @Query on Mongo repository methods.

Adapted changes from Spring Data Commons. Created ConvertingParameterAccessor that uses a MongoWriter to already convert query method parameters on access. Added @Query annotation to allow defining JSON based queries on query methods using ? placeholders.
This commit is contained in:
Oliver Gierke
2011-02-16 20:17:19 +01:00
parent 53f71a068c
commit 8f851310cc
16 changed files with 727 additions and 195 deletions

View File

@@ -35,7 +35,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-core</artifactId>
<version>1.0.0.M3</version>
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
<!-- Logging -->

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-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.
@@ -24,10 +24,10 @@ import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.SimpleParameterAccessor;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.Assert;
import com.mongodb.DBCollection;
@@ -36,32 +36,30 @@ import com.mongodb.DBObject;
/**
* {@link RepositoryQuery} implementation for Mongo.
* Base class for {@link RepositoryQuery} implementations for Mongo.
*
* @author Oliver Gierke
*/
public class MongoQuery implements RepositoryQuery {
public abstract class AbstractMongoQuery implements RepositoryQuery {
private final QueryMethod method;
private final MongoTemplate template;
private final PartTree tree;
/**
* Creates a new {@link MongoQuery} from the given {@link QueryMethod} and
* Creates a new {@link AbstractMongoQuery} from the given {@link QueryMethod} and
* {@link MongoTemplate}.
*
* @param method
* @param template
*/
public MongoQuery(QueryMethod method, MongoTemplate template) {
public AbstractMongoQuery(QueryMethod method, MongoTemplate template) {
Assert.notNull(template);
Assert.notNull(method);
this.method = method;
this.template = template;
this.tree = new PartTree(method.getName(), method.getDomainClass());
}
@@ -74,22 +72,27 @@ public class MongoQuery implements RepositoryQuery {
*/
public Object execute(Object[] parameters) {
SimpleParameterAccessor accessor =
new SimpleParameterAccessor(method.getParameters(), parameters);
MongoQueryCreator creator =
new MongoQueryCreator(tree, accessor, template.getConverter());
Query query = creator.createQuery();
ParameterAccessor accessor =
new ParametersParameterAccessor(method.getParameters(), parameters);
Query query = createQuery(new ConvertingParameterAccessor(template.getConverter(), accessor));
if (method.isCollectionQuery()) {
return new CollectionExecution().execute(query);
} else if (method.isPageQuery()) {
return new PagedExecution(creator, accessor.getPageable())
.execute(query);
return new PagedExecution(accessor.getPageable()).execute(query);
} else {
return new SingleEntityExecution().execute(query);
}
}
/**
* Create a {@link Query} instance using the given {@link ParameterAccessor}
* @param accessor
* @param converter
* @return
*/
protected abstract Query createQuery(ConvertingParameterAccessor accessor);
private abstract class Execution {
@@ -133,7 +136,6 @@ public class MongoQuery implements RepositoryQuery {
class PagedExecution extends Execution {
private final Pageable pageable;
private final MongoQueryCreator creator;
/**
@@ -141,11 +143,9 @@ public class MongoQuery implements RepositoryQuery {
*
* @param pageable
*/
public PagedExecution(MongoQueryCreator creator, Pageable pageable) {
public PagedExecution(Pageable pageable) {
Assert.notNull(creator);
Assert.notNull(pageable);
this.creator = creator;
this.pageable = pageable;
}
@@ -161,10 +161,8 @@ public class MongoQuery implements RepositoryQuery {
@SuppressWarnings({ "rawtypes", "unchecked" })
Object execute(Query query) {
Query countQuery = creator.createQuery();
String collectionName = getCollectionName(method.getDomainClass());
int count =
getCollectionCursor(collectionName, countQuery.getQueryObject()).count();
int count = getCollectionCursor(collectionName, query.getQueryObject()).count();
List<?> result =
template.find(collectionName, applyPagination(query, pageable),

View File

@@ -0,0 +1,144 @@
/*
* 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.util.Iterator;
import org.springframework.data.document.mongodb.MongoWriter;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.ParameterAccessor;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Custom {@link ParameterAccessor} that uses a {@link MongoWriter} to serialize parameters into Mongo format.
*
* @author Oliver Gierke
*/
public class ConvertingParameterAccessor implements ParameterAccessor {
private final MongoWriter<Object> writer;
private final ParameterAccessor delegate;
/**
* Creates a new {@link ConvertingParameterAccessor} with the given {@link MongoWriter} and delegate.
*
* @param writer
*/
public ConvertingParameterAccessor(MongoWriter<Object> writer, ParameterAccessor delegate) {
this.writer = writer;
this.delegate = delegate;
}
/*
* (non-Javadoc)
*
* @see java.lang.Iterable#iterator()
*/
public Iterator<Object> iterator() {
return new ConvertingIterator(delegate.iterator());
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.query.ParameterAccessor#getPageable()
*/
public Pageable getPageable() {
return delegate.getPageable();
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.query.ParameterAccessor#getSort()
*/
public Sort getSort() {
return delegate.getSort();
}
/**
* Custom {@link Iterator} to convert items before returning them.
*
* @author Oliver Gierke
*/
private class ConvertingIterator implements Iterator<Object> {
private final Iterator<Object> delegate;
/**
* Creates a new {@link ConvertingIterator} for the given delegate.
*
* @param delegate
*/
public ConvertingIterator(Iterator<Object> delegate) {
this.delegate = delegate;
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return delegate.hasNext();
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#next()
*/
public Object next() {
DBObject result = new BasicDBObject();
writer.write(new ValueHolder(delegate.next()), result);
return result.get("value");
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#remove()
*/
public void remove() {
delegate.remove();
}
}
/**
* Simple value holder class to allow conversion and accessing the converted value in a deterministic way.
*
* @author Oliver Gierke
*/
private static class ValueHolder {
private Object value;
public ValueHolder(Object value) {
this.value = value;
}
@SuppressWarnings("unused")
public Object getValue() {
return value;
}
}
}

View File

@@ -18,25 +18,22 @@ package org.springframework.data.document.mongodb.repository;
import static org.springframework.data.document.mongodb.query.Criteria.*;
import java.util.Collections;
import java.util.Iterator;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.document.mongodb.MongoConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.document.mongodb.query.Criteria;
import org.springframework.data.document.mongodb.query.CriteriaDefinition;
import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.SimpleParameterAccessor;
import org.springframework.data.repository.query.SimpleParameterAccessor.BindableParameterIterator;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Custom query creator to create Mongo criterias.
@@ -45,36 +42,28 @@ import com.mongodb.DBObject;
*/
class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
private static final Log LOG = LogFactory.getLog(MongoQueryCreator.class);
private final MongoConverter converter;
private static final Logger LOG = LoggerFactory.getLog(MongoQueryCreator.class);
/**
* Creates a new {@link MongoQueryCreator} from the given {@link PartTree}
* and {@link SimpleParameterAccessor}.
* and {@link ParametersParameterAccessor}.
*
* @param tree
* @param accessor
*/
public MongoQueryCreator(PartTree tree,
SimpleParameterAccessor accessor, MongoConverter converter) {
public MongoQueryCreator(PartTree tree, ParameterAccessor accessor) {
super(tree, accessor);
this.converter = converter;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.document.mongodb.repository.AbstractQueryCreator
* #create(org.springframework.data.repository.query.parser.Part,
* org.springframework
* .data.repository.query.SimpleParameterAccessor.BindableParameterIterator)
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#create(org.springframework.data.repository.query.parser.Part, java.util.Iterator)
*/
@Override
protected Criteria create(Part part, BindableParameterIterator iterator) {
protected Criteria create(Part part, Iterator<Object> iterator) {
return from(part.getType(),
where(part.getProperty().toDotPath()), iterator);
@@ -83,16 +72,11 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.document.mongodb.repository.AbstractQueryCreator
* #handlePart(org.springframework.data.repository.query.parser.Part,
* org.springframework
* .data.repository.query.SimpleParameterAccessor.BindableParameterIterator)
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#and(org.springframework.data.repository.query.parser.Part, java.lang.Object, java.util.Iterator)
*/
@Override
protected Criteria and(Part part, Criteria base,
BindableParameterIterator iterator) {
Iterator<Object> iterator) {
return from(part.getType(), where(part.getProperty().toDotPath()),
iterator);
@@ -143,16 +127,16 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
* @return
*/
private Criteria from(Type type, Criteria criteria,
BindableParameterIterator parameters) {
Iterator<Object> parameters) {
switch (type) {
case GREATER_THAN:
return criteria.gt(getConvertedParameter(parameters));
return criteria.gt(parameters.next());
case LESS_THAN:
return criteria.lt(getConvertedParameter(parameters));
return criteria.lt(parameters.next());
case BETWEEN:
return criteria.gt(getConvertedParameter(parameters)).lt(
getConvertedParameter(parameters));
return criteria.gt(parameters.next()).lt(
parameters.next());
case IS_NOT_NULL:
return criteria.not().is(null);
case IS_NULL:
@@ -161,22 +145,15 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
String value = parameters.next().toString();
return criteria.is(toLikeRegex(value));
case SIMPLE_PROPERTY:
return criteria.is(getConvertedParameter(parameters));
return criteria.is(parameters.next());
case NEGATING_SIMPLE_PROPERTY:
return criteria.not().is(getConvertedParameter(parameters));
return criteria.not().is(parameters.next());
}
throw new IllegalArgumentException("Unsupported keyword!");
}
private Object getConvertedParameter(BindableParameterIterator parameters) {
DBObject result = new BasicDBObject();
converter.write(new ValueHolder(parameters.next()), result);
return result.get("value");
}
private Pattern toLikeRegex(String source) {
@@ -184,27 +161,5 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
return Pattern.compile(regex);
}
/**
* Simple value holder class to allow conversion and accessing the converted
* value in a deterministic way.
*
* @author Oliver Gierke
*/
private static class ValueHolder {
private Object value;
public ValueHolder(Object value) {
this.value = value;
}
@SuppressWarnings("unused")
public Object getValue() {
return value;
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.reflect.Method;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.repository.query.QueryMethod;
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 {
private final Method method;
/**
* Creates a new {@link MongoQueryMethod} from the given {@link Method}.
*
* @param method
*/
public MongoQueryMethod(Method method) {
super(method);
this.method = method;
}
boolean hasAnnotatedQuery() {
return getAnnotatedQuery() != null;
}
/**
* 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() {
String query = (String) AnnotationUtils.getValue(getQueryAnnotation());
return StringUtils.hasText(query) ? query : null;
}
/**
* Returns the {@link Query} annotation that is applied to the method or {@code null} if none available.
*
* @return
*/
private Query getQueryAnnotation() {
return method.getAnnotation(Query.class);
}
}

View File

@@ -23,7 +23,6 @@ import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.support.RepositoryFactoryBeanSupport;
import org.springframework.data.repository.support.RepositoryFactorySupport;
@@ -126,15 +125,21 @@ public class MongoRepositoryFactoryBean extends
}
/**
* {@link QueryLookupStrategy} to create {@link MongoQuery} instances.
* {@link QueryLookupStrategy} to create {@link PartTreeMongoQuery} instances.
*
* @author Oliver Gierke
*/
private class MongoQueryLookupStrategy implements QueryLookupStrategy {
public RepositoryQuery resolveQuery(Method method) {
return new MongoQuery(new QueryMethod(method), template);
MongoQueryMethod queryMethod = new MongoQueryMethod(method);
if (queryMethod.hasAnnotatedQuery()) {
return new StringBasedMongoQuery(queryMethod, template);
} else {
return new PartTreeMongoQuery(queryMethod, template);
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2002-2010 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 org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.parser.PartTree;
/**
* {@link RepositoryQuery} implementation for Mongo.
*
* @author Oliver Gierke
*/
public class PartTreeMongoQuery extends AbstractMongoQuery {
private final PartTree tree;
/**
* Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}.
*
* @param method
* @param template
*/
public PartTreeMongoQuery(QueryMethod method, MongoTemplate template) {
super(method, template);
this.tree = new PartTree(method.getName(), method.getDomainClass());
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.document.mongodb.repository.AbstractMongoQuery#createQuery(org.springframework.data.
* document.mongodb.repository.ConvertingParameterAccessor)
*/
@Override
protected Query createQuery(ConvertingParameterAccessor accessor) {
MongoQueryCreator creator = new MongoQueryCreator(tree, accessor);
return creator.createQuery();
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to declare finder queries directly on repository methods.
*
* @author Oliver Gierke
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Query {
String value() default "";
}

View File

@@ -122,7 +122,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> extends
*/
public boolean exists(ID id) {
return findById(id) == null;
return findById(id) != null;
}
@@ -157,10 +157,13 @@ public class SimpleMongoRepository<T, ID extends Serializable> extends
* org.springframework.data.repository.Repository#delete(java.lang.Object)
*/
public void delete(T entity) {
Object id = entityInformation.getId(entity);
ObjectId objectId = template.getConverter().convertObjectId(id);
Query query =
new Query(where(entityInformation.getFieldName()).is(
entityInformation.getId(entity)));
new Query(where("_id").is(
objectId));
template.remove(getCollectionName(getDomainClass()), query);
}

View File

@@ -0,0 +1,75 @@
/*
* 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.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.query.BasicQuery;
import org.springframework.data.document.mongodb.query.Query;
/**
* Query to use a plain JSON String to create the {@link Query} to actually execute.
*
* @author Oliver Gierke
*/
public class StringBasedMongoQuery extends AbstractMongoQuery {
private static final Pattern PLACEHOLDER = Pattern.compile("\\?");
private static final Logger LOG = LoggerFactory.getLogger(StringBasedMongoQuery.class);
private final String query;
/**
* Creates a new {@link StringBasedMongoQuery}.
*
* @param method
* @param template
*/
public StringBasedMongoQuery(MongoQueryMethod method, MongoTemplate template) {
super(method, template);
this.query = method.getAnnotatedQuery();
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.document.mongodb.repository.AbstractMongoQuery#createQuery(org.springframework.data.
* repository.query.SimpleParameterAccessor, org.springframework.data.document.mongodb.MongoConverter)
*/
@Override
protected Query createQuery(ConvertingParameterAccessor accessor) {
Matcher matcher = PLACEHOLDER.matcher(query);
Iterator<Object> iterator = accessor.iterator();
String result = null;
while (matcher.find()) {
String group = matcher.group();
result = query.replace(group, String.format("\"%s\"", iterator.next()));
}
Query query = new BasicQuery(result);
LOG.debug("Created query {}", query.getQueryObject());
return query;
}
}

View File

@@ -8,6 +8,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import org.bson.types.ObjectId;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -19,116 +20,146 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Base class for tests for {@link PersonRepository}.
*
*
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
public abstract class AbstractPersonRepositoryIntegrationTests {
@Autowired
protected PersonRepository repository;
Person dave, carter, boyd,stefan,leroi;
@Autowired
protected PersonRepository repository;
Person dave, carter, boyd, stefan, leroi;
@Before
public void setUp() {
repository.deleteAll();
dave = new Person("Dave", "Matthews", 42);
carter = new Person("Carter", "Beauford", 49);
boyd = new Person("Boyd", "Tinsley", 45);
stefan = new Person("Stefan", "Lessard", 34);
leroi = new Person("Leroi", "Moore", 41);
repository.save(Arrays.asList(dave, carter, boyd, stefan, leroi));
}
@Test
@Before
public void setUp() {
repository.deleteAll();
dave = new Person("Dave", "Matthews", 42);
carter = new Person("Carter", "Beauford", 49);
boyd = new Person("Boyd", "Tinsley", 45);
stefan = new Person("Stefan", "Lessard", 34);
leroi = new Person("Leroi", "Moore", 41);
repository.save(Arrays.asList(dave, carter, boyd, stefan, leroi));
}
@Test
public void existsWorksCorrectly() {
assertThat(repository.exists(dave.getId()), is(true));
assertThat(repository.exists(carter.getId()), is(true));
assertThat(repository.exists(boyd.getId()), is(true));
assertThat(repository.exists(stefan.getId()), is(true));
assertThat(repository.exists(leroi.getId()), is(true));
assertThat(repository.exists(new ObjectId().toString()), is(false));
}
@Test
public void findsPersonById() throws Exception {
assertThat(repository.findById(dave.getId()), is(dave));
assertThat(repository.findById(dave.getId()), is(dave));
}
@Test
public void findsPersonsByLastname() throws Exception {
List<Person> result = repository.findByLastname("Beauford");
assertThat(result.size(), is(1));
assertThat(result, hasItem(carter));
}
@Test
public void findsAllMusicians() throws Exception {
List<Person> result = repository.findAll();
assertThat(result, hasItems(dave, carter, boyd, stefan, leroi));
assertThat(result.size(), is(5));
}
@Test
public void deletesPersonCorrectly() throws Exception {
repository.delete(dave);
List<Person> result = repository.findAll();
assertThat(result.size(), is(4));
assertThat(result, not(hasItem(dave)));
}
@Test
public void findsPersonsByFirstnameLike() throws Exception {
List<Person> result = repository.findByFirstnameLike("Bo*");
assertThat(result.size(), is(1));
assertThat(result, hasItem(boyd));
}
@Test
public void findsPersonsByLastname() throws Exception {
@Test
public void findsPagedPersons() throws Exception {
Page<Person> result =
repository.findAll(new PageRequest(1, 2, Direction.ASC,
"lastname"));
assertThat(result.isFirstPage(), is(false));
assertThat(result.isLastPage(), is(false));
assertThat(result, hasItems(dave, leroi));
}
List<Person> result = repository.findByLastname("Beauford");
assertThat(result.size(), is(1));
assertThat(result, hasItem(carter));
}
@Test
public void finsPersonsByFirstname() {
List<Person> result = repository.findByThePersonsFirstname("Leroi");
assertThat(result.size(), is(1));
assertThat(result, hasItem(leroi));
}
@Test
public void executesPagedFinderCorrectly() throws Exception {
Page<Person> page =
repository.findByLastnameLike("*a*", new PageRequest(0, 2,
Direction.ASC, "lastname"));
assertThat(page.isFirstPage(), is(true));
assertThat(page.isLastPage(), is(false));
assertThat(page.getNumberOfElements(), is(2));
assertThat(page, hasItems(carter, stefan));
}
@Test
public void findsPersonsByFirstnameLike() throws Exception {
@Test
public void findsPersonInAgeRangeCorrectly() throws Exception {
List<Person> result = repository.findByFirstnameLike("Bo*");
assertThat(result.size(), is(1));
assertThat(result, hasItem(boyd));
}
List<Person> result = repository.findByAgeBetween(40, 45);
assertThat(result.size(), is(2));
assertThat(result, hasItems(dave, leroi));
}
@Test
@Test
public void findsPagedPersons() throws Exception {
Page<Person> result = repository.findAll(new PageRequest(1, 2, Direction.ASC, "lastname"));
assertThat(result.isFirstPage(), is(false));
assertThat(result.isLastPage(), is(false));
assertThat(result, hasItems(dave, leroi));
}
@Test
public void executesPagedFinderCorrectly() throws Exception {
Page<Person> page = repository.findByLastnameLike("*a*", new PageRequest(0, 2, Direction.ASC, "lastname"));
assertThat(page.isFirstPage(), is(true));
assertThat(page.isLastPage(), is(false));
assertThat(page.getNumberOfElements(), is(2));
assertThat(page, hasItems(carter, stefan));
}
@Test
public void findsPersonInAgeRangeCorrectly() throws Exception {
List<Person> result = repository.findByAgeBetween(40, 45);
assertThat(result.size(), is(2));
assertThat(result, hasItems(dave, leroi));
}
@Test
public void findsPersonByShippingAddressesCorrectly() throws Exception {
Address address = new Address("Foo Street 1", "C0123", "Bar");
dave.setShippingAddresses(new HashSet<Address>(asList(address)));
repository.save(dave);
assertThat(repository.findByShippingAddresses(address), is(dave));
Address address = new Address("Foo Street 1", "C0123", "Bar");
dave.setShippingAddresses(new HashSet<Address>(asList(address)));
repository.save(dave);
assertThat(repository.findByShippingAddresses(address), is(dave));
}
@Test
@Test
public void findsPersonByAddressCorrectly() throws Exception {
Address address = new Address("Foo Street 1", "C0123", "Bar");
dave.setAddress(address);
repository.save(dave);
List<Person> result = repository.findByAddress(address);
assertThat(result.size(), is(1));
Address address = new Address("Foo Street 1", "C0123", "Bar");
dave.setAddress(address);
repository.save(dave);
List<Person> result = repository.findByAddress(address);
assertThat(result.size(), is(1));
assertThat(result, hasItem(dave));
}
@Test
@Test
public void findsPeopleByZipCode() throws Exception {
Address address = new Address("Foo Street 1", "C0123", "Bar");
dave.setAddress(address);
repository.save(dave);
List<Person> result = repository.findByAddressZipCode(address.getZipCode());
assertThat(result.size(), is(1));
Address address = new Address("Foo Street 1", "C0123", "Bar");
dave.setAddress(address);
repository.save(dave);
List<Person> result = repository.findByAddressZipCode(address.getZipCode());
assertThat(result.size(), is(1));
assertThat(result, hasItem(dave));
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.document.mongodb.repository;
import static org.springframework.data.document.mongodb.repository.StubParameterAccessor.*;
import java.lang.reflect.Method;
import java.util.List;
@@ -25,8 +27,6 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.document.mongodb.MongoConverter;
import org.springframework.data.document.mongodb.Person;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.SimpleParameterAccessor;
import org.springframework.data.repository.query.parser.PartTree;
@@ -63,17 +63,13 @@ public class MongoQueryCreatorUnitTests {
PartTree tree = new PartTree("findByFirstName", Person.class);
MongoQueryCreator creator =
new MongoQueryCreator(tree, new SimpleParameterAccessor(
new Parameters(findByFirstname),
new Object[] { "Oliver" }), converter);
new MongoQueryCreator(tree, getAccessor(converter, "Oliver"));
creator.createQuery();
creator =
new MongoQueryCreator(new PartTree("findByFirstNameAndFriend",
Person.class), new SimpleParameterAccessor(
new Parameters(findByFirstnameAndFriend), new Object[] {
"Oliver", new Person() }), converter);
Person.class), getAccessor(converter, "Oliver", new Person()));
creator.createQuery();
}

View File

@@ -35,6 +35,16 @@ public interface PersonRepository extends MongoRepository<Person, String> {
* @return
*/
List<Person> findByLastname(String lastname);
/**
* Returns the {@link Person}s with the given firstname. Uses {@link Query} annotation to define the query to be
* executed.
*
* @param firstname
* @return
*/
@Query("{ 'firstname' : ? }")
List<Person> findByThePersonsFirstname(String firstname);
/**

View File

@@ -0,0 +1,70 @@
/*
* 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 static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.document.mongodb.MongoConverter;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.SimpleMongoConverter;
import org.springframework.data.document.mongodb.query.BasicQuery;
/**
* Unit tests for {@link StringBasedMongoQuery}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class StringBasedMongoQueryUnitTests {
@Mock
MongoTemplate template;
MongoConverter converter = new SimpleMongoConverter();
@Before
public void setUp() {
when(template.getConverter()).thenReturn(converter);
}
@Test
public void testname() throws Exception {
Method method = SampleRepository.class.getMethod("findByLastname", String.class);
MongoQueryMethod queryMethod = new MongoQueryMethod(method);
StringBasedMongoQuery mongoQuery = new StringBasedMongoQuery(queryMethod, template);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
org.springframework.data.document.mongodb.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.document.mongodb.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
}
private interface SampleRepository {
@Query("{ 'lastname' : ? }")
Person findByLastname(String lastname);
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.util.Arrays;
import java.util.Iterator;
import org.springframework.data.document.mongodb.MongoWriter;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.ParameterAccessor;
/**
* Simple {@link ParameterAccessor} that returns the given parameters unfiltered.
*
* @author Oliver Gierke
*/
class StubParameterAccessor implements ParameterAccessor {
/**
* Creates a new {@link ConvertingParameterAccessor} backed by a {@link StubParameterAccessor} simply returning the
* given parameters converted but unfiltered.
*
* @param converter
* @param parameters
* @return
*/
public static ConvertingParameterAccessor getAccessor(MongoWriter<Object> converter, Object... parameters) {
return new ConvertingParameterAccessor(converter, new StubParameterAccessor(parameters));
}
/**
*
*/
private Object[] values;
public StubParameterAccessor(Object... values) {
this.values = values;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.query.ParameterAccessor#getPageable()
*/
public Pageable getPageable() {
return null;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.query.ParameterAccessor#getSort()
*/
public Sort getSort() {
return null;
}
/*
* (non-Javadoc)
*
* @see java.lang.Iterable#iterator()
*/
public Iterator<Object> iterator() {
return Arrays.asList(values).iterator();
}
}

View File

@@ -19,6 +19,7 @@ Import-Template:
org.bson.*;version="0",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.slf4j.*;version="[1.5.0,1.6.0)",
org.w3c.dom.*;version="0"