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 d59efb045..ad41e7158 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 @@ -15,25 +15,20 @@ */ package org.springframework.data.mongodb.repository.query; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.convert.EntityInstantiators; +import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithProjection; +import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithQuery; import org.springframework.data.mongodb.core.MongoOperations; 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.CountExecution; import org.springframework.data.mongodb.repository.query.MongoQueryExecution.DeleteExecution; -import org.springframework.data.mongodb.repository.query.MongoQueryExecution.ExistsExecution; 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.repository.query.ResultProcessor; +import org.springframework.data.repository.query.ReturnedType; import org.springframework.util.Assert; /** @@ -48,7 +43,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { private final MongoQueryMethod method; private final MongoOperations operations; - private final EntityInstantiators instantiators; + private final FindOperationWithProjection findOperationWithProjection; /** * Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}. @@ -63,7 +58,12 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { this.method = method; this.operations = operations; - this.instantiators = new EntityInstantiators(); + + ReturnedType returnedType = method.getResultProcessor().getReturnedType(); + + this.findOperationWithProjection = operations// + .query(returnedType.getDomainType())// + .inCollection(method.getEntityInformation().getCollectionName()); } /* @@ -86,52 +86,36 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { applyQueryMetaAttributesWhenPresent(query); ResultProcessor processor = method.getResultProcessor().withDynamicProjection(accessor); - String collection = method.getEntityInformation().getCollectionName(); + ReturnedType returnedType = processor.getReturnedType(); + FindOperationWithQuery find = findOperationWithProjection.as(returnedType.getTypeToRead()); - MongoQueryExecution execution = getExecution(query, accessor, - new ResultProcessingConverter(processor, operations, instantiators)); + MongoQueryExecution execution = getExecution(accessor, find); - return execution.execute(query, processor.getReturnedType().getDomainType(), collection); + return processor.processResult(execution.execute(query)); } - /** - * 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(operations, resultProcessing); - } - - return new ResultProcessingExecution(getExecutionToWrap(query, accessor), resultProcessing); - } - - private MongoQueryExecution getExecutionToWrap(Query query, MongoParameterAccessor accessor) { + private MongoQueryExecution getExecution(MongoParameterAccessor accessor, FindOperationWithQuery operation) { if (isDeleteQuery()) { return new DeleteExecution(operations, method); } else if (method.isGeoNearQuery() && method.isPageQuery()) { - return new PagingGeoNearExecution(operations, accessor, method.getReturnType(), this); + return new PagingGeoNearExecution(operations, method, accessor, this); } else if (method.isGeoNearQuery()) { - return new GeoNearExecution(operations, accessor, method.getReturnType()); + return new GeoNearExecution(operations, method, accessor); } else if (method.isSliceQuery()) { - return new SlicedExecution(operations, accessor.getPageable()); + return new SlicedExecution(operation, accessor.getPageable()); + } else if (method.isStreamQuery()) { + return new StreamExecution(operation); } else if (method.isCollectionQuery()) { - return new CollectionExecution(operations, accessor.getPageable()); + return q -> operation.matching(q.with(accessor.getPageable())).all(); } else if (method.isPageQuery()) { - return new PagedExecution(operations, accessor.getPageable()); + return new PagedExecution(operation, accessor.getPageable()); } else if (isCountQuery()) { - return new CountExecution(operations); + return q -> operation.matching(q).count(); } else if (isExistsQuery()) { - return new ExistsExecution(operations); + return q -> operation.matching(q).exists(); } else { - return new SingleEntityExecution(operations); + return q -> operation.matching(q).oneValue(); } } 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 index f9ac79696..18e8b8cf5 100644 --- 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 @@ -19,11 +19,7 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import java.util.List; -import java.util.Optional; -import java.util.function.Function; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.convert.EntityInstantiators; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Range; @@ -34,16 +30,16 @@ 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.ExecutableFindOperation; +import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithQuery; +import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFindOperation; 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.repository.support.PageableExecutionUtils; 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.client.result.DeleteResult; @@ -58,7 +54,7 @@ import com.mongodb.client.result.DeleteResult; */ interface MongoQueryExecution { - Object execute(Query query, Class type, String collection); + Object execute(Query query); /** * {@link MongoQueryExecution} for collection returning queries. @@ -68,16 +64,16 @@ interface MongoQueryExecution { @RequiredArgsConstructor final class CollectionExecution implements MongoQueryExecution { - private final @NonNull MongoOperations operations; - private final Pageable pageable; + private final @NonNull ExecutableFindOperation.FindOperationWithQuery find; + 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) + * @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query) */ @Override - public Object execute(Query query, Class type, String collection) { - return operations.find(query.with(pageable), type, collection); + public Object execute(Query query) { + return find.matching(query.with(pageable)).all(); } } @@ -89,24 +85,24 @@ interface MongoQueryExecution { * @since 1.5 */ @RequiredArgsConstructor - final class SlicedExecution implements MongoQueryExecution { + static final class SlicedExecution implements MongoQueryExecution { - private final @NonNull MongoOperations operations; + private final @NonNull FindOperationWithQuery find; 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) + * @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query) */ @Override @SuppressWarnings({ "unchecked", "rawtypes" }) - public Object execute(Query query, Class type, String collection) { + public Object execute(Query query) { 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); + List result = find.matching(modifiedQuery).all(); boolean hasNext = result.size() > pageSize; @@ -121,19 +117,21 @@ interface MongoQueryExecution { * @author Mark Paluch */ @RequiredArgsConstructor - final class PagedExecution implements MongoQueryExecution { + static final class PagedExecution implements MongoQueryExecution { - private final @NonNull MongoOperations operations; + private final @NonNull FindOperationWithQuery operation; 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) + * @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query) */ @Override - public Object execute(final Query query, final Class type, final String collection) { + public Object execute(Query query) { - final int overallLimit = query.getLimit(); + int overallLimit = query.getLimit(); + + TerminatingFindOperation matching = operation.matching(query); // Apply raw pagination query.with(pageable); @@ -143,35 +141,14 @@ interface MongoQueryExecution { query.limit((int) (overallLimit - pageable.getOffset())); } - return PageableExecutionUtils.getPage(operations.find(query, type, collection), pageable, () -> { + return PageableExecutionUtils.getPage(matching.all(), pageable, () -> { - long count = operations.count(query, type, collection); + long count = matching.count(); return overallLimit != 0 ? Math.min(count, overallLimit) : count; - }); } } - /** - * {@link MongoQueryExecution} to return a single entity. - * - * @author Oliver Gierke - */ - @RequiredArgsConstructor - final class SingleEntityExecution implements MongoQueryExecution { - - private final MongoOperations operations; - - /* - * (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.findOne(query, type, collection); - } - } - /** * {@link MongoQueryExecution} to perform a count projection. * @@ -182,36 +159,15 @@ interface MongoQueryExecution { @RequiredArgsConstructor static final class CountExecution implements MongoQueryExecution { - private final MongoOperations operations; + private final @NonNull FindOperationWithQuery operation; /* * (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) + * @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query) */ @Override - public Object execute(Query query, Class type, String collection) { - return operations.count(query, type, collection); - } - } - - /** - * {@link MongoQueryExecution} to perform an exists projection. - * - * @author Mark Paluch - * @since 1.10 - */ - @RequiredArgsConstructor - static final class ExistsExecution implements MongoQueryExecution { - - private final MongoOperations operations; - - /* - * (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.exists(query, type, collection); + public Object execute(Query query) { + return operation.count(); } } @@ -221,25 +177,28 @@ interface MongoQueryExecution { * @author Oliver Gierke */ @RequiredArgsConstructor - class GeoNearExecution implements MongoQueryExecution { + static class GeoNearExecution implements MongoQueryExecution { private final MongoOperations operations; + private final MongoQueryMethod method; 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) { + public Object execute(Query query) { - GeoResults results = doExecuteQuery(query, type, collection); - return isListOfGeoResult() ? results.getContent() : results; + GeoResults results = doExecuteQuery(query); + return isListOfGeoResult(method.getReturnType()) ? results.getContent() : results; } @SuppressWarnings("unchecked") - protected GeoResults doExecuteQuery(Query query, Class type, String collection) { + protected GeoResults doExecuteQuery(Query query) { + + Class type = method.getReturnedObjectType(); + String collection = method.getEntityInformation().getCollectionName(); Point nearLocation = accessor.getGeoNearLocation(); NearQuery nearQuery = NearQuery.near(nearLocation); @@ -261,7 +220,7 @@ interface MongoQueryExecution { return (GeoResults) operations.geoNear(nearQuery, type, collection); } - private boolean isListOfGeoResult() { + private static boolean isListOfGeoResult(TypeInformation returnType) { if (!returnType.getType().equals(List.class)) { return false; @@ -278,20 +237,22 @@ interface MongoQueryExecution { * @author Oliver Gierke * @author Mark Paluch */ - final class PagingGeoNearExecution extends GeoNearExecution { + static final class PagingGeoNearExecution extends GeoNearExecution { private final MongoOperations operations; + private final MongoQueryMethod method; private final MongoParameterAccessor accessor; private final AbstractMongoQuery mongoQuery; - public PagingGeoNearExecution(MongoOperations operations, MongoParameterAccessor accessor, - TypeInformation returnType, AbstractMongoQuery query) { + public PagingGeoNearExecution(MongoOperations operations, MongoQueryMethod method, MongoParameterAccessor accessor, + AbstractMongoQuery query) { - super(operations, accessor, returnType); + super(operations, method, accessor); this.accessor = accessor; this.operations = operations; this.mongoQuery = query; + this.method = method; } /* @@ -299,19 +260,20 @@ interface MongoQueryExecution { * @see org.springframework.data.mongodb.repository.query.MongoQueryExecution.GeoNearExecution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String) */ @Override - public Object execute(Query query, Class type, final String collection) { + public Object execute(Query query) { - GeoResults geoResults = doExecuteQuery(query, type, collection); + String collectionName = method.getEntityInformation().getCollectionName(); + + GeoResults geoResults = doExecuteQuery(query); Page> page = PageableExecutionUtils.getPage(geoResults.getContent(), accessor.getPageable(), () -> { - ConvertingParameterAccessor parameterAccessor = new ConvertingParameterAccessor(operations.getConverter(), - accessor); Query countQuery = mongoQuery - .applyQueryMetaAttributesWhenPresent(mongoQuery.createCountQuery(parameterAccessor)); + .createCountQuery(new ConvertingParameterAccessor(operations.getConverter(), accessor)); + countQuery = mongoQuery.applyQueryMetaAttributesWhenPresent(countQuery); - return operations.count(countQuery, collection); + return operations.count(countQuery, collectionName); }); @@ -326,23 +288,26 @@ interface MongoQueryExecution { * @since 1.5 */ @RequiredArgsConstructor - final class DeleteExecution implements MongoQueryExecution { + 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) + * @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query) */ @Override - public Object execute(Query query, Class type, String collection) { + public Object execute(Query query) { + + String collectionName = method.getEntityInformation().getCollectionName(); + Class type = method.getEntityInformation().getJavaType(); if (method.isCollectionQuery()) { - return operations.findAllAndRemove(query, type, collection); + return operations.findAllAndRemove(query, type, collectionName); } - DeleteResult writeResult = operations.remove(query, type, collection); + DeleteResult writeResult = operations.remove(query, type, collectionName); return writeResult != null ? writeResult.getDeletedCount() : 0L; } } @@ -352,82 +317,21 @@ interface MongoQueryExecution { * @since 1.7 */ @RequiredArgsConstructor - final class StreamExecution implements MongoQueryExecution { + static final class StreamExecution implements MongoQueryExecution { - private final @NonNull MongoOperations operations; - private final @NonNull Converter resultProcessing; + private final @NonNull FindOperationWithQuery operation; /* * (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) + * @see org.springframework.data.mongodb.repository.query.MongoQueryExecution#execute(org.springframework.data.mongodb.core.query.Query) */ @Override @SuppressWarnings("unchecked") - public Object execute(Query query, Class type, String collection) { + public Object execute(Query query) { - return StreamUtils.createStreamFromIterator((CloseableIterator) operations.stream(query, type)) - .map(new Function() { + TerminatingFindOperation matching = operation.matching(query); - @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 - 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 - 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); + return StreamUtils.createStreamFromIterator((CloseableIterator) matching.stream()); } } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java index 044d19f9c..be12b8ae5 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java @@ -17,6 +17,8 @@ package org.springframework.data.mongodb.repository.query; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.lang.reflect.Method; @@ -24,12 +26,10 @@ import java.util.List; import java.util.Optional; import org.bson.Document; -import org.hamcrest.core.Is; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; @@ -39,6 +39,9 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperation; +import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithProjection; +import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithQuery; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.Person; import org.springframework.data.mongodb.core.convert.DbRefResolver; @@ -69,6 +72,9 @@ import com.mongodb.client.result.DeleteResult; public class AbstractMongoQueryUnitTests { @Mock MongoOperations mongoOperationsMock; + @Mock FindOperation findOperationMock; + @Mock FindOperationWithProjection withProjectionMock; + @Mock FindOperationWithQuery withQueryMock; @Mock BasicMongoPersistentEntity persitentEntityMock; @Mock MongoMappingContext mappingContextMock; @Mock WriteResult writeResultMock; @@ -87,26 +93,27 @@ public class AbstractMongoQueryUnitTests { converter.afterPropertiesSet(); doReturn(converter).when(mongoOperationsMock).getConverter(); + doReturn(findOperationMock).when(mongoOperationsMock).query(any()); + doReturn(withProjectionMock).when(findOperationMock).inCollection(any()); + doReturn(withQueryMock).when(withProjectionMock).as(any()); + doReturn(withQueryMock).when(withQueryMock).matching(any()); } - @SuppressWarnings("unchecked") @Test // DATAMONGO-566 public void testDeleteExecutionCallsRemoveCorreclty() { createQueryForMethod("deletePersonByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" }); - verify(mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), eq(Person.class), eq("persons")); - verify(mongoOperationsMock, times(0)).find(Matchers.any(Query.class), Matchers.any(Class.class), - Matchers.anyString()); + verify(mongoOperationsMock, times(1)).remove(any(), eq(Person.class), eq("persons")); + verify(mongoOperationsMock, times(0)).find(any(), any(), any()); } - @SuppressWarnings("unchecked") @Test // DATAMONGO-566, DATAMONGO-1040 public void testDeleteExecutionLoadsListOfRemovedDocumentsWhenReturnTypeIsCollectionLike() { createQueryForMethod("deleteByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" }); - verify(mongoOperationsMock, times(1)).findAllAndRemove(Mockito.any(Query.class), eq(Person.class), eq("persons")); + verify(mongoOperationsMock, times(1)).findAllAndRemove(any(), eq(Person.class), eq("persons")); } @Test // DATAMONGO-566 @@ -115,21 +122,20 @@ public class AbstractMongoQueryUnitTests { MongoQueryFake query = createQueryForMethod("deletePersonByLastname", String.class); query.setDeleteQuery(true); - assertThat(query.execute(new Object[] { "fake" }), Is. is(0L)); + assertThat(query.execute(new Object[] { "fake" }), is(0L)); } @Test // DATAMONGO-566, DATAMONGO-978 public void testDeleteExecutionReturnsNrDocumentsDeletedFromWriteResult() { when(deleteResultMock.getDeletedCount()).thenReturn(100L); - when(mongoOperationsMock.remove(Matchers.any(Query.class), eq(Person.class), eq("persons"))) - .thenReturn(deleteResultMock); + when(mongoOperationsMock.remove(any(), eq(Person.class), eq("persons"))).thenReturn(deleteResultMock); MongoQueryFake query = createQueryForMethod("deletePersonByLastname", String.class); query.setDeleteQuery(true); assertThat(query.execute(new Object[] { "fake" }), is((Object) 100L)); - verify(mongoOperationsMock, times(1)).remove(Matchers.any(Query.class), eq(Person.class), eq("persons")); + verify(mongoOperationsMock, times(1)).remove(any(), eq(Person.class), eq("persons")); } @Test // DATAMONGO-957 @@ -140,7 +146,9 @@ public class AbstractMongoQueryUnitTests { ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons")); + verify(withProjectionMock).as(Person.class); + verify(withQueryMock).matching(captor.capture()); + verify(findOperationMock).inCollection("persons"); assertThat(captor.getValue().getMeta().getComment(), nullValue()); } @@ -153,7 +161,10 @@ public class AbstractMongoQueryUnitTests { ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(this.mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons")); + verify(withProjectionMock).as(Person.class); + verify(withQueryMock).matching(captor.capture()); + verify(findOperationMock).inCollection("persons"); + assertThat(captor.getValue().getMeta().getComment(), is("comment")); } @@ -165,7 +176,10 @@ public class AbstractMongoQueryUnitTests { ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(mongoOperationsMock, times(1)).count(captor.capture(), eq(Person.class), eq("persons")); + verify(withProjectionMock).as(Person.class); + verify(withQueryMock).matching(captor.capture()); + verify(findOperationMock).inCollection("persons"); + assertThat(captor.getValue().getMeta().getComment(), is("comment")); } @@ -177,7 +191,10 @@ public class AbstractMongoQueryUnitTests { ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(this.mongoOperationsMock, times(1)).find(captor.capture(), eq(Person.class), eq("persons")); + verify(withProjectionMock).as(Person.class); + verify(withQueryMock).matching(captor.capture()); + verify(findOperationMock).inCollection("persons"); + assertThat(captor.getValue().getMeta().getComment(), is("comment")); } @@ -193,7 +210,9 @@ public class AbstractMongoQueryUnitTests { ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons")); + verify(withProjectionMock, times(2)).as(Person.class); + verify(withQueryMock, times(2)).matching(captor.capture()); + verify(findOperationMock).inCollection("persons"); assertThat(captor.getAllValues().get(0).getSkip(), is(0L)); assertThat(captor.getAllValues().get(1).getSkip(), is(10L)); @@ -211,7 +230,9 @@ public class AbstractMongoQueryUnitTests { ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons")); + verify(withProjectionMock, times(2)).as(Person.class); + verify(withQueryMock, times(2)).matching(captor.capture()); + verify(findOperationMock).inCollection("persons"); assertThat(captor.getAllValues().get(0).getLimit(), is(11)); assertThat(captor.getAllValues().get(1).getLimit(), is(11)); @@ -229,7 +250,9 @@ public class AbstractMongoQueryUnitTests { ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(mongoOperationsMock, times(2)).find(captor.capture(), eq(Person.class), eq("persons")); + verify(withProjectionMock, times(2)).as(Person.class); + verify(withQueryMock, times(2)).matching(captor.capture()); + verify(findOperationMock).inCollection("persons"); Document expectedSortObject = new Document().append("bar", -1); assertThat(captor.getAllValues().get(0).getSortObject(), is(expectedSortObject)); @@ -240,8 +263,8 @@ public class AbstractMongoQueryUnitTests { public void doesNotTryToPostProcessQueryResultIntoWrapperType() { Person reference = new Person(); - when(mongoOperationsMock.findOne(Mockito.any(Query.class), eq(Person.class), eq("persons"))).// - thenReturn(reference); + + doReturn(reference).when(withQueryMock).oneValue(); AbstractMongoQuery query = createQueryForMethod("findByLastname", String.class); @@ -269,7 +292,6 @@ public class AbstractMongoQueryUnitTests { private static class MongoQueryFake extends AbstractMongoQuery { private boolean isCountQuery; - private boolean isExistsQuery; private boolean isDeleteQuery; public MongoQueryFake(MongoQueryMethod method, MongoOperations operations) { @@ -310,7 +332,7 @@ public class AbstractMongoQueryUnitTests { List findByFirstname(String firstname); - @Meta(comment = "comment", flags = {org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT}) + @Meta(comment = "comment", flags = { org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT }) Page findByFirstname(String firstnanme, Pageable pageable); @Meta(comment = "comment") diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java index 54ef9ed59..033be0ff0 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java @@ -15,8 +15,7 @@ */ package org.springframework.data.mongodb.repository.query; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.lang.reflect.Method; @@ -36,6 +35,9 @@ import org.springframework.data.geo.GeoResult; import org.springframework.data.geo.GeoResults; import org.springframework.data.geo.Metrics; import org.springframework.data.geo.Point; +import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperation; +import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperationWithQuery; +import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFindOperation; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.convert.DbRefResolver; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; @@ -50,7 +52,6 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.util.ClassTypeInformation; import org.springframework.util.ReflectionUtils; /** @@ -64,6 +65,8 @@ import org.springframework.util.ReflectionUtils; public class MongoQueryExecutionUnitTests { @Mock MongoOperations mongoOperationsMock; + @Mock FindOperation findOperationMock; + @Mock FindOperationWithQuery operationMock; @Mock DbRefResolver dbRefResolver; Point POINT = new Point(10, 20); @@ -79,46 +82,54 @@ public class MongoQueryExecutionUnitTests { public void setUp() throws Exception { MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, context); + when(mongoOperationsMock.getConverter()).thenReturn(converter); + when(mongoOperationsMock.query(any(Class.class))).thenReturn(findOperationMock); } @Test // DATAMONGO-1464 public void pagedExecutionShouldNotGenerateCountQueryIfQueryReportedNoResults() { - when(mongoOperationsMock.find(any(Query.class), eq(Person.class), eq("person"))) - .thenReturn(Collections. emptyList()); + TerminatingFindOperation terminating = mock(TerminatingFindOperation.class); - PagedExecution execution = new PagedExecution(mongoOperationsMock, PageRequest.of(0, 10)); - execution.execute(new Query(), Person.class, "person"); + doReturn(terminating).when(operationMock).matching(any(Query.class)); + doReturn(Collections.emptyList()).when(terminating).all(); - verify(mongoOperationsMock).find(any(Query.class), eq(Person.class), eq("person")); - verify(mongoOperationsMock, never()).count(any(Query.class), eq(Person.class), eq("person")); + PagedExecution execution = new PagedExecution(operationMock, PageRequest.of(0, 10)); + execution.execute(new Query()); + + verify(terminating).all(); + verify(terminating, never()).count(); } @Test // DATAMONGO-1464 public void pagedExecutionShouldUseCountFromResultWithOffsetAndResultsWithinPageSize() { - when(mongoOperationsMock.find(any(Query.class), eq(Person.class), eq("person"))) - .thenReturn(Arrays.asList(new Person(), new Person(), new Person(), new Person())); + TerminatingFindOperation terminating = mock(TerminatingFindOperation.class); - PagedExecution execution = new PagedExecution(mongoOperationsMock, PageRequest.of(0, 10)); - execution.execute(new Query(), Person.class, "person"); + doReturn(terminating).when(operationMock).matching(any(Query.class)); + doReturn(Arrays.asList(new Person(), new Person(), new Person(), new Person())).when(terminating).all(); - verify(mongoOperationsMock).find(any(Query.class), eq(Person.class), eq("person")); - verify(mongoOperationsMock, never()).count(any(Query.class), eq(Person.class), eq("person")); + PagedExecution execution = new PagedExecution(operationMock, PageRequest.of(0, 10)); + execution.execute(new Query()); + + verify(terminating).all(); + verify(terminating, never()).count(); } @Test // DATAMONGO-1464 public void pagedExecutionRetrievesObjectsForPageableOutOfRange() throws Exception { - when(mongoOperationsMock.find(any(Query.class), eq(Person.class), eq("person"))) - .thenReturn(Collections. emptyList()); + TerminatingFindOperation terminating = mock(TerminatingFindOperation.class); - PagedExecution execution = new PagedExecution(mongoOperationsMock, PageRequest.of(2, 10)); - execution.execute(new Query(), Person.class, "person"); + doReturn(terminating).when(operationMock).matching(any(Query.class)); + doReturn(Collections.emptyList()).when(terminating).all(); - verify(mongoOperationsMock).find(any(Query.class), eq(Person.class), eq("person")); - verify(mongoOperationsMock).count(any(Query.class), eq(Person.class), eq("person")); + PagedExecution execution = new PagedExecution(operationMock, PageRequest.of(2, 10)); + execution.execute(new Query()); + + verify(terminating).all(); + verify(terminating).count(); } @Test // DATAMONGO-1464 @@ -133,9 +144,8 @@ public class MongoQueryExecutionUnitTests { when(mongoOperationsMock.geoNear(any(NearQuery.class), eq(Person.class), eq("person"))) .thenReturn(new GeoResults(Arrays.asList(result, result, result, result))); - PagingGeoNearExecution execution = new PagingGeoNearExecution(mongoOperationsMock, accessor, - ClassTypeInformation.fromReturnTypeOf(method), query); - execution.execute(new Query(), Person.class, "person"); + PagingGeoNearExecution execution = new PagingGeoNearExecution(mongoOperationsMock, queryMethod, accessor, query); + execution.execute(new Query()); verify(mongoOperationsMock).geoNear(any(NearQuery.class), eq(Person.class), eq("person")); verify(mongoOperationsMock, never()).count(any(Query.class), eq("person")); @@ -152,9 +162,8 @@ public class MongoQueryExecutionUnitTests { when(mongoOperationsMock.geoNear(any(NearQuery.class), eq(Person.class), eq("person"))) .thenReturn(new GeoResults(Collections.> emptyList())); - PagingGeoNearExecution execution = new PagingGeoNearExecution(mongoOperationsMock, accessor, - ClassTypeInformation.fromReturnTypeOf(method), query); - execution.execute(new Query(), Person.class, "person"); + PagingGeoNearExecution execution = new PagingGeoNearExecution(mongoOperationsMock, queryMethod, accessor, query); + execution.execute(new Query()); verify(mongoOperationsMock).geoNear(any(NearQuery.class), eq(Person.class), eq("person")); verify(mongoOperationsMock).count(any(Query.class), eq("person")); 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 15bea55e7..c26f99fad 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 @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.springframework.data.mongodb.core.query.IsTextQuery.*; @@ -33,6 +34,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperation; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.convert.DbRefResolver; import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; @@ -63,6 +65,7 @@ import com.mongodb.util.JSONParseException; public class PartTreeMongoQueryUnitTests { @Mock MongoOperations mongoOperationsMock; + @Mock FindOperation findOperationMock; MongoMappingContext mappingContext; @@ -75,7 +78,8 @@ public class PartTreeMongoQueryUnitTests { DbRefResolver dbRefResolver = new DefaultDbRefResolver(mock(MongoDbFactory.class)); MongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext); - when(mongoOperationsMock.getConverter()).thenReturn(converter); + doReturn(converter).when(mongoOperationsMock).getConverter(); + doReturn(findOperationMock).when(mongoOperationsMock).query(any()); } @Test // DATAMOGO-952 diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java index ca59d91f2..fcedd832d 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.lang.reflect.Method; @@ -36,6 +37,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.data.mongodb.core.DocumentTestUtils; +import org.springframework.data.mongodb.core.ExecutableFindOperation.FindOperation; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.convert.DbRefResolver; import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper; @@ -67,6 +69,7 @@ public class StringBasedMongoQueryUnitTests { SpelExpressionParser PARSER = new SpelExpressionParser(); @Mock MongoOperations operations; + @Mock FindOperation findOperation; @Mock DbRefResolver factory; MongoConverter converter; @@ -75,6 +78,8 @@ public class StringBasedMongoQueryUnitTests { public void setUp() { this.converter = new MappingMongoConverter(factory, new MongoMappingContext()); + + doReturn(findOperation).when(operations).query(any()); } @Test