Refactored repository implementation to use QuerySpec API.

Use QuerySpec instead of QueryBuilder now. Refactored query method query building to populate CriteriaSpec and work with root QuerySpec object until the API is polished. Some test should break right now as we have to get the skip and sorting stuff into the API.
This commit is contained in:
Oliver Gierke
2011-02-03 18:45:07 +00:00
parent 4ed5ff7dc8
commit f9e50916e1
6 changed files with 196 additions and 249 deletions

View File

@@ -1,157 +0,0 @@
/*
* Copyright 2010-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.HashMap;
import java.util.Map;
import org.springframework.data.document.mongodb.CursorPreparer;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCursor;
/**
* Collection of utility methods to apply sorting and pagination to a
* {@link DBCursor}.
*
* @author Oliver Gierke
*/
abstract class MongoCursorUtils {
private MongoCursorUtils() {
}
/**
* Creates a {@link CursorPreparer} applying the given {@link Pageable} to
* the cursor.
*
* @param pageable
* @return
*/
public static CursorPreparer withPagination(Pageable pageable) {
return new PaginationCursorPreparer(pageable);
}
/**
* Creates a {@link CursorPreparer} to apply the given {@link Sort} to the
* cursor.
*
* @param sort
* @return
*/
public static CursorPreparer withSorting(Sort sort) {
return new SortingCursorPreparer(sort);
}
/**
* Applies the given {@link Pageable} to the given {@link DBCursor}.
*
* @author Oliver Gierke
*/
private static class PaginationCursorPreparer implements CursorPreparer {
private final Pageable pageable;
private final SortingCursorPreparer sortingPreparer;
/**
* Creates a new {@link PaginationCursorPreparer}.
*
* @param pageable
*/
public PaginationCursorPreparer(Pageable pageable) {
this.pageable = pageable;
this.sortingPreparer =
new SortingCursorPreparer(pageable.getSort());
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.document.mongodb.CursorPreparer#prepare(
* com.mongodb.DBCursor)
*/
public void prepare(DBCursor cursor) {
if (pageable == null) {
return;
}
int toSkip = pageable.getOffset();
int first = pageable.getPageSize();
cursor.limit(first).skip(toSkip);
sortingPreparer.prepare(cursor);
}
}
/**
* Applies the given {@link Sort} to the given {@link DBCursor}.
*
* @author Oliver Gierke
*/
private static class SortingCursorPreparer implements CursorPreparer {
private final Sort sort;
/**
* Creates a new {@link SortingCursorPreparer}.
*
* @param sort
*/
public SortingCursorPreparer(Sort sort) {
this.sort = sort;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.document.mongodb.CursorPreparer#prepare(
* com.mongodb.DBCursor)
*/
public void prepare(DBCursor cursor) {
if (sort == null) {
return;
}
Map<String, Integer> sorts = new HashMap<String, Integer>();
for (Order order : sort) {
sorts.put(order.getProperty(),
Direction.ASC.equals(order.getDirection()) ? 1 : -1);
}
cursor.sort(new BasicDBObject(sorts));
}
}
}

View File

@@ -15,12 +15,13 @@
*/
package org.springframework.data.document.mongodb.repository;
import static org.springframework.data.document.mongodb.repository.MongoCursorUtils.*;
import static org.springframework.data.document.mongodb.repository.QueryUtils.*;
import java.util.List;
import org.springframework.data.document.mongodb.CollectionCallback;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.builder.QuerySpec;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.query.QueryMethod;
@@ -75,28 +76,31 @@ public class MongoQuery implements RepositoryQuery {
SimpleParameterAccessor accessor =
new SimpleParameterAccessor(method.getParameters(), parameters);
MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, template.getConverter());
DBObject query = creator.createQuery();
QuerySpec spec = new QuerySpec();
MongoQueryCreator creator =
new MongoQueryCreator(spec, tree, accessor,
template.getConverter());
creator.createQuery();
if (method.isCollectionQuery()) {
return new CollectionExecution().execute(query);
return new CollectionExecution().execute(spec);
} else if (method.isPageQuery()) {
return new PagedExecution(creator, accessor.getPageable())
.execute(query);
.execute(spec);
} else {
return new SingleEntityExecution().execute(query);
return new SingleEntityExecution().execute(spec);
}
}
private abstract class Execution {
abstract Object execute(DBObject query);
abstract Object execute(QuerySpec query);
protected List<?> readCollection(DBObject query) {
protected List<?> readCollection(QuerySpec query) {
return template.find(template.getDefaultCollectionName(),
query, method.getDomainClass());
return template.query(query.build(), method.getDomainClass());
}
}
@@ -115,7 +119,7 @@ public class MongoQuery implements RepositoryQuery {
* #execute(com.mongodb.DBObject)
*/
@Override
public Object execute(DBObject query) {
public Object execute(QuerySpec query) {
return readCollection(query);
}
@@ -155,12 +159,13 @@ public class MongoQuery implements RepositoryQuery {
*/
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
Object execute(DBObject query) {
Object execute(QuerySpec query) {
int count = getCollectionCursor(query.getQueryObject()).count();
int count = getCollectionCursor(creator.createQuery()).count();
List<?> result =
template.find(query, method.getDomainClass(),
withPagination(pageable));
template.query(applyPagination(query, pageable),
method.getDomainClass());
return new PageImpl(result, pageable, count);
}
@@ -193,7 +198,7 @@ public class MongoQuery implements RepositoryQuery {
* #execute(com.mongodb.DBObject)
*/
@Override
Object execute(DBObject query) {
Object execute(QuerySpec query) {
List<?> result = readCollection(query);
return result.isEmpty() ? null : result.get(0);

View File

@@ -15,11 +15,16 @@
*/
package org.springframework.data.document.mongodb.repository;
import java.util.Arrays;
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.springframework.data.document.mongodb.builder.Criteria;
import org.springframework.data.document.mongodb.builder.CriteriaSpec;
import org.springframework.data.document.mongodb.builder.Query;
import org.springframework.data.document.mongodb.builder.QuerySpec;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.SimpleParameterAccessor;
import org.springframework.data.repository.query.SimpleParameterAccessor.BindableParameterIterator;
@@ -30,7 +35,6 @@ import org.springframework.data.repository.query.parser.PartTree;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
/**
@@ -38,10 +42,12 @@ import com.mongodb.QueryBuilder;
*
* @author Oliver Gierke
*/
class MongoQueryCreator extends AbstractQueryCreator<DBObject, QueryBuilder> {
private static final Log LOG = LogFactory.getLog(MongoQueryCreator.class);
private final MongoConverter converter;
class MongoQueryCreator extends AbstractQueryCreator<Void, CriteriaSpec> {
private static final Log LOG = LogFactory.getLog(MongoQueryCreator.class);
private final MongoConverter converter;
private final QuerySpec querySpec;
/**
* Creates a new {@link MongoQueryCreator} from the given {@link PartTree}
@@ -50,9 +56,11 @@ class MongoQueryCreator extends AbstractQueryCreator<DBObject, QueryBuilder> {
* @param tree
* @param accessor
*/
public MongoQueryCreator(PartTree tree, SimpleParameterAccessor accessor, MongoConverter converter) {
public MongoQueryCreator(QuerySpec querySpec, PartTree tree,
SimpleParameterAccessor accessor, MongoConverter converter) {
super(tree, accessor);
this.querySpec = querySpec;
this.converter = converter;
}
@@ -67,10 +75,10 @@ class MongoQueryCreator extends AbstractQueryCreator<DBObject, QueryBuilder> {
* .data.repository.query.SimpleParameterAccessor.BindableParameterIterator)
*/
@Override
protected QueryBuilder create(Part part, BindableParameterIterator iterator) {
protected CriteriaSpec create(Part part, BindableParameterIterator iterator) {
return from(part.getType(), QueryBuilder.start(part.getProperty().toDotPath()),
iterator);
return from(part.getType(),
querySpec.find(part.getProperty().toDotPath()), iterator);
}
@@ -84,10 +92,11 @@ class MongoQueryCreator extends AbstractQueryCreator<DBObject, QueryBuilder> {
* .data.repository.query.SimpleParameterAccessor.BindableParameterIterator)
*/
@Override
protected QueryBuilder and(Part part, QueryBuilder base,
protected CriteriaSpec and(Part part, CriteriaSpec base,
BindableParameterIterator iterator) {
return from(part.getType(), base.and(part.getProperty().toDotPath()), iterator);
return from(part.getType(), base.and(part.getProperty().toDotPath()),
iterator);
}
@@ -95,13 +104,13 @@ class MongoQueryCreator extends AbstractQueryCreator<DBObject, QueryBuilder> {
* (non-Javadoc)
*
* @see
* org.springframework.data.document.mongodb.repository.AbstractQueryCreator
* #reduceCriterias(java.lang.Object, java.lang.Object)
* org.springframework.data.repository.query.parser.AbstractQueryCreator
* #or(java.lang.Object, java.lang.Object)
*/
@Override
protected QueryBuilder or(QueryBuilder base, QueryBuilder criteria) {
protected CriteriaSpec or(CriteriaSpec base, CriteriaSpec criteria) {
base.or(criteria.get());
base.or(Arrays.asList(criteria.build()));
return base;
}
@@ -110,19 +119,19 @@ class MongoQueryCreator extends AbstractQueryCreator<DBObject, QueryBuilder> {
* (non-Javadoc)
*
* @see
* org.springframework.data.document.mongodb.repository.AbstractQueryCreator
* #finalize(java.lang.Object)
* org.springframework.data.repository.query.parser.AbstractQueryCreator
* #complete(java.lang.Object, org.springframework.data.domain.Sort)
*/
@Override
protected DBObject complete(QueryBuilder criteria, Sort sort) {
protected Void complete(CriteriaSpec criteria, Sort sort) {
DBObject query = criteria.get();
if (LOG.isDebugEnabled()) {
LOG.debug("Created query " + query);
}
return query;
Query query = criteria.build();
if (LOG.isDebugEnabled()) {
LOG.debug("Created query " + query);
}
return null;
}
@@ -134,38 +143,39 @@ class MongoQueryCreator extends AbstractQueryCreator<DBObject, QueryBuilder> {
* @param parameters
* @return
*/
private QueryBuilder from(Type type, QueryBuilder criteria,
private CriteriaSpec from(Type type, CriteriaSpec criteria,
BindableParameterIterator parameters) {
switch (type) {
case GREATER_THAN:
return criteria.greaterThan(getConvertedParameter(parameters));
return criteria.gt(getConvertedParameter(parameters));
case LESS_THAN:
return criteria.lessThan(getConvertedParameter(parameters));
return criteria.lt(getConvertedParameter(parameters));
case BETWEEN:
return criteria.greaterThan(getConvertedParameter(parameters)).lessThan(getConvertedParameter(parameters));
return criteria.gt(getConvertedParameter(parameters)).lt(
getConvertedParameter(parameters));
case IS_NOT_NULL:
return criteria.notEquals(null);
return criteria.not().is(null);
case IS_NULL:
return criteria.is(null);
case LIKE:
String value = parameters.next().toString();
return criteria.regex(toLikeRegex(value));
return criteria.is(toLikeRegex(value));
case SIMPLE_PROPERTY:
return criteria.is(getConvertedParameter(parameters));
case NEGATING_SIMPLE_PROPERTY:
return criteria.notEquals(getConvertedParameter(parameters));
return criteria.not().is(getConvertedParameter(parameters));
}
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");
DBObject result = new BasicDBObject();
converter.write(new ValueHolder(parameters.next()), result);
return result.get("value");
}
@@ -175,22 +185,27 @@ class MongoQueryCreator extends AbstractQueryCreator<DBObject, QueryBuilder> {
return Pattern.compile(regex);
}
/**
* Simple value holder class to allow conversion and accessing the converted value in a deterministic way.
*
* @author Oliver Gierke
*/
/**
* 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;
}
private Object value;
public ValueHolder(Object value) {
this.value = value;
}
@SuppressWarnings("unused")
public Object getValue() {
return value;
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2010-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 org.springframework.data.document.mongodb.builder.QuerySpec;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import com.mongodb.DBCursor;
/**
* Collection of utility methods to apply sorting and pagination to a
* {@link DBCursor}.
*
* @author Oliver Gierke
*/
abstract class QueryUtils {
private QueryUtils() {
}
/**
* Applies the given {@link Pageable} to the given {@link QuerySpec}. Will
* do nothing if {@link Pageable} is {@literal null}.
*
* @param spec
* @param pageable
* @return
*/
public static QuerySpec applyPagination(QuerySpec spec, Pageable pageable) {
if (pageable == null) {
return spec;
}
spec.limit(pageable.getPageSize());
// spec.skip(pageable.getOffset());
return applySorting(spec, pageable.getSort());
}
/**
* Applies the given {@link Sort} to the {@link QuerySpec}. Will do nothing
* if {@link Sort} is {@literal null}.
*
* @param spec
* @param sort
* @return
*/
public static QuerySpec applySorting(QuerySpec spec, Sort sort) {
if (sort == null) {
return spec;
}
// TODO apply sorting
// spec.
return spec;
}
}

View File

@@ -15,14 +15,14 @@
*/
package org.springframework.data.document.mongodb.repository;
import static org.springframework.data.document.mongodb.repository.MongoCursorUtils.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.bson.types.ObjectId;
import org.springframework.data.document.mongodb.MongoConverter;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.builder.QuerySpec;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
@@ -32,7 +32,6 @@ import org.springframework.data.repository.support.IsNewAware;
import org.springframework.data.repository.support.RepositorySupport;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.QueryBuilder;
@@ -55,8 +54,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> extends
* @param domainClass
* @param template
*/
public SimpleMongoRepository(Class<T> domainClass,
MongoTemplate template) {
public SimpleMongoRepository(Class<T> domainClass, MongoTemplate template) {
super(domainClass);
@@ -105,12 +103,16 @@ public class SimpleMongoRepository<T, ID extends Serializable> extends
* )
*/
public T findById(ID id) {
MongoConverter converter = template.getConverter();
List<T> result = template.find(QueryBuilder.start("_id").is(converter.convertObjectId(id)).get(),
getDomainClass());
return result.isEmpty() ? null : result.get(0);
MongoConverter converter = template.getConverter();
ObjectId objectId = converter.convertObjectId(id);
List<T> result =
template.query(
new QuerySpec().find("_id").is(objectId).build(),
getDomainClass());
return result.isEmpty() ? null : result.get(0);
}
@@ -200,10 +202,11 @@ public class SimpleMongoRepository<T, ID extends Serializable> extends
public Page<T> findAll(final Pageable pageable) {
Long count = count();
QuerySpec spec = new QuerySpec();
List<T> list =
template.find(new BasicDBObject(), getDomainClass(),
withPagination(pageable));
template.query(QueryUtils.applyPagination(spec, pageable),
getDomainClass());
return new PageImpl<T>(list, pageable, count);
}
@@ -218,8 +221,8 @@ public class SimpleMongoRepository<T, ID extends Serializable> extends
*/
public List<T> findAll(final Sort sort) {
return template.find(new BasicDBObject(), getDomainClass(),
withSorting(sort));
QuerySpec query = QueryUtils.applySorting(new QuerySpec(), sort);
return template.query(query, getDomainClass());
}

View File

@@ -25,6 +25,7 @@ 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.document.mongodb.builder.QuerySpec;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.SimpleParameterAccessor;
import org.springframework.data.repository.query.parser.PartTree;
@@ -40,7 +41,7 @@ public class MongoQueryCreatorUnitTests {
Method findByFirstname;
Method findByFirstnameAndFriend;
@Mock
MongoConverter converter;
@@ -63,16 +64,18 @@ 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(new QuerySpec(), tree,
new SimpleParameterAccessor(new Parameters(
findByFirstname), new Object[] { "Oliver" }),
converter);
creator.createQuery();
creator =
new MongoQueryCreator(new PartTree("findByFirstNameAndFriend",
Person.class), new SimpleParameterAccessor(
new Parameters(findByFirstnameAndFriend), new Object[] {
new MongoQueryCreator(new QuerySpec(), new PartTree(
"findByFirstNameAndFriend", Person.class),
new SimpleParameterAccessor(new Parameters(
findByFirstnameAndFriend), new Object[] {
"Oliver", new Person() }), converter);
creator.createQuery();
}