From f10e5a19c5f9c00414f7f1e8d18a505ef3559bad Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 11 Feb 2016 17:56:04 +0100 Subject: [PATCH] DATAMONGO-1345 - Finalized application of projections in query methods. Refactored the query execution out of AbstractMongoQuery into MongoQueryExecution. Made sure the streaming execution lazily applies the projections, too. Added a DtoInstantiatingConverter to be able to copy data from created entities into DTOs as we cannot hand the DTO type into the MongoTemplate execution in the first place as it's going to be used for the query mapping currently. --- .../repository/query/AbstractMongoQuery.java | 382 +++--------------- .../query/DtoInstantiatingConverter.java | 108 +++++ .../repository/query/MongoQueryCreator.java | 34 +- .../repository/query/MongoQueryExecution.java | 381 +++++++++++++++++ .../repository/query/MongoQueryMethod.java | 3 +- .../repository/query/PartTreeMongoQuery.java | 6 +- .../support/MongoRepositoryFactory.java | 24 +- .../query/MongoQueryCreatorUnitTests.java | 41 +- .../query/PartTreeMongoQueryUnitTests.java | 4 +- spring-data-mongodb/template.mf | 2 + 10 files changed, 602 insertions(+), 383 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DtoInstantiatingConverter.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java index ebe585872..d6b65be25 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 the original author or authors. + * Copyright 2010-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,31 +15,25 @@ */ package org.springframework.data.mongodb.repository.query; -import java.util.Collections; -import java.util.List; - -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Range; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.SliceImpl; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.GeoPage; -import org.springframework.data.geo.GeoResult; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Point; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.EntityInstantiators; import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.CollectionExecution; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.DeleteExecution; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.GeoNearExecution; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagedExecution; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagingGeoNearExecution; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.ResultProcessingConverter; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.ResultProcessingExecution; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.SingleEntityExecution; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.SlicedExecution; +import org.springframework.data.mongodb.repository.query.MongoQueryExecution.StreamExecution; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.RepositoryQuery; -import org.springframework.data.util.CloseableIterator; -import org.springframework.data.util.StreamUtils; -import org.springframework.data.util.TypeInformation; +import org.springframework.data.repository.query.ResultProcessor; import org.springframework.util.Assert; -import com.mongodb.WriteResult; - /** * Base class for {@link RepositoryQuery} implementations for Mongo. * @@ -51,6 +45,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { private final MongoQueryMethod method; private final MongoOperations operations; + private final EntityInstantiators instantiators; /** * Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}. @@ -65,6 +60,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { this.method = method; this.operations = operations; + this.instantiators = new EntityInstantiators(); } /* @@ -86,30 +82,53 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { applyQueryMetaAttributesWhenPresent(query); + ResultProcessor processor = method.getResultProcessor().withDynamicProjection(accessor); + String collection = method.getEntityInformation().getCollectionName(); + + MongoQueryExecution execution = getExecution(query, accessor, + new ResultProcessingConverter(processor, operations, instantiators)); + + return execution.execute(query, processor.getReturnedType().getDomainType(), collection); + } + + /** + * Returns the execution instance to use. + * + * @param query must not be {@literal null}. + * @param parameters must not be {@literal null}. + * @param accessor must not be {@literal null}. + * @return + */ + private MongoQueryExecution getExecution(Query query, MongoParameterAccessor accessor, + Converter resultProcessing) { + if (method.isStreamQuery()) { - return new StreamExecution().execute(query); - } else if (isDeleteQuery()) { - return new DeleteExecution().execute(query); + return new StreamExecution(operations, resultProcessing); + } + + return new ResultProcessingExecution(getExecutionToWrap(query, accessor), resultProcessing); + } + + private MongoQueryExecution getExecutionToWrap(Query query, MongoParameterAccessor accessor) { + + if (isDeleteQuery()) { + return new DeleteExecution(operations, method); } else if (method.isGeoNearQuery() && method.isPageQuery()) { - - MongoParameterAccessor countAccessor = new MongoParametersParameterAccessor(method, parameters); - Query countQuery = createCountQuery(new ConvertingParameterAccessor(operations.getConverter(), countAccessor)); - - return new GeoNearExecution(accessor).execute(query, countQuery); + return new PagingGeoNearExecution(operations, accessor, method.getReturnType(), this); } else if (method.isGeoNearQuery()) { - return new GeoNearExecution(accessor).execute(query); + return new GeoNearExecution(operations, accessor, method.getReturnType()); } else if (method.isSliceQuery()) { - return new SlicedExecution(accessor.getPageable()).execute(query); + return new SlicedExecution(operations, accessor.getPageable()); } else if (method.isCollectionQuery()) { - return new CollectionExecution(accessor.getPageable()).execute(query); + return new CollectionExecution(operations, accessor.getPageable()); } else if (method.isPageQuery()) { - return new PagedExecution(accessor.getPageable()).execute(query); + return new PagedExecution(operations, accessor.getPageable()); } else { - return new SingleEntityExecution(isCountQuery()).execute(query); + return new SingleEntityExecution(operations, isCountQuery()); } } - private Query applyQueryMetaAttributesWhenPresent(Query query) { + Query applyQueryMetaAttributesWhenPresent(Query query) { if (method.hasQueryMetaAttributes()) { query.setMeta(method.getQueryMetaAttributes()); @@ -127,12 +146,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { * @return */ protected Query createCountQuery(ConvertingParameterAccessor accessor) { - - Query query = createQuery(accessor); - - applyQueryMetaAttributesWhenPresent(query); - - return query; + return applyQueryMetaAttributesWhenPresent(createQuery(accessor)); } /** @@ -157,292 +171,4 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { * @since 1.5 */ protected abstract boolean isDeleteQuery(); - - private abstract class Execution { - - abstract Object execute(Query query); - - protected List readCollection(Query query) { - - MongoEntityMetadata metadata = method.getEntityInformation(); - - String collectionName = metadata.getCollectionName(); - return operations.find(query, metadata.getJavaType(), collectionName); - } - } - - /** - * {@link Execution} for collection returning queries. - * - * @author Oliver Gierke - */ - final class CollectionExecution extends Execution { - - private final Pageable pageable; - - CollectionExecution(Pageable pageable) { - this.pageable = pageable; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query) - */ - @Override - public Object execute(Query query) { - return readCollection(query.with(pageable)); - } - } - - /** - * {@link Execution} for {@link Slice} query methods. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @since 1.5 - */ - - final class SlicedExecution extends Execution { - - private final Pageable pageable; - - SlicedExecution(Pageable pageable) { - this.pageable = pageable; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query) - */ - @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) - Object execute(Query query) { - - MongoEntityMetadata metadata = method.getEntityInformation(); - int pageSize = pageable.getPageSize(); - - // 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; - - return new SliceImpl(hasNext ? result.subList(0, pageSize) : result, pageable, hasNext); - } - } - - /** - * {@link Execution} for pagination queries. - * - * @author Oliver Gierke - */ - final class PagedExecution extends Execution { - - private final Pageable pageable; - - /** - * Creates a new {@link PagedExecution}. - * - * @param pageable - */ - public PagedExecution(Pageable pageable) { - - Assert.notNull(pageable); - this.pageable = pageable; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query) - */ - @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - Object execute(Query query) { - - MongoEntityMetadata metadata = method.getEntityInformation(); - String collectionName = metadata.getCollectionName(); - Class type = metadata.getJavaType(); - - int overallLimit = query.getLimit(); - long count = operations.count(query, type, collectionName); - count = overallLimit != 0 ? Math.min(count, query.getLimit()) : count; - - boolean pageableOutOfScope = pageable.getOffset() > count; - - if (pageableOutOfScope) { - return new PageImpl(Collections.emptyList(), pageable, count); - } - - // Apply raw pagination - query = query.with(pageable); - - // Adjust limit if page would exceed the overall limit - if (overallLimit != 0 && pageable.getOffset() + pageable.getPageSize() > overallLimit) { - query.limit(overallLimit - pageable.getOffset()); - } - - List result = operations.find(query, type, collectionName); - return new PageImpl(result, pageable, count); - } - } - - /** - * {@link Execution} to return a single entity. - * - * @author Oliver Gierke - */ - final class SingleEntityExecution extends Execution { - - private final boolean countProjection; - - private SingleEntityExecution(boolean countProjection) { - this.countProjection = countProjection; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.core.query.Query) - */ - @Override - Object execute(Query query) { - - MongoEntityMetadata metadata = method.getEntityInformation(); - return countProjection ? operations.count(query, metadata.getJavaType()) - : operations.findOne(query, metadata.getJavaType(), metadata.getCollectionName()); - } - } - - /** - * {@link Execution} to execute geo-near queries. - * - * @author Oliver Gierke - */ - final class GeoNearExecution extends Execution { - - private final MongoParameterAccessor accessor; - - public GeoNearExecution(MongoParameterAccessor accessor) { - this.accessor = accessor; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query) - */ - @Override - Object execute(Query query) { - - GeoResults results = doExecuteQuery(query); - return isListOfGeoResult() ? results.getContent() : results; - } - - /** - * Executes the given {@link Query} to return a page. - * - * @param query must not be {@literal null}. - * @param countQuery must not be {@literal null}. - * @return - */ - Object execute(Query query, Query countQuery) { - - MongoEntityMetadata metadata = method.getEntityInformation(); - long count = operations.count(countQuery, metadata.getCollectionName()); - - return new GeoPage(doExecuteQuery(query), accessor.getPageable(), count); - } - - @SuppressWarnings("unchecked") - private GeoResults doExecuteQuery(Query query) { - - Point nearLocation = accessor.getGeoNearLocation(); - NearQuery nearQuery = NearQuery.near(nearLocation); - - if (query != null) { - nearQuery.query(query); - } - - Range distances = accessor.getDistanceRange(); - Distance maxDistance = distances.getUpperBound(); - - if (maxDistance != null) { - nearQuery.maxDistance(maxDistance).in(maxDistance.getMetric()); - } - - Distance minDistance = distances.getLowerBound(); - - if (minDistance != null) { - nearQuery.minDistance(minDistance).in(minDistance.getMetric()); - } - - Pageable pageable = accessor.getPageable(); - if (pageable != null) { - nearQuery.with(pageable); - } - - MongoEntityMetadata metadata = method.getEntityInformation(); - return (GeoResults) operations.geoNear(nearQuery, metadata.getJavaType(), metadata.getCollectionName()); - } - - private boolean isListOfGeoResult() { - - TypeInformation returnType = method.getReturnType(); - - if (!returnType.getType().equals(List.class)) { - return false; - } - - TypeInformation componentType = returnType.getComponentType(); - return componentType == null ? false : GeoResult.class.equals(componentType.getType()); - } - } - - /** - * {@link Execution} removing documents matching the query. - * - * @since 1.5 - */ - final class DeleteExecution extends Execution { - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query) - */ - @Override - Object execute(Query query) { - - MongoEntityMetadata metadata = method.getEntityInformation(); - return deleteAndConvertResult(query, metadata); - } - - private Object deleteAndConvertResult(Query query, MongoEntityMetadata metadata) { - - if (method.isCollectionQuery()) { - return operations.findAllAndRemove(query, metadata.getJavaType(), metadata.getCollectionName()); - } - - WriteResult writeResult = operations.remove(query, metadata.getJavaType(), metadata.getCollectionName()); - return writeResult != null ? writeResult.getN() : 0L; - } - } - - /** - * @author Thomas Darimont - * @since 1.7 - */ - final class StreamExecution extends Execution { - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query) - */ - @Override - @SuppressWarnings("unchecked") - Object execute(Query query) { - - Class entityType = getQueryMethod().getEntityInformation().getJavaType(); - - return StreamUtils.createStreamFromIterator((CloseableIterator) operations.stream(query, entityType)); - } - } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DtoInstantiatingConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DtoInstantiatingConverter.java new file mode 100644 index 000000000..0aa06e5e5 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DtoInstantiatingConverter.java @@ -0,0 +1,108 @@ +/* + * Copyright 2015-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.query; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.EntityInstantiator; +import org.springframework.data.convert.EntityInstantiators; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PersistentPropertyAccessor; +import org.springframework.data.mapping.PreferredConstructor; +import org.springframework.data.mapping.PreferredConstructor.Parameter; +import org.springframework.data.mapping.SimplePropertyHandler; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.model.ParameterValueProvider; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.util.Assert; + +/** + * {@link Converter} to instantiate DTOs from fully equipped domain objects. + * + * @author Oliver Gierke + */ +class DtoInstantiatingConverter implements Converter { + + private final Class targetType; + private final MappingContext, ? extends PersistentProperty> context; + private final EntityInstantiator instantiator; + + /** + * Creates a new {@link Converter} to instantiate DTOs. + * + * @param dtoType must not be {@literal null}. + * @param context must not be {@literal null}. + * @param instantiators must not be {@literal null}. + */ + public DtoInstantiatingConverter(Class dtoType, + MappingContext, MongoPersistentProperty> context, + EntityInstantiators instantiator) { + + Assert.notNull(dtoType, "DTO type must not be null!"); + Assert.notNull(context, "MappingContext must not be null!"); + Assert.notNull(instantiator, "EntityInstantiators must not be null!"); + + this.targetType = dtoType; + this.context = context; + this.instantiator = instantiator.getInstantiatorFor(context.getPersistentEntity(dtoType)); + } + + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) + */ + @Override + public Object convert(Object source) { + + if (targetType.isInterface()) { + return source; + } + + final PersistentEntity sourceEntity = context.getPersistentEntity(source.getClass()); + final PersistentPropertyAccessor sourceAccessor = sourceEntity.getPropertyAccessor(source); + final PersistentEntity targetEntity = context.getPersistentEntity(targetType); + final PreferredConstructor> constructor = targetEntity + .getPersistenceConstructor(); + + @SuppressWarnings({ "rawtypes", "unchecked" }) + Object dto = instantiator.createInstance(targetEntity, new ParameterValueProvider() { + + @Override + public Object getParameterValue(Parameter parameter) { + return sourceAccessor.getProperty(sourceEntity.getPersistentProperty(parameter.getName())); + } + }); + + final PersistentPropertyAccessor dtoAccessor = targetEntity.getPropertyAccessor(dto); + + targetEntity.doWithProperties(new SimplePropertyHandler() { + + @Override + public void doWithPersistentProperty(PersistentProperty property) { + + if (constructor.isConstructorParameter(property)) { + return; + } + + dtoAccessor.setProperty(property, + sourceAccessor.getProperty(sourceEntity.getPersistentProperty(property.getName()))); + } + }); + + return dto; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java index a0cb9aa00..caee21e2a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java @@ -169,32 +169,31 @@ class MongoQueryCreator extends AbstractQueryCreator { * @param parameters * @return */ - private Criteria from(Part part, MongoPersistentProperty property, Criteria criteria, - PotentiallyConvertingIterator parameters) { + private Criteria from(Part part, MongoPersistentProperty property, Criteria criteria, Iterator parameters) { Type type = part.getType(); switch (type) { case AFTER: case GREATER_THAN: - return criteria.gt(parameters.nextConverted(property)); + return criteria.gt(parameters.next()); case GREATER_THAN_EQUAL: - return criteria.gte(parameters.nextConverted(property)); + return criteria.gte(parameters.next()); case BEFORE: case LESS_THAN: - return criteria.lt(parameters.nextConverted(property)); + return criteria.lt(parameters.next()); case LESS_THAN_EQUAL: - return criteria.lte(parameters.nextConverted(property)); + return criteria.lte(parameters.next()); case BETWEEN: - return criteria.gt(parameters.nextConverted(property)).lt(parameters.nextConverted(property)); + return criteria.gt(parameters.next()).lt(parameters.next()); case IS_NOT_NULL: return criteria.ne(null); case IS_NULL: return criteria.is(null); case NOT_IN: - return criteria.nin(nextAsArray(parameters, property)); + return criteria.nin(nextAsArray(parameters)); case IN: - return criteria.in(nextAsArray(parameters, property)); + return criteria.in(nextAsArray(parameters)); case LIKE: case STARTING_WITH: case ENDING_WITH: @@ -241,12 +240,12 @@ class MongoQueryCreator extends AbstractQueryCreator { return criteria.within((Shape) parameter); case SIMPLE_PROPERTY: - return isSimpleComparisionPossible(part) ? criteria.is(parameters.nextConverted(property)) + return isSimpleComparisionPossible(part) ? criteria.is(parameters.next()) : createLikeRegexCriteriaOrThrow(part, property, criteria, parameters, false); case NEGATING_SIMPLE_PROPERTY: - return isSimpleComparisionPossible(part) ? criteria.ne(parameters.nextConverted(property)) + return isSimpleComparisionPossible(part) ? criteria.ne(parameters.next()) : createLikeRegexCriteriaOrThrow(part, property, criteria, parameters, true); default: throw new IllegalArgumentException("Unsupported keyword!"); @@ -278,7 +277,7 @@ class MongoQueryCreator extends AbstractQueryCreator { * @return the criteria extended with the like-regex. */ private Criteria createLikeRegexCriteriaOrThrow(Part part, MongoPersistentProperty property, Criteria criteria, - PotentiallyConvertingIterator parameters, boolean shouldNegateExpression) { + Iterator parameters, boolean shouldNegateExpression) { PropertyPath path = part.getProperty().getLeafProperty(); @@ -297,7 +296,7 @@ class MongoQueryCreator extends AbstractQueryCreator { criteria = criteria.not(); } - return addAppropriateLikeRegexTo(criteria, part, parameters.nextConverted(property).toString()); + return addAppropriateLikeRegexTo(criteria, part, parameters.next().toString()); case NEVER: // intentional no-op @@ -319,10 +318,10 @@ class MongoQueryCreator extends AbstractQueryCreator { * @return */ private Criteria createContainingCriteria(Part part, MongoPersistentProperty property, Criteria criteria, - PotentiallyConvertingIterator parameters) { + Iterator parameters) { if (property.isCollectionLike()) { - return criteria.in(nextAsArray(parameters, property)); + return criteria.in(nextAsArray(parameters)); } return addAppropriateLikeRegexTo(criteria, part, parameters.next().toString()); @@ -377,8 +376,9 @@ class MongoQueryCreator extends AbstractQueryCreator { String.format("Expected parameter type of %s but got %s!", type, parameter.getClass())); } - private Object[] nextAsArray(PotentiallyConvertingIterator iterator, MongoPersistentProperty property) { - Object next = iterator.nextConverted(property); + private Object[] nextAsArray(Iterator iterator) { + + Object next = iterator.next(); if (next instanceof Collection) { return ((Collection) next).toArray(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java new file mode 100644 index 000000000..c4274977b --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java @@ -0,0 +1,381 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.query; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.EntityInstantiators; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Range; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; +import org.springframework.data.geo.Distance; +import org.springframework.data.geo.GeoPage; +import org.springframework.data.geo.GeoResult; +import org.springframework.data.geo.GeoResults; +import org.springframework.data.geo.Point; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.query.NearQuery; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.repository.query.ResultProcessor; +import org.springframework.data.repository.query.ReturnedType; +import org.springframework.data.util.CloseableIterator; +import org.springframework.data.util.StreamUtils; +import org.springframework.data.util.TypeInformation; +import org.springframework.util.ClassUtils; + +import com.mongodb.WriteResult; + +interface MongoQueryExecution { + + Object execute(Query query, Class type, String collection); + + /** + * {@link MongoQueryExecution} for collection returning queries. + * + * @author Oliver Gierke + */ + @RequiredArgsConstructor + static final class CollectionExecution implements MongoQueryExecution { + + private final @NonNull MongoOperations operations; + private final Pageable pageable; + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String) + */ + @Override + public Object execute(Query query, Class type, String collection) { + return operations.find(query.with(pageable), type, collection); + } + } + + /** + * {@link MongoQueryExecution} for {@link Slice} query methods. + * + * @author Oliver Gierke + * @author Christoph Strobl + * @since 1.5 + */ + @RequiredArgsConstructor + static final class SlicedExecution implements MongoQueryExecution { + + private final @NonNull MongoOperations operations; + private final @NonNull Pageable pageable; + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String) + */ + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object execute(Query query, Class type, String collection) { + + int pageSize = pageable.getPageSize(); + + // Apply Pageable but tweak limit to peek into next page + Query modifiedQuery = query.with(pageable).limit(pageSize + 1); + List result = operations.find(modifiedQuery, type, collection); + + boolean hasNext = result.size() > pageSize; + + return new SliceImpl(hasNext ? result.subList(0, pageSize) : result, pageable, hasNext); + } + } + + /** + * {@link MongoQueryExecution} for pagination queries. + * + * @author Oliver Gierke + */ + @RequiredArgsConstructor + static final class PagedExecution implements MongoQueryExecution { + + private final @NonNull MongoOperations operations; + private final @NonNull Pageable pageable; + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String) + */ + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Object execute(Query query, Class type, String collection) { + + int overallLimit = query.getLimit(); + long count = operations.count(query, type, collection); + count = overallLimit != 0 ? Math.min(count, query.getLimit()) : count; + + boolean pageableOutOfScope = pageable.getOffset() > count; + + if (pageableOutOfScope) { + return new PageImpl(Collections.emptyList(), pageable, count); + } + + // Apply raw pagination + query = query.with(pageable); + + // Adjust limit if page would exceed the overall limit + if (overallLimit != 0 && pageable.getOffset() + pageable.getPageSize() > overallLimit) { + query.limit(overallLimit - pageable.getOffset()); + } + + List result = operations.find(query, type, collection); + return new PageImpl(result, pageable, count); + } + } + + /** + * {@link MongoQueryExecution} to return a single entity. + * + * @author Oliver Gierke + */ + @RequiredArgsConstructor + static final class SingleEntityExecution implements MongoQueryExecution { + + private final MongoOperations operations; + private final boolean countProjection; + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String) + */ + @Override + public Object execute(Query query, Class type, String collection) { + return countProjection ? operations.count(query, type, collection) : operations.findOne(query, type, collection); + } + } + + /** + * {@link MongoQueryExecution} to execute geo-near queries. + * + * @author Oliver Gierke + */ + @RequiredArgsConstructor + static class GeoNearExecution implements MongoQueryExecution { + + private final MongoOperations operations; + private final MongoParameterAccessor accessor; + private final TypeInformation returnType; + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String) + */ + @Override + public Object execute(Query query, Class type, String collection) { + + GeoResults results = doExecuteQuery(query, type, collection); + return isListOfGeoResult() ? results.getContent() : results; + } + + @SuppressWarnings("unchecked") + protected GeoResults doExecuteQuery(Query query, Class type, String collection) { + + Point nearLocation = accessor.getGeoNearLocation(); + NearQuery nearQuery = NearQuery.near(nearLocation); + + if (query != null) { + nearQuery.query(query); + } + + Range distances = accessor.getDistanceRange(); + Distance maxDistance = distances.getUpperBound(); + + if (maxDistance != null) { + nearQuery.maxDistance(maxDistance).in(maxDistance.getMetric()); + } + + Distance minDistance = distances.getLowerBound(); + + if (minDistance != null) { + nearQuery.minDistance(minDistance).in(minDistance.getMetric()); + } + + Pageable pageable = accessor.getPageable(); + + if (pageable != null) { + nearQuery.with(pageable); + } + + return (GeoResults) operations.geoNear(nearQuery, type, collection); + } + + private boolean isListOfGeoResult() { + + if (!returnType.getType().equals(List.class)) { + return false; + } + + TypeInformation componentType = returnType.getComponentType(); + return componentType == null ? false : GeoResult.class.equals(componentType.getType()); + } + } + + static final class PagingGeoNearExecution extends GeoNearExecution { + + private final MongoOperations operations; + private final MongoParameterAccessor accessor; + private final AbstractMongoQuery mongoQuery; + + public PagingGeoNearExecution(MongoOperations operations, MongoParameterAccessor accessor, + TypeInformation returnType, AbstractMongoQuery query) { + + super(operations, accessor, returnType); + + this.accessor = accessor; + this.operations = operations; + this.mongoQuery = query; + } + + /** + * Executes the given {@link Query} to return a page. + * + * @param query must not be {@literal null}. + * @param countQuery must not be {@literal null}. + * @return + */ + @Override + public Object execute(Query query, Class type, String collection) { + + ConvertingParameterAccessor parameterAccessor = new ConvertingParameterAccessor(operations.getConverter(), + accessor); + Query countQuery = mongoQuery.applyQueryMetaAttributesWhenPresent(mongoQuery.createCountQuery(parameterAccessor)); + long count = operations.count(countQuery, collection); + + return new GeoPage(doExecuteQuery(query, type, collection), accessor.getPageable(), count); + } + } + + /** + * {@link MongoQueryExecution} removing documents matching the query. + * + * @since 1.5 + */ + @RequiredArgsConstructor + static final class DeleteExecution implements MongoQueryExecution { + + private final MongoOperations operations; + private final MongoQueryMethod method; + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String) + */ + @Override + public Object execute(Query query, Class type, String collection) { + + if (method.isCollectionQuery()) { + return operations.findAllAndRemove(query, type, collection); + } + + WriteResult writeResult = operations.remove(query, type, collection); + return writeResult != null ? writeResult.getN() : 0L; + } + } + + /** + * @author Thomas Darimont + * @since 1.7 + */ + @RequiredArgsConstructor + static final class StreamExecution implements MongoQueryExecution { + + private final @NonNull MongoOperations operations; + private final @NonNull Converter resultProcessing; + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String) + */ + @Override + @SuppressWarnings("unchecked") + public Object execute(Query query, Class type, String collection) { + + return StreamUtils.createStreamFromIterator((CloseableIterator) operations.stream(query, type)) + .map(new Function() { + + @Override + public Object apply(Object t) { + return resultProcessing.convert(t); + } + }); + } + } + + /** + * An {@link MongoQueryExecution} that wraps the results of the given delegate with the given result processing. + * + * @author Oliver Gierke + * @since 1.9 + */ + @RequiredArgsConstructor + static final class ResultProcessingExecution implements MongoQueryExecution { + + private final @NonNull MongoQueryExecution delegate; + private final @NonNull Converter converter; + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String) + */ + @Override + public Object execute(Query query, Class type, String collection) { + return converter.convert(delegate.execute(query, type, collection)); + } + } + + /** + * A {@link Converter} to post-process all source objects using the given {@link ResultProcessor}. + * + * @author Oliver Gierke + * @since 1.9 + */ + @RequiredArgsConstructor + static final class ResultProcessingConverter implements Converter { + + private final @NonNull ResultProcessor processor; + private final @NonNull MongoOperations operations; + private final @NonNull EntityInstantiators instantiators; + + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) + */ + @Override + public Object convert(Object source) { + + ReturnedType returnedType = processor.getReturnedType(); + + if (ClassUtils.isPrimitiveOrWrapper(returnedType.getReturnedType())) { + return source; + } + + Converter converter = new DtoInstantiatingConverter(returnedType.getReturnedType(), + operations.getConverter().getMappingContext(), instantiators); + + return processor.processResult(source, converter); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java index f000fc392..85f0805f5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java @@ -136,7 +136,8 @@ public class MongoQueryMethod extends QueryMethod { MongoPersistentEntity returnedEntity = mappingContext.getPersistentEntity(returnedObjectType); MongoPersistentEntity managedEntity = mappingContext.getPersistentEntity(domainClass); - returnedEntity = returnedEntity == null ? managedEntity : returnedEntity; + returnedEntity = returnedEntity == null || returnedEntity.getType().isInterface() ? managedEntity + : returnedEntity; MongoPersistentEntity collectionEntity = domainClass.isAssignableFrom(returnedObjectType) ? returnedEntity : managedEntity; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java index ffbfe3505..349df7a2c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java @@ -44,6 +44,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { private final PartTree tree; private final boolean isGeoNearQuery; private final MappingContext context; + private final ResultProcessor processor; /** * Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}. @@ -54,7 +55,9 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations) { super(method, mongoOperations); - this.tree = new PartTree(method.getName(), method.getEntityInformation().getJavaType()); + + this.processor = method.getResultProcessor(); + this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType()); this.isGeoNearQuery = method.isGeoNearQuery(); this.context = mongoOperations.getConverter().getMappingContext(); } @@ -91,7 +94,6 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { if (!StringUtils.hasText(fieldSpec)) { - ResultProcessor processor = getQueryMethod().getResultProcessor(); ReturnedType returnedType = processor.withDynamicProjection(accessor).getReturnedType(); if (returnedType.isProjecting()) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java index f73676008..03d75f2c8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java @@ -54,7 +54,7 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); - private final MongoOperations mongoOperations; + private final MongoOperations operations; private final MappingContext, MongoPersistentProperty> mappingContext; /** @@ -66,7 +66,7 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { Assert.notNull(mongoOperations); - this.mongoOperations = mongoOperations; + this.operations = mongoOperations; this.mappingContext = mongoOperations.getConverter().getMappingContext(); } @@ -92,7 +92,7 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { MongoEntityInformation entityInformation = getEntityInformation(information.getDomainType(), information); - return getTargetRepositoryViaReflection(information, entityInformation, mongoOperations); + return getTargetRepositoryViaReflection(information, entityInformation, operations); } /* @@ -101,7 +101,7 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { */ @Override protected QueryLookupStrategy getQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider) { - return new MongoQueryLookupStrategy(evaluationContextProvider); + return new MongoQueryLookupStrategy(operations, evaluationContextProvider, mappingContext); } /* @@ -133,12 +133,18 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { * @author Oliver Gierke * @author Thomas Darimont */ - private class MongoQueryLookupStrategy implements QueryLookupStrategy { + private static class MongoQueryLookupStrategy implements QueryLookupStrategy { + private final MongoOperations operations; private final EvaluationContextProvider evaluationContextProvider; + MappingContext, MongoPersistentProperty> mappingContext; - public MongoQueryLookupStrategy(EvaluationContextProvider evaluationContextProvider) { + public MongoQueryLookupStrategy(MongoOperations operations, EvaluationContextProvider evaluationContextProvider, + MappingContext, MongoPersistentProperty> mappingContext) { + + this.operations = operations; this.evaluationContextProvider = evaluationContextProvider; + this.mappingContext = mappingContext; } /* @@ -154,12 +160,12 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { if (namedQueries.hasQuery(namedQueryName)) { String namedQuery = namedQueries.getQuery(namedQueryName); - return new StringBasedMongoQuery(namedQuery, queryMethod, mongoOperations, EXPRESSION_PARSER, + return new StringBasedMongoQuery(namedQuery, queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); } else if (queryMethod.hasAnnotatedQuery()) { - return new StringBasedMongoQuery(queryMethod, mongoOperations, EXPRESSION_PARSER, evaluationContextProvider); + return new StringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); } else { - return new PartTreeMongoQuery(queryMethod, mongoOperations); + return new PartTreeMongoQuery(queryMethod, operations); } } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java index d2ac7fd53..9780f32be 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java @@ -17,8 +17,6 @@ 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.Matchers.any; import static org.mockito.Mockito.*; import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.Query.*; @@ -27,16 +25,11 @@ import static org.springframework.data.mongodb.repository.query.StubParameterAcc import java.lang.reflect.Method; import java.util.List; +import org.bson.types.ObjectId; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; import org.springframework.data.domain.Range; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Metrics; @@ -44,14 +37,19 @@ import org.springframework.data.geo.Point; import org.springframework.data.geo.Polygon; import org.springframework.data.geo.Shape; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.Person; import org.springframework.data.mongodb.core.Venue; +import org.springframework.data.mongodb.core.convert.DbRefResolver; +import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; +import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; @@ -59,7 +57,8 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.data.repository.query.parser.PartTree; -import org.springframework.data.util.TypeInformation; + +import com.mongodb.DBObject; /** * Unit test for {@link MongoQueryCreator}. @@ -68,14 +67,12 @@ import org.springframework.data.util.TypeInformation; * @author Thomas Darimont * @author Christoph Strobl */ -@RunWith(MockitoJUnitRunner.class) public class MongoQueryCreatorUnitTests { Method findByFirstname, findByFirstnameAndFriend, findByFirstnameNotNull; - @Mock MongoConverter converter; - - MappingContext context; + MappingContext, MongoPersistentProperty> context; + MongoConverter converter; @Rule public ExpectedException expection = ExpectedException.none(); @@ -84,11 +81,8 @@ public class MongoQueryCreatorUnitTests { context = new MongoMappingContext(); - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) throws Throwable { - return invocation.getArguments()[0]; - } - }).when(converter).convertToMongoType(any(), Mockito.any(TypeInformation.class)); + DbRefResolver resolver = new DefaultDbRefResolver(mock(MongoDbFactory.class)); + converter = new MappingMongoConverter(resolver, context); } @Test @@ -243,14 +237,13 @@ public class MongoQueryCreatorUnitTests { public void createsQueryReferencingADBRefCorrectly() { User user = new User(); - com.mongodb.DBRef dbref = new com.mongodb.DBRef("user", "id"); - when(converter.toDBRef(eq(user), Mockito.any(MongoPersistentProperty.class))).thenReturn(dbref); + user.id = new ObjectId(); PartTree tree = new PartTree("findByCreator", User.class); MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, user), context); - Query query = creator.createQuery(); + DBObject queryObject = creator.createQuery().getQueryObject(); - assertThat(query, is(query(where("creator").is(dbref)))); + assertThat(queryObject.get("creator"), is((Object) user)); } /** @@ -294,8 +287,6 @@ public class MongoQueryCreatorUnitTests { private void assertBindsDistanceToQuery(Point point, Distance distance, Query reference) throws Exception { - when(converter.convertToMongoType("Dave")).thenReturn("Dave"); - PartTree tree = new PartTree("findByLocationNearAndFirstname", org.springframework.data.mongodb.repository.Person.class); Method method = PersonRepository.class.getMethod("findByLocationNearAndFirstname", Point.class, Distance.class, @@ -684,6 +675,8 @@ public class MongoQueryCreatorUnitTests { class User { + ObjectId id; + @Field("foo") String username; @DBRef User creator; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java index 6323417b5..dc61cfbfd 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java @@ -173,7 +173,7 @@ public class PartTreeMongoQueryUnitTests { @Test public void restrictsQueryToFieldsRequiredForDto() { - DBObject fieldsObject = deriveQueryFromMethod("findPersonDtoBy", new Object[0]).getFieldsObject(); + DBObject fieldsObject = deriveQueryFromMethod("findPersonDtoByAge", new Object[] { 42 }).getFieldsObject(); assertThat(fieldsObject.get("firstname"), is((Object) 1)); assertThat(fieldsObject.get("lastname"), is((Object) 1)); @@ -246,7 +246,7 @@ public class PartTreeMongoQueryUnitTests { PersonProjection findPersonProjectedBy(); - PersonDto findPersonDtoBy(); + PersonDto findPersonDtoByAge(Integer age); T findDynamicallyProjectedBy(Class type); } diff --git a/spring-data-mongodb/template.mf b/spring-data-mongodb/template.mf index 570624241..1bad89f75 100644 --- a/spring-data-mongodb/template.mf +++ b/spring-data-mongodb/template.mf @@ -2,6 +2,8 @@ Bundle-SymbolicName: org.springframework.data.mongodb Bundle-Name: Spring Data MongoDB Support Bundle-Vendor: Pivotal Software, Inc. Bundle-ManifestVersion: 2 +Excluded-Imports: + lombok.* Import-Package: sun.reflect;version="0";resolution:=optional Export-Template: