DATACMNS-1005 - Paginated query methods now support Javaslang collection types.

To eagerly catch invalid declarations, query methods using pagination had a check applied that the method either returns a Page or List. We've introduced support for alternative collection libraries (Javaslang in particular) and thus the check needs to take these newly supported types into account.

This is now fixed by externalizing the valid types into QueryExecutionConverters and using the ones returned by that to check against.

Original pull request: #200.
This commit is contained in:
Maciek Opala
2017-03-07 20:08:29 +01:00
committed by Oliver Gierke
parent a3afa84198
commit b194f7794d
4 changed files with 63 additions and 3 deletions

View File

@@ -18,7 +18,8 @@ package org.springframework.data.repository.query;
import static org.springframework.data.repository.util.ClassUtils.*;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Arrays;
import java.util.Set;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -30,6 +31,7 @@ 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.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
@@ -78,7 +80,8 @@ public class QueryMethod {
if (hasParameterOfType(method, Pageable.class)) {
if (!isStreamQuery()) {
assertReturnTypeAssignable(method, Slice.class, Page.class, List.class);
final Set<Class<?>> allowedPageableTypes = QueryExecutionConverters.getAllowedPageableTypes();
assertReturnTypeAssignable(method, allowedPageableTypes.toArray(new Class<?>[allowedPageableTypes.size()]));
}
if (hasParameterOfType(method, Sort.class)) {
@@ -275,4 +278,21 @@ public class QueryMethod {
return method.getReturnType();
}
}
private static void assertReturnTypeAssignable(Method method, Class<?>... types) {
Assert.notNull(method, "Method must not be null!");
Assert.notEmpty(types, "Types must not be null or empty!");
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);
returnType = QueryExecutionConverters.isSingleValue(returnType.getType()) ? returnType.getComponentType() : returnType;
for (Class<?> type : types) {
if (type.isAssignableFrom(returnType.getType())) {
return;
}
}
throw new IllegalStateException("Method has to have one of the following return types! " + Arrays.toString(types));
}
}

View File

@@ -15,11 +15,14 @@
*/
package org.springframework.data.repository.util;
import javaslang.collection.Seq;
import javaslang.collection.Traversable;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Slice;
import scala.Function0;
import scala.Option;
import scala.runtime.AbstractFunction0;
@@ -28,6 +31,7 @@ import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
@@ -80,6 +84,7 @@ public abstract class QueryExecutionConverters {
private static final Set<WrapperType> WRAPPER_TYPES = new HashSet<WrapperType>();
private static final Set<WrapperType> UNWRAPPER_TYPES = new HashSet<WrapperType>();
private static final Set<Converter<Object, Object>> UNWRAPPERS = new HashSet<Converter<Object, Object>>();
private static final Set<Class<?>> ALLOWED_PAGEABLE_TYPES = new HashSet<Class<?>>();
static {
@@ -88,6 +93,10 @@ public abstract class QueryExecutionConverters {
WRAPPER_TYPES.add(WrapperType.singleValue(ListenableFuture.class));
UNWRAPPER_TYPES.add(WrapperType.singleValue(ListenableFuture.class));
ALLOWED_PAGEABLE_TYPES.add(Slice.class);
ALLOWED_PAGEABLE_TYPES.add(Page.class);
ALLOWED_PAGEABLE_TYPES.add(List.class);
if (GUAVA_PRESENT) {
WRAPPER_TYPES.add(NullableWrapperToGuavaOptionalConverter.getWrapperType());
UNWRAPPER_TYPES.add(NullableWrapperToGuavaOptionalConverter.getWrapperType());
@@ -117,6 +126,8 @@ public abstract class QueryExecutionConverters {
WRAPPER_TYPES.add(JavaslangCollections.ToJavaConverter.INSTANCE.getWrapperType());
UNWRAPPERS.add(JavaslangOptionUnwrapper.INSTANCE);
ALLOWED_PAGEABLE_TYPES.add(Seq.class);
}
if (ReactiveWrappers.isAvailable()) {
@@ -180,6 +191,10 @@ public abstract class QueryExecutionConverters {
return false;
}
public static Set<Class<?>> getAllowedPageableTypes() {
return Collections.unmodifiableSet(ALLOWED_PAGEABLE_TYPES);
}
/**
* Registers converters for wrapper types found on the classpath.
*