diff --git a/src/main/java/org/springframework/data/repository/query/QueryMethod.java b/src/main/java/org/springframework/data/repository/query/QueryMethod.java index c00fbf48d..b80b4f4ce 100644 --- a/src/main/java/org/springframework/data/repository/query/QueryMethod.java +++ b/src/main/java/org/springframework/data/repository/query/QueryMethod.java @@ -26,6 +26,8 @@ import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; import org.springframework.data.repository.core.EntityMetadata; import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.util.QueryExecutionConverters; +import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.ReflectionUtils; import org.springframework.util.Assert; @@ -40,6 +42,7 @@ public class QueryMethod { private final RepositoryMetadata metadata; private final Method method; + private final Class unwrappedReturnType; private final Parameters parameters; private Class domainClass; @@ -64,6 +67,7 @@ public class QueryMethod { } this.method = method; + this.unwrappedReturnType = potentiallyUnwrapReturnTypeFor(method); this.parameters = createParameters(method); this.metadata = metadata; @@ -140,8 +144,8 @@ public class QueryMethod { Class repositoryDomainClass = metadata.getDomainType(); Class methodDomainClass = metadata.getReturnedDomainClass(method); - this.domainClass = repositoryDomainClass == null || repositoryDomainClass.isAssignableFrom(methodDomainClass) ? methodDomainClass - : repositoryDomainClass; + this.domainClass = repositoryDomainClass == null || repositoryDomainClass.isAssignableFrom(methodDomainClass) + ? methodDomainClass : repositoryDomainClass; } return domainClass; @@ -163,9 +167,9 @@ public class QueryMethod { */ public boolean isCollectionQuery() { - Class returnType = method.getReturnType(); return !(isPageQuery() || isSliceQuery()) - && org.springframework.util.ClassUtils.isAssignable(Iterable.class, returnType) || returnType.isArray(); + && org.springframework.util.ClassUtils.isAssignable(Iterable.class, unwrappedReturnType) + || unwrappedReturnType.isArray(); } /** @@ -175,9 +179,7 @@ public class QueryMethod { * @since 1.8 */ public boolean isSliceQuery() { - - Class returnType = method.getReturnType(); - return !isPageQuery() && org.springframework.util.ClassUtils.isAssignable(Slice.class, returnType); + return !isPageQuery() && org.springframework.util.ClassUtils.isAssignable(Slice.class, unwrappedReturnType); } /** @@ -186,9 +188,7 @@ public class QueryMethod { * @return */ public final boolean isPageQuery() { - - Class returnType = method.getReturnType(); - return org.springframework.util.ClassUtils.isAssignable(Page.class, returnType); + return org.springframework.util.ClassUtils.isAssignable(Page.class, unwrappedReturnType); } /** @@ -216,7 +216,7 @@ public class QueryMethod { * @since 1.10 */ public boolean isStreamQuery() { - return ReflectionUtils.isJava8StreamType(method.getReturnType()); + return ReflectionUtils.isJava8StreamType(unwrappedReturnType); } /** @@ -236,4 +236,14 @@ public class QueryMethod { public String toString() { return method.toString(); } + + private static Class potentiallyUnwrapReturnTypeFor(Method method) { + + if (QueryExecutionConverters.supports(method.getReturnType())) { + // unwrap only one level to handle cases like Future> correctly. + return ClassTypeInformation.fromReturnTypeOf(method).getComponentType().getType(); + } + + return method.getReturnType(); + } } diff --git a/src/test/java/org/springframework/data/repository/query/QueryMethodUnitTests.java b/src/test/java/org/springframework/data/repository/query/QueryMethodUnitTests.java index a5d27071b..9e898dd2c 100644 --- a/src/test/java/org/springframework/data/repository/query/QueryMethodUnitTests.java +++ b/src/test/java/org/springframework/data/repository/query/QueryMethodUnitTests.java @@ -20,6 +20,9 @@ import static org.junit.Assert.*; import java.io.Serializable; import java.lang.reflect.Method; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.stream.Stream; import org.junit.Test; @@ -34,6 +37,7 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat * Unit tests for {@link QueryMethod}. * * @author Oliver Gierke + * @author Thomas Darimont */ public class QueryMethodUnitTests { @@ -163,6 +167,54 @@ public class QueryMethodUnitTests { assertThat(new QueryMethod(method, repositoryMetadata).isStreamQuery(), is(true)); } + /** + * @see DATACMNS-716 + */ + @Test + public void doesNotRejectCompletableFutureQueryForSingleEntity() throws Exception { + + RepositoryMetadata repositoryMetadata = new DefaultRepositoryMetadata(SampleRepository.class); + Method method = SampleRepository.class.getMethod("returnsCompletableFutureForSingleEntity"); + + assertThat(new QueryMethod(method, repositoryMetadata).isCollectionQuery(), is(false)); + } + + /** + * @see DATACMNS-716 + */ + @Test + public void doesNotRejectCompletableFutureQueryForEntityCollection() throws Exception { + + RepositoryMetadata repositoryMetadata = new DefaultRepositoryMetadata(SampleRepository.class); + Method method = SampleRepository.class.getMethod("returnsCompletableFutureForEntityCollection"); + + assertThat(new QueryMethod(method, repositoryMetadata).isCollectionQuery(), is(true)); + } + + /** + * @see DATACMNS-716 + */ + @Test + public void doesNotRejectFutureQueryForSingleEntity() throws Exception { + + RepositoryMetadata repositoryMetadata = new DefaultRepositoryMetadata(SampleRepository.class); + Method method = SampleRepository.class.getMethod("returnsFutureForSingleEntity"); + + assertThat(new QueryMethod(method, repositoryMetadata).isCollectionQuery(), is(false)); + } + + /** + * @see DATACMNS-716 + */ + @Test + public void doesNotRejectFutureQueryForEntityCollection() throws Exception { + + RepositoryMetadata repositoryMetadata = new DefaultRepositoryMetadata(SampleRepository.class); + Method method = SampleRepository.class.getMethod("returnsFutureForEntityCollection"); + + assertThat(new QueryMethod(method, repositoryMetadata).isCollectionQuery(), is(true)); + } + interface SampleRepository extends Repository { String pagingMethodWithInvalidReturnType(Pageable pageable); @@ -186,6 +238,26 @@ public class QueryMethodUnitTests { Stream streaming(); Stream streaming(Pageable pageable); + + /** + * @see DATACMNS-716 + */ + CompletableFuture returnsCompletableFutureForSingleEntity(); + + /** + * @see DATACMNS-716 + */ + CompletableFuture> returnsCompletableFutureForEntityCollection(); + + /** + * @see DATACMNS-716 + */ + Future returnsFutureForSingleEntity(); + + /** + * @see DATACMNS-716 + */ + Future> returnsFutureForEntityCollection(); } class User {