Skip to content

Commit a01c459

Browse files
committed
Revise QuerydslPredicateBuilder nullability behavior.
We now no longer return a null Predicate from QuerydslPredicateBuilder.getPredicate(…) if the input values are empty or if the constraints are empty. Instead, we return an empty BooleanBuilder instance to avoid null handling on the calling side. HandlerMethodArgumentsResolvers for QuerydslBindings retain their null/Optional.empty semantics. Closes #2396
1 parent 2516d71 commit a01c459

File tree

5 files changed

+51
-28
lines changed

5 files changed

+51
-28
lines changed

src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java

+27-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.data.mapping.PropertyPath;
3333
import org.springframework.data.querydsl.EntityPathResolver;
3434
import org.springframework.data.util.TypeInformation;
35-
import org.springframework.lang.Nullable;
3635
import org.springframework.util.Assert;
3736
import org.springframework.util.MultiValueMap;
3837
import org.springframework.util.StringUtils;
@@ -80,9 +79,8 @@ public QuerydslPredicateBuilder(ConversionService conversionService, EntityPathR
8079
* @param type the type to create a predicate for.
8180
* @param values the values to bind.
8281
* @param bindings the {@link QuerydslBindings} for the predicate.
83-
* @return
82+
* @return the {@link Predicate}.
8483
*/
85-
@Nullable
8684
public Predicate getPredicate(TypeInformation<?> type, MultiValueMap<String, String> values,
8785
QuerydslBindings bindings) {
8886

@@ -91,7 +89,7 @@ public Predicate getPredicate(TypeInformation<?> type, MultiValueMap<String, Str
9189
BooleanBuilder builder = new BooleanBuilder();
9290

9391
if (values.isEmpty()) {
94-
return builder.getValue();
92+
return getPredicate(builder);
9593
}
9694

9795
for (Entry<String, List<String>> entry : values.entrySet()) {
@@ -118,7 +116,19 @@ public Predicate getPredicate(TypeInformation<?> type, MultiValueMap<String, Str
118116
predicate.ifPresent(builder::and);
119117
}
120118

121-
return builder.getValue();
119+
return getPredicate(builder);
120+
}
121+
122+
/**
123+
* Returns whether the given {@link Predicate} represents an empty predicate instance.
124+
*
125+
* @param predicate
126+
* @return
127+
* @since 2.5.3
128+
* @see BooleanBuilder
129+
*/
130+
public static boolean isEmpty(Predicate predicate) {
131+
return new BooleanBuilder().equals(predicate);
122132
}
123133

124134
/**
@@ -219,4 +229,16 @@ private static TypeDescriptor getTargetTypeDescriptor(PathInformation path) {
219229
private static boolean isSingleElementCollectionWithoutText(List<String> source) {
220230
return source.size() == 1 && !StringUtils.hasLength(source.get(0));
221231
}
232+
233+
/**
234+
* Returns the {@link Predicate} from {@link BooleanBuilder}.
235+
*
236+
* @param builder
237+
* @return
238+
*/
239+
private static Predicate getPredicate(BooleanBuilder builder) {
240+
241+
Predicate predicate = builder.getValue();
242+
return predicate == null ? new BooleanBuilder() : predicate;
243+
}
222244
}

src/main/java/org/springframework/data/web/querydsl/QuerydslPredicateArgumentResolver.java

+1-9
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.util.Optional;
2222

2323
import org.springframework.core.MethodParameter;
24-
import org.springframework.core.ResolvableType;
2524
import org.springframework.core.convert.ConversionService;
2625
import org.springframework.core.convert.support.DefaultConversionService;
2726
import org.springframework.data.querydsl.binding.QuerydslBindingsFactory;
@@ -33,7 +32,6 @@
3332
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
3433
import org.springframework.web.method.support.ModelAndViewContainer;
3534

36-
import com.querydsl.core.BooleanBuilder;
3735
import com.querydsl.core.types.Predicate;
3836

3937
/**
@@ -84,13 +82,7 @@ public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewC
8482
MultiValueMap<String, String> queryParameters = getQueryParameters(webRequest);
8583
Predicate result = getPredicate(parameter, queryParameters);
8684

87-
if (!parameter.isOptional() && result == null) {
88-
return new BooleanBuilder();
89-
}
90-
91-
return OPTIONAL_OF_PREDICATE.isAssignableFrom(ResolvableType.forMethodParameter(parameter)) //
92-
? Optional.ofNullable(result) //
93-
: result;
85+
return potentiallyConvertMethodParameterValue(parameter, result);
9486
}
9587

9688
private static MultiValueMap<String, String> getQueryParameters(NativeWebRequest webRequest) {

src/main/java/org/springframework/data/web/querydsl/QuerydslPredicateArgumentResolverSupport.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ public boolean supportsParameter(MethodParameter parameter) {
9494
return false;
9595
}
9696

97-
@Nullable
9897
Predicate getPredicate(MethodParameter parameter, MultiValueMap<String, String> queryParameters) {
9998

10099
MergedAnnotations annotations = MergedAnnotations.from(parameter.getParameter());
@@ -112,6 +111,20 @@ Predicate getPredicate(MethodParameter parameter, MultiValueMap<String, String>
112111
return predicateBuilder.getPredicate(domainType, queryParameters, bindings);
113112
}
114113

114+
@Nullable
115+
static Object potentiallyConvertMethodParameterValue(MethodParameter parameter, Predicate predicate) {
116+
117+
if (!parameter.isOptional()) {
118+
return predicate;
119+
}
120+
121+
if (OPTIONAL_OF_PREDICATE.isAssignableFrom(ResolvableType.forMethodParameter(parameter))) {
122+
return QuerydslPredicateBuilder.isEmpty(predicate) ? Optional.empty() : Optional.of(predicate);
123+
}
124+
125+
return QuerydslPredicateBuilder.isEmpty(predicate) ? null : predicate;
126+
}
127+
115128
/**
116129
* Obtains the domain type information from the given method parameter. Will favor an explicitly registered on through
117130
* {@link QuerydslPredicate#root()} but use the actual type of the method's return type as fallback.

src/main/java/org/springframework/data/web/querydsl/ReactiveQuerydslPredicateArgumentResolver.java

+4-10
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,18 @@
1717

1818
import java.util.List;
1919
import java.util.Map.Entry;
20-
import java.util.Optional;
2120

2221
import org.springframework.core.MethodParameter;
23-
import org.springframework.core.ResolvableType;
2422
import org.springframework.core.convert.ConversionService;
2523
import org.springframework.data.querydsl.binding.QuerydslBindingsFactory;
24+
import org.springframework.lang.Nullable;
2625
import org.springframework.util.LinkedMultiValueMap;
2726
import org.springframework.util.MultiValueMap;
2827
import org.springframework.web.reactive.BindingContext;
2928
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
3029
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
3130
import org.springframework.web.server.ServerWebExchange;
3231

33-
import com.querydsl.core.BooleanBuilder;
3432
import com.querydsl.core.types.Predicate;
3533

3634
/**
@@ -54,21 +52,17 @@ public ReactiveQuerydslPredicateArgumentResolver(QuerydslBindingsFactory factory
5452
* @see org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver(org.springframework.core.MethodParameter, org.springframework.web.reactive.BindingContext, org.springframework.web.server.ServerWebExchange)
5553
*/
5654
@Override
55+
@Nullable
5756
public Object resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
5857
ServerWebExchange exchange) {
5958

6059
MultiValueMap<String, String> queryParameters = getQueryParameters(exchange);
6160
Predicate result = getPredicate(parameter, queryParameters);
6261

63-
if (!parameter.isOptional() && result == null) {
64-
return new BooleanBuilder();
65-
}
66-
67-
return OPTIONAL_OF_PREDICATE.isAssignableFrom(ResolvableType.forMethodParameter(parameter)) //
68-
? Optional.ofNullable(result) //
69-
: result;
62+
return potentiallyConvertMethodParameterValue(parameter, result);
7063
}
7164

65+
7266
private static MultiValueMap<String, String> getQueryParameters(ServerWebExchange exchange) {
7367

7468
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();

src/test/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilderUnitTests.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,10 @@ void getPredicateShouldThrowErrorWhenBindingContextIsNull() {
7777
}
7878

7979
@Test // DATACMNS-669, DATACMNS-1168
80-
void getPredicateShouldReturnNullWhenPropertiesAreEmpty() {
81-
assertThat(builder.getPredicate(ClassTypeInformation.OBJECT, values, DEFAULT_BINDINGS)).isNull();
80+
void getPredicateShouldReturnEmptyWhenPropertiesAreEmpty() {
81+
assertThat(
82+
QuerydslPredicateBuilder.isEmpty(builder.getPredicate(ClassTypeInformation.OBJECT, values, DEFAULT_BINDINGS)))
83+
.isTrue();
8284
}
8385

8486
@Test // DATACMNS-669
@@ -228,6 +230,6 @@ void dropsValuesContainingAnEmptyString() {
228230

229231
values.add("firstname", "");
230232

231-
assertThat(builder.getPredicate(USER_TYPE, values, DEFAULT_BINDINGS)).isNull();
233+
assertThat(QuerydslPredicateBuilder.isEmpty(builder.getPredicate(USER_TYPE, values, DEFAULT_BINDINGS))).isTrue();
232234
}
233235
}

0 commit comments

Comments
 (0)