Browse Source

DATAMONGO-1865 - Avoid IncorrectResultSizeDataAccessException for derived findFirst/findTop queries.

We now return the first result when executing findFirst/findTop queries. This fixes a glitch introduced in the Kay release throwing IncorrectResultSizeDataAccessException for single entity executions returning more than one result, which is explicitly not the desired behavior in this case.

Original pull request: #530.
pull/534/head
Christoph Strobl 8 years ago committed by Mark Paluch
parent
commit
0ec840e34a
  1. 15
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
  2. 50
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java
  3. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java
  4. 35
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java
  5. 15
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java
  6. 15
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java
  7. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java
  8. 16
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
  9. 10
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java
  10. 14
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java
  11. 26
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java
  12. 12
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java
  13. 8
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java
  14. 11
      src/main/asciidoc/reference/mongo-repositories.adoc
  15. 15
      src/main/asciidoc/reference/reactive-mongo-repositories.adoc

15
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java

@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query; @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query;
import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery;
import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.DeleteExecution;
@ -117,7 +118,11 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -117,7 +118,11 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
} else if (isExistsQuery()) {
return q -> operation.matching(q).exists();
} else {
return q -> operation.matching(q).oneValue();
return q -> {
TerminatingFind<?> find = operation.matching(q);
return isLimiting() ? find.firstValue() : find.oneValue();
};
}
}
@ -172,4 +177,12 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { @@ -172,4 +177,12 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
* @since 1.5
*/
protected abstract boolean isDeleteQuery();
/**
* Return weather the query has an explicit limit set.
*
* @return
* @since 2.0.4
*/
protected abstract boolean isLimiting();
}

50
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java

@ -22,18 +22,20 @@ import org.reactivestreams.Publisher; @@ -22,18 +22,20 @@ import org.reactivestreams.Publisher;
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.ReactiveFindOperation.FindWithProjection;
import org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithQuery;
import org.springframework.data.mongodb.core.ReactiveFindOperation.TerminatingFind;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.CollectionExecution;
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.DeleteExecution;
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.GeoNearExecution;
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.ResultProcessingConverter;
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.ResultProcessingExecution;
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.SingleEntityExecution;
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.TailExecution;
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,6 +50,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { @@ -48,6 +50,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
private final ReactiveMongoQueryMethod method;
private final ReactiveMongoOperations operations;
private final EntityInstantiators instantiators;
private final FindWithProjection<?> findOperationWithProjection;
/**
* Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
@ -64,6 +67,12 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { @@ -64,6 +67,12 @@ public abstract class AbstractReactiveMongoQuery 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());
}
/*
@ -103,10 +112,16 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { @@ -103,10 +112,16 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
applyQueryMetaAttributesWhenPresent(query);
ResultProcessor processor = method.getResultProcessor().withDynamicProjection(parameterAccessor);
Class<?> typeToRead = processor.getReturnedType().getTypeToRead();
FindWithQuery<?> find = typeToRead == null //
? findOperationWithProjection //
: findOperationWithProjection.as(typeToRead);
String collection = method.getEntityInformation().getCollectionName();
ReactiveMongoQueryExecution execution = getExecution(query, parameterAccessor,
new ResultProcessingConverter(processor, operations, instantiators));
new ResultProcessingConverter(processor, operations, instantiators), find);
return execution.execute(query, processor.getReturnedType().getDomainType(), collection);
}
@ -120,11 +135,11 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { @@ -120,11 +135,11 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
* @return
*/
private ReactiveMongoQueryExecution getExecution(Query query, MongoParameterAccessor accessor,
Converter<Object, Object> resultProcessing) {
return new ResultProcessingExecution(getExecutionToWrap(accessor), resultProcessing);
Converter<Object, Object> resultProcessing, FindWithQuery<?> operation) {
return new ResultProcessingExecution(getExecutionToWrap(accessor, operation), resultProcessing);
}
private ReactiveMongoQueryExecution getExecutionToWrap(MongoParameterAccessor accessor) {
private ReactiveMongoQueryExecution getExecutionToWrap(MongoParameterAccessor accessor, FindWithQuery<?> operation) {
if (isDeleteQuery()) {
return new DeleteExecution(operations, method);
@ -133,9 +148,20 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { @@ -133,9 +148,20 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
} else if (isTailable(method)) {
return new TailExecution(operations, accessor.getPageable());
} else if (method.isCollectionQuery()) {
return new CollectionExecution(operations, accessor.getPageable());
return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all();
} else if (isCountQuery()) {
return (q, t, c) -> operation.matching(q).count();
} else {
return new SingleEntityExecution(operations, isCountQuery());
return (q, t, c) -> {
TerminatingFind<?> find = operation.matching(q);
if (isCountQuery()) {
return find.count();
}
return isLimiting() ? find.first() : find.one();
};
}
}
@ -186,4 +212,12 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { @@ -186,4 +212,12 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
* @since 1.5
*/
protected abstract boolean isDeleteQuery();
/**
* Return weather the query has an explicit limit set.
*
* @return
* @since 2.0.4
*/
protected abstract boolean isLimiting();
}

9
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java

@ -160,4 +160,13 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { @@ -160,4 +160,13 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
protected boolean isDeleteQuery() {
return tree.isDelete();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#isLimiting()
*/
@Override
protected boolean isLimiting() {
return tree.isLimiting();
}
}

35
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java

@ -44,29 +44,13 @@ import com.mongodb.client.result.DeleteResult; @@ -44,29 +44,13 @@ import com.mongodb.client.result.DeleteResult;
* various flavors.
*
* @author Mark Paluch
* @author Christoph Strobl
* @since 2.0
*/
interface ReactiveMongoQueryExecution {
Object execute(Query query, Class<?> type, String collection);
/**
* {@link ReactiveMongoQueryExecution} for collection returning queries.
*
* @author Mark Paluch
*/
@RequiredArgsConstructor
final class CollectionExecution implements ReactiveMongoQueryExecution {
private final @NonNull ReactiveMongoOperations operations;
private final Pageable pageable;
@Override
public Object execute(Query query, Class<?> type, String collection) {
return operations.find(query.with(pageable), type, collection);
}
}
/**
* {@link ReactiveMongoQueryExecution} for collection returning queries using tailable cursors.
*
@ -84,23 +68,6 @@ interface ReactiveMongoQueryExecution { @@ -84,23 +68,6 @@ interface ReactiveMongoQueryExecution {
}
}
/**
* {@link ReactiveMongoQueryExecution} to return a single entity.
*
* @author Mark Paluch
*/
@RequiredArgsConstructor
final class SingleEntityExecution implements ReactiveMongoQueryExecution {
private final ReactiveMongoOperations operations;
private final boolean countProjection;
@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.
*

15
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java

@ -118,7 +118,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { @@ -118,7 +118,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#createCountQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor)
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#createCountQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor)
*/
@Override
protected Query createCountQuery(ConvertingParameterAccessor accessor) {
@ -127,7 +127,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { @@ -127,7 +127,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#isCountQuery()
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#isCountQuery()
*/
@Override
protected boolean isCountQuery() {
@ -136,10 +136,19 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { @@ -136,10 +136,19 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#isDeleteQuery()
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#isDeleteQuery()
*/
@Override
protected boolean isDeleteQuery() {
return tree.isDelete();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#isLimiting()
*/
@Override
protected boolean isLimiting() {
return tree.isLimiting();
}
}

15
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java

@ -104,7 +104,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { @@ -104,7 +104,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#createQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor)
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#createQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor)
*/
@Override
protected Query createQuery(ConvertingParameterAccessor accessor) {
@ -125,7 +125,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { @@ -125,7 +125,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#isCountQuery()
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#isCountQuery()
*/
@Override
protected boolean isCountQuery() {
@ -134,11 +134,20 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { @@ -134,11 +134,20 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#isDeleteQuery()
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#isDeleteQuery()
*/
@Override
protected boolean isDeleteQuery() {
return this.isDeleteQuery;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#isLimiting()
*/
@Override
protected boolean isLimiting() {
return false;
}
}

9
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java

@ -174,6 +174,15 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { @@ -174,6 +174,15 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
return countBooleanValues(isCountQuery, isExistsQuery, isDeleteQuery) > 1;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#isLimiting()
*/
@Override
protected boolean isLimiting() {
return false;
}
private static int countBooleanValues(boolean... values) {
int count = 0;

16
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java

@ -38,6 +38,7 @@ import org.junit.rules.ExpectedException; @@ -38,6 +38,7 @@ import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@ -1177,4 +1178,19 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -1177,4 +1178,19 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
public void readsClosedProjection() {
assertThat(repository.findClosedProjectionBy()).isNotEmpty();
}
@Test // DATAMONGO-1865
public void findFirstEntityReturnsFirstResultEvenForNonUniqueMatches() {
repository.findFirstBy();
}
@Test(expected = IncorrectResultSizeDataAccessException.class) // DATAMONGO-1865
public void findSingleEntityThrowsErrorWhenNotUnique() {
repository.findPersonByLastnameLike(dave.getLastname());
}
@Test(expected = IncorrectResultSizeDataAccessException.class) // DATAMONGO-1865
public void findOptionalSingleEntityThrowsErrorWhenNotUnique() {
repository.findOptionalPersonByLastnameLike(dave.getLastname());
}
}

10
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java

@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository; @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.data.domain.Page;
@ -286,6 +287,15 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query @@ -286,6 +287,15 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
// DATAMONGO-950
Page<Person> findTop3ByLastnameStartingWith(String lastname, Pageable pageRequest);
// DATAMONGO-1865
Person findFirstBy(); // limits to 1 result if more, just return the first one
// DATAMONGO-1865
Person findPersonByLastnameLike(String firstname); // single person, error if more than one
// DATAMONGO-1865
Optional<Person> findOptionalPersonByLastnameLike(String firstname); // optional still, error when more than one
// DATAMONGO-1030
PersonSummaryDto findSummaryByLastname(String lastname);

14
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java

@ -39,6 +39,7 @@ import org.springframework.beans.factory.BeanClassLoaderAware; @@ -39,6 +39,7 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@ -300,6 +301,17 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF @@ -300,6 +301,17 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF
.verifyComplete();
}
@Test // DATAMONGO-1865
public void shouldErrorOnFindOneWithNonUniqueResult() {
StepVerifier.create(repository.findOneByLastname(dave.getLastname()))
.expectError(IncorrectResultSizeDataAccessException.class).verify();
}
@Test // DATAMONGO-1865
public void shouldReturnFirstFindFirstWithMoreResults() {
StepVerifier.create(repository.findFirstByLastname(dave.getLastname())).expectNextCount(1).verifyComplete();
}
interface ReactivePersonRepository extends ReactiveMongoRepository<Person, String> {
Flux<Person> findByLastname(String lastname);
@ -326,6 +338,8 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF @@ -326,6 +338,8 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF
Flux<GeoResult<Person>> findByLocationNear(Point point, Distance maxDistance, Pageable pageable);
Flux<Person> findPersonByLocationNear(Point point, Distance maxDistance);
Mono<Person> findFirstByLastname(String lastname);
}
interface ReactiveCappedCollectionRepository extends Repository<Capped, String> {

26
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java

@ -261,6 +261,18 @@ public class AbstractMongoQueryUnitTests { @@ -261,6 +261,18 @@ public class AbstractMongoQueryUnitTests {
assertThat(query.execute(new Object[] { "lastname" }), is(reference));
}
@Test // DATAMONGO-1865
public void limitingSingleEntityQueryCallsFirst() {
Person reference = new Person();
doReturn(reference).when(withQueryMock).firstValue();
AbstractMongoQuery query = createQueryForMethod("findFirstByLastname", String.class).setLimitingQuery(true);
assertThat(query.execute(new Object[] { "lastname" }), is(reference));
}
@Test // DATAMONGO-1872
public void doesNotFixCollectionOnPreparation() {
@ -294,6 +306,7 @@ public class AbstractMongoQueryUnitTests { @@ -294,6 +306,7 @@ public class AbstractMongoQueryUnitTests {
private static class MongoQueryFake extends AbstractMongoQuery {
private boolean isDeleteQuery;
private boolean isLimitingQuery;
public MongoQueryFake(MongoQueryMethod method, MongoOperations operations) {
super(method, operations);
@ -319,10 +332,21 @@ public class AbstractMongoQueryUnitTests { @@ -319,10 +332,21 @@ public class AbstractMongoQueryUnitTests {
return isDeleteQuery;
}
@Override
protected boolean isLimiting() {
return isLimitingQuery;
}
public MongoQueryFake setDeleteQuery(boolean isDeleteQuery) {
this.isDeleteQuery = isDeleteQuery;
return this;
}
public MongoQueryFake setLimitingQuery(boolean limitingQuery) {
isLimitingQuery = limitingQuery;
return this;
}
}
private interface Repo extends MongoRepository<Person, Long> {
@ -344,6 +368,8 @@ public class AbstractMongoQueryUnitTests { @@ -344,6 +368,8 @@ public class AbstractMongoQueryUnitTests {
Slice<Person> findByLastname(String lastname, Pageable page);
Optional<Person> findByLastname(String lastname);
Person findFirstByLastname(String lastname);
}
// DATAMONGO-1872

12
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java

@ -185,6 +185,16 @@ public class PartTreeMongoQueryUnitTests { @@ -185,6 +185,16 @@ public class PartTreeMongoQueryUnitTests {
assertThat(query.getFieldsObject(), is(new Document()));
}
@Test // DATAMONGO-1865
public void limitingReturnsTrueIfTreeIsLimiting() {
assertThat(createQueryForMethod("findFirstBy").isLimiting(), is(true));
}
@Test // DATAMONGO-1865
public void limitingReturnsFalseIfTreeIsNotLimiting() {
assertThat(createQueryForMethod("findPersonBy").isLimiting(), is(false));
}
private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object... args) {
Class<?>[] types = new Class<?>[args.length];
@ -245,6 +255,8 @@ public class PartTreeMongoQueryUnitTests { @@ -245,6 +255,8 @@ public class PartTreeMongoQueryUnitTests {
List<Person> findBySex(Sex sex);
OpenProjection findAllBy();
Person findFirstBy();
}
interface PersonProjection {

8
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java

@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.query; @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.query;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.any;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -35,6 +36,7 @@ import org.junit.Test; @@ -35,6 +36,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.mongodb.core.ReactiveFindOperation.ReactiveFind;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
@ -56,6 +58,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -56,6 +58,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
* Unit tests for {@link ReactiveStringBasedMongoQuery}.
*
* @author Mark Paluch
* @author Christoph Strobl
*/
@RunWith(MockitoJUnitRunner.class)
public class ReactiveStringBasedMongoQueryUnitTests {
@ -64,11 +67,16 @@ public class ReactiveStringBasedMongoQueryUnitTests { @@ -64,11 +67,16 @@ public class ReactiveStringBasedMongoQueryUnitTests {
@Mock ReactiveMongoOperations operations;
@Mock DbRefResolver factory;
@Mock ReactiveFind reactiveFind;
MongoConverter converter;
@Before
public void setUp() {
when(operations.query(any())).thenReturn(reactiveFind);
when(reactiveFind.inCollection(anyString())).thenReturn(reactiveFind);
this.converter = new MappingMongoConverter(factory, new MongoMappingContext());
}

11
src/main/asciidoc/reference/mongo-repositories.adoc

@ -140,17 +140,18 @@ public interface PersonRepository extends PagingAndSortingRepository<Person, Str @@ -140,17 +140,18 @@ public interface PersonRepository extends PagingAndSortingRepository<Person, Str
Person findByShippingAddresses(Address address); <3>
Stream<Person> findAllBy(); <4>
Person findFirstByLastname(String lastname) <4>
Stream<Person> findAllBy(); <5>
}
----
<1> The method shows a query for all people with the given lastname. The query will be derived parsing the method name for constraints which can be concatenated with `And` and `Or`. Thus the method name will result in a query expression of `{"lastname" : lastname}`.
<2> Applies pagination to a query. Just equip your method signature with a `Pageable` parameter and let the method return a `Page` instance and we will automatically page the query accordingly.
<3> Shows that you can query based on properties which are not a primitive type.
<4> Uses a Java 8 `Stream` which reads and converts individual elements while iterating the stream.
<3> Shows that you can query based on properties which are not a primitive type. Errors with `IncorrectResultSizeDataAccessException` if more than one match found.
<4> Uses the `First` keyword to restrict the query to the very first result. Unlike <3> this method does not error if more than one match found.
<5> Uses a Java 8 `Stream` which reads and converts individual elements while iterating the stream.
====
NOTE: Note that for version 1.0 we currently don't support referring to parameters that are mapped as `DBRef` in the domain class.
[cols="1,2,3", options="header"]

15
src/main/asciidoc/reference/reactive-mongo-repositories.adoc

@ -52,15 +52,22 @@ We have a quite simple domain object here. Note that it has a property named `id @@ -52,15 +52,22 @@ We have a quite simple domain object here. Note that it has a property named `id
----
public interface ReactivePersonRepository extends ReactiveSortingRepository<Person, Long> {
Flux<Person> findByFirstname(String firstname);
Flux<Person> findByFirstname(String firstname); <1>
Flux<Person> findByFirstname(Publisher<String> firstname); <2>
Flux<Person> findByFirstname(Publisher<String> firstname);
Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable); <3>
Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable);
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname); <4>
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname);
Mono<Person> findFirstByLastname(String lastname); <5>
}
----
<1> The method shows a query for all people with the given lastname. The query will be derived parsing the method name for constraints which can be concatenated with `And` and `Or`. Thus the method name will result in a query expression of `{"lastname" : lastname}`.
<2> The method shows a query for all people with the given firstname once the firstname becomes available via the given `Publisher`.
<3> Use `Pageable` to pass on offset and sorting parameters to the database.
<4> Find a single entity for given criteria. Errors on non unique results.
<5> Unless <4> the first entity is returned no matter what.
====
For JavaConfig use the `@EnableReactiveMongoRepositories` annotation. The annotation carries the very same attributes like the namespace element. If no base package is configured the infrastructure will scan the package of the annotated configuration class.

Loading…
Cancel
Save