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:
committed by
Oliver Gierke
parent
a3afa84198
commit
b194f7794d
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user