Enable QuerydslPredicate for meta-annotation usage.
We now support composed annotations that are meta-annotated with QuerydslPredicate. Resolves #2277.
This commit is contained in:
@@ -20,6 +20,8 @@ import java.util.Optional;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
|
||||
import org.springframework.data.querydsl.binding.QuerydslBindings;
|
||||
@@ -82,7 +84,9 @@ public abstract class QuerydslPredicateArgumentResolverSupport {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parameter.hasParameterAnnotation(QuerydslPredicate.class)) {
|
||||
MergedAnnotations annotations = MergedAnnotations.from(parameter.getParameter());
|
||||
|
||||
if (annotations.isPresent(QuerydslPredicate.class)) {
|
||||
throw new IllegalArgumentException(String.format("Parameter at position %s must be of type Predicate but was %s.",
|
||||
parameter.getParameterIndex(), parameter.getParameterType()));
|
||||
}
|
||||
@@ -93,12 +97,12 @@ public abstract class QuerydslPredicateArgumentResolverSupport {
|
||||
@Nullable
|
||||
Predicate getPredicate(MethodParameter parameter, MultiValueMap<String, String> queryParameters) {
|
||||
|
||||
Optional<QuerydslPredicate> annotation = Optional
|
||||
.ofNullable(parameter.getParameterAnnotation(QuerydslPredicate.class));
|
||||
TypeInformation<?> domainType = extractTypeInfo(parameter).getRequiredActualType();
|
||||
MergedAnnotations annotations = MergedAnnotations.from(parameter.getParameter());
|
||||
MergedAnnotation<QuerydslPredicate> predicateAnnotation = annotations.get(QuerydslPredicate.class);
|
||||
|
||||
Optional<Class<? extends QuerydslBinderCustomizer<?>>> bindingsAnnotation = annotation //
|
||||
.map(QuerydslPredicate::bindings) //
|
||||
TypeInformation<?> domainType = extractTypeInfo(parameter, predicateAnnotation).getRequiredActualType();
|
||||
|
||||
Optional<Class<? extends QuerydslBinderCustomizer<?>>> bindingsAnnotation = predicateAnnotation.getValue("bindings") //
|
||||
.map(CastUtils::cast);
|
||||
|
||||
QuerydslBindings bindings = bindingsAnnotation //
|
||||
@@ -115,10 +119,10 @@ public abstract class QuerydslPredicateArgumentResolverSupport {
|
||||
* @param parameter must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected static TypeInformation<?> extractTypeInfo(MethodParameter parameter) {
|
||||
protected static TypeInformation<?> extractTypeInfo(MethodParameter parameter,
|
||||
MergedAnnotation<QuerydslPredicate> predicateAnnotation) {
|
||||
|
||||
Optional<QuerydslPredicate> annotation = Optional
|
||||
.ofNullable(parameter.getParameterAnnotation(QuerydslPredicate.class));
|
||||
Optional<QuerydslPredicate> annotation = predicateAnnotation.synthesize(MergedAnnotation::isPresent);
|
||||
|
||||
return annotation.filter(it -> !Object.class.equals(it.root()))//
|
||||
.<TypeInformation<?>> map(it -> ClassTypeInformation.from(it.root()))//
|
||||
|
||||
@@ -18,11 +18,17 @@ package org.springframework.data.web.querydsl;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.*;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.querydsl.QUser;
|
||||
@@ -149,6 +155,20 @@ class QuerydslPredicateArgumentResolverUnitTests {
|
||||
QUser.user.firstname.eq("egwene".toUpperCase()).and(QUser.user.lastname.toLowerCase().eq("al'vere")));
|
||||
}
|
||||
|
||||
@Test // #2277
|
||||
void resolveArgumentShouldHonorMetaAnnotation() throws Exception {
|
||||
|
||||
request.addParameter("firstname", "egwene");
|
||||
request.addParameter("lastname", "al'vere");
|
||||
|
||||
Object predicate = resolver.resolveArgument(
|
||||
getMethodParameterFor("specificFindWithMetaAnnotation", Predicate.class), null, new ServletWebRequest(request),
|
||||
null);
|
||||
|
||||
assertThat(predicate).isEqualTo(
|
||||
QUser.user.firstname.eq("egwene".toUpperCase()).and(QUser.user.lastname.toLowerCase().eq("al'vere")));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-669
|
||||
void shouldCreatePredicateForNonStringPropertyCorrectly() throws Exception {
|
||||
|
||||
@@ -188,7 +208,7 @@ class QuerydslPredicateArgumentResolverUnitTests {
|
||||
void extractTypeInformationShouldUseTypeExtractedFromMethodReturnTypeIfPredicateNotAnnotated() {
|
||||
|
||||
TypeInformation<?> type = ReflectionTestUtils.invokeMethod(resolver, "extractTypeInfo",
|
||||
getMethodParameterFor("predicateWithoutAnnotation", Predicate.class));
|
||||
getMethodParameterFor("predicateWithoutAnnotation", Predicate.class), MergedAnnotation.missing());
|
||||
|
||||
assertThat(type).isEqualTo(ClassTypeInformation.from(User.class));
|
||||
}
|
||||
@@ -200,9 +220,11 @@ class QuerydslPredicateArgumentResolverUnitTests {
|
||||
TypeInformation USER_TYPE = ClassTypeInformation.from(User.class);
|
||||
TypeInformation MODELA_AND_VIEW_TYPE = ClassTypeInformation.from(ModelAndView.class);
|
||||
|
||||
assertThat(extractTypeInfo(getMethodParameterFor("forEntity"))).isEqualTo(USER_TYPE);
|
||||
assertThat(extractTypeInfo(getMethodParameterFor("forResourceOfUser"))).isEqualTo(USER_TYPE);
|
||||
assertThat(extractTypeInfo(getMethodParameterFor("forModelAndView"))).isEqualTo(MODELA_AND_VIEW_TYPE);
|
||||
assertThat(extractTypeInfo(getMethodParameterFor("forEntity"), MergedAnnotation.missing())).isEqualTo(USER_TYPE);
|
||||
assertThat(extractTypeInfo(getMethodParameterFor("forResourceOfUser"), MergedAnnotation.missing()))
|
||||
.isEqualTo(USER_TYPE);
|
||||
assertThat(extractTypeInfo(getMethodParameterFor("forModelAndView"), MergedAnnotation.missing()))
|
||||
.isEqualTo(MODELA_AND_VIEW_TYPE);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1593
|
||||
@@ -276,6 +298,8 @@ class QuerydslPredicateArgumentResolverUnitTests {
|
||||
|
||||
User specificFind(@QuerydslPredicate(bindings = SpecificBinding.class) Predicate predicate);
|
||||
|
||||
User specificFindWithMetaAnnotation(@MyQuerydslPredicate Predicate predicate);
|
||||
|
||||
HttpEntity<User> forEntity();
|
||||
|
||||
ModelAndView forModelAndView();
|
||||
@@ -296,4 +320,23 @@ class QuerydslPredicateArgumentResolverUnitTests {
|
||||
bindings.bind(QUser.user.firstname).first((path, value) -> path.contains(value));
|
||||
}
|
||||
}
|
||||
|
||||
@Target({ ElementType.PARAMETER, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@QuerydslPredicate
|
||||
public @interface MyQuerydslPredicate {
|
||||
|
||||
/**
|
||||
* To customize the way individual properties' values should be bound to the predicate a
|
||||
* {@link QuerydslBinderCustomizer} can be specified here. We'll try to obtain a Spring bean of this type but fall
|
||||
* back to a plain instantiation if no bean is found in the current
|
||||
* {@link org.springframework.beans.factory.BeanFactory}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@AliasFor(annotation = QuerydslPredicate.class, attribute = "bindings")
|
||||
Class<? extends QuerydslBinderCustomizer> bindings() default SpecificBinding.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user