DATACMNS-1593, DATACMNS-1635 - Overhauled null handling in QuerydslPredicateArgumentResolver.

QuerydslPredicateArgumentResolver now properly handles predicate lookups that result in null values. The semantics of a handler method parameter of type of Querydsl's Predicate have been tightened to always see a non-null Predicate by default. Users that want to handle the absence of predicates explicitly can opt into seeing null by annotating the parameter with @Nullable or use Optional<Predicate>.

QuerydslPredicateBuilder now consistently returns null in case the original parameter map is entirely empty or consists of only keys with empty value arrays as empty form submissions do. This partially reverts the work of DATACMNS-1168, which moved into the direction of returning a default Predicate value for empty maps in the first place. That however prevents us from producing empty Optionals.

Related tickets: DATACMNS-1168.
This commit is contained in:
Oliver Drotbohm
2019-12-10 14:52:06 +01:00
parent 9b63646e82
commit 664e661783
4 changed files with 86 additions and 26 deletions

View File

@@ -21,9 +21,11 @@ import java.util.Map.Entry;
import java.util.Optional;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.querydsl.binding.QuerydslBindingsFactory;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import org.springframework.data.querydsl.binding.QuerydslPredicateBuilder;
@@ -38,6 +40,7 @@ import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Predicate;
/**
@@ -50,6 +53,10 @@ import com.querydsl.core.types.Predicate;
*/
public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentResolver {
private static final ResolvableType PREDICATE = ResolvableType.forClass(Predicate.class);
private static final ResolvableType OPTIONAL_OF_PREDICATE = ResolvableType.forClassWithGenerics(Optional.class,
PREDICATE);
private final QuerydslBindingsFactory bindingsFactory;
private final QuerydslPredicateBuilder predicateBuilder;
@@ -74,7 +81,9 @@ public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentR
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (Predicate.class.equals(parameter.getParameterType())) {
ResolvableType type = ResolvableType.forMethodParameter(parameter);
if (PREDICATE.isAssignableFrom(type) || OPTIONAL_OF_PREDICATE.isAssignableFrom(type)) {
return true;
}
@@ -92,7 +101,7 @@ public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentR
*/
@Nullable
@Override
public Predicate resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
@@ -105,13 +114,23 @@ public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentR
.ofNullable(parameter.getParameterAnnotation(QuerydslPredicate.class));
TypeInformation<?> domainType = extractTypeInfo(parameter).getRequiredActualType();
Optional<Class<? extends QuerydslBinderCustomizer<?>>> bindings = annotation//
.map(QuerydslPredicate::bindings)//
Optional<Class<? extends QuerydslBinderCustomizer<?>>> bindingsAnnotation = annotation //
.map(QuerydslPredicate::bindings) //
.map(CastUtils::cast);
return predicateBuilder.getPredicate(domainType, parameters,
bindings.map(it -> bindingsFactory.createBindingsFor(domainType, it))
.orElseGet(() -> bindingsFactory.createBindingsFor(domainType)));
QuerydslBindings bindings = bindingsAnnotation //
.map(it -> bindingsFactory.createBindingsFor(domainType, it)) //
.orElseGet(() -> bindingsFactory.createBindingsFor(domainType));
Predicate result = predicateBuilder.getPredicate(domainType, parameters, bindings);
if (!parameter.isOptional() && result == null) {
return new BooleanBuilder();
}
return OPTIONAL_OF_PREDICATE.isAssignableFrom(ResolvableType.forMethodParameter(parameter)) //
? Optional.ofNullable(result) //
: result;
}
/**