Skip to content

Commit 5200bdb

Browse files
Rodrigo Dos SantosRodrigo Dos Santos
Rodrigo Dos Santos
authored and
Rodrigo Dos Santos
committed
Add support for queryDsl webflux
1 parent 2d6755f commit 5200bdb

13 files changed

+428
-261
lines changed

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

Lines changed: 14 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,20 @@
1515
*/
1616
package org.springframework.data.querydsl.binding;
1717

18-
import java.beans.PropertyDescriptor;
19-
import java.util.ArrayList;
20-
import java.util.Collection;
21-
import java.util.Collections;
22-
import java.util.List;
23-
import java.util.Map;
24-
import java.util.Optional;
25-
import java.util.concurrent.ConcurrentHashMap;
26-
18+
import com.querydsl.core.BooleanBuilder;
19+
import com.querydsl.core.types.Path;
20+
import com.querydsl.core.types.Predicate;
2721
import org.springframework.beans.PropertyValues;
2822
import org.springframework.core.convert.ConversionService;
29-
import org.springframework.core.convert.Property;
30-
import org.springframework.core.convert.TypeDescriptor;
31-
import org.springframework.data.mapping.PropertyPath;
3223
import org.springframework.data.querydsl.EntityPathResolver;
3324
import org.springframework.data.util.TypeInformation;
34-
import org.springframework.lang.Nullable;
3525
import org.springframework.util.Assert;
36-
import org.springframework.util.ClassUtils;
3726
import org.springframework.util.MultiValueMap;
38-
import org.springframework.util.ObjectUtils;
3927

40-
import com.querydsl.core.BooleanBuilder;
41-
import com.querydsl.core.types.Path;
42-
import com.querydsl.core.types.Predicate;
28+
import java.util.Collection;
29+
import java.util.Map;
30+
import java.util.Optional;
31+
import java.util.concurrent.ConcurrentHashMap;
4332

4433
/**
4534
* Builder assembling {@link Predicate} out of {@link PropertyValues}.
@@ -50,12 +39,12 @@
5039
* @author Johannes Englmeier
5140
* @since 1.11
5241
*/
53-
public class QuerydslPredicateBuilder {
42+
public class QuerydslPredicateBuilder implements QuerydslPredicateBuilderCustomizer {
5443

55-
private final ConversionService conversionService;
56-
private final MultiValueBinding<Path<? extends Object>, Object> defaultBinding;
57-
private final Map<PathInformation, Path<?>> paths;
58-
private final EntityPathResolver resolver;
44+
protected final ConversionService conversionService;
45+
protected final MultiValueBinding<Path<? extends Object>, Object> defaultBinding;
46+
protected final Map<PathInformation, Path<?>> paths;
47+
protected final EntityPathResolver resolver;
5948

6049
/**
6150
* Creates a new {@link QuerydslPredicateBuilder} for the given {@link ConversionService} and
@@ -111,145 +100,12 @@ public Predicate getPredicate(TypeInformation<?> type, MultiValueMap<String, ?>
111100
continue;
112101
}
113102

114-
Collection<Object> value = convertToPropertyPathSpecificType(entry.getValue(), propertyPath);
115-
Optional<Predicate> predicate = invokeBinding(propertyPath, bindings, value);
103+
Collection<Object> value = convertToPropertyPathSpecificType(entry.getValue(), propertyPath, conversionService);
104+
Optional<Predicate> predicate = invokeBinding(propertyPath, bindings, value, resolver, defaultBinding);
116105

117106
predicate.ifPresent(builder::and);
118107
}
119108

120109
return getPredicate(builder);
121110
}
122-
123-
/**
124-
* Returns whether the given {@link Predicate} represents an empty predicate instance.
125-
*
126-
* @param predicate
127-
* @return
128-
* @since 2.5.3
129-
* @see BooleanBuilder
130-
*/
131-
public static boolean isEmpty(Predicate predicate) {
132-
return new BooleanBuilder().equals(predicate);
133-
}
134-
135-
/**
136-
* Invokes the binding of the given values, for the given {@link PropertyPath} and {@link QuerydslBindings}.
137-
*
138-
* @param dotPath must not be {@literal null}.
139-
* @param bindings must not be {@literal null}.
140-
* @param values must not be {@literal null}.
141-
* @return
142-
*/
143-
protected Optional<Predicate> invokeBinding(PathInformation dotPath, QuerydslBindings bindings,
144-
Collection<Object> values) {
145-
146-
Path<?> path = getPath(dotPath, bindings);
147-
148-
return bindings.getBindingForPath(dotPath).orElse(defaultBinding).bind(path, values);
149-
}
150-
151-
/**
152-
* Returns the {@link Path} for the given {@link PropertyPath} and {@link QuerydslBindings}. Will try to obtain the
153-
* {@link Path} from the bindings first but fall back to reifying it from the PropertyPath in case no specific binding
154-
* has been configured.
155-
*
156-
* @param path must not be {@literal null}.
157-
* @param bindings must not be {@literal null}.
158-
* @return
159-
*/
160-
private Path<?> getPath(PathInformation path, QuerydslBindings bindings) {
161-
162-
Optional<Path<?>> resolvedPath = bindings.getExistingPath(path);
163-
164-
return resolvedPath.orElseGet(() -> paths.computeIfAbsent(path, it -> it.reifyPath(resolver)));
165-
}
166-
167-
/**
168-
* Converts the given source values into a collection of elements that are of the given {@link PropertyPath}'s type.
169-
* Considers a single element list with an empty object an empty collection because this basically indicates the
170-
* property having been submitted but no value provided.
171-
*
172-
* @param source must not be {@literal null}.
173-
* @param path must not be {@literal null}.
174-
* @return
175-
*/
176-
protected Collection<Object> convertToPropertyPathSpecificType(List<?> source, PathInformation path) {
177-
178-
if (source.isEmpty() || isSingleElementCollectionWithEmptyItem(source)) {
179-
return Collections.emptyList();
180-
}
181-
182-
TypeDescriptor targetType = getTargetTypeDescriptor(path);
183-
Collection<Object> target = new ArrayList<>(source.size());
184-
185-
for (Object value : source) {
186-
target.add(getValue(targetType, value));
187-
}
188-
189-
return target;
190-
}
191-
192-
@Nullable
193-
private Object getValue(TypeDescriptor targetType, Object value) {
194-
195-
if (ClassUtils.isAssignableValue(targetType.getType(), value)) {
196-
return value;
197-
}
198-
199-
if (conversionService.canConvert(value.getClass(), targetType.getType())) {
200-
return conversionService.convert(value, TypeDescriptor.forObject(value), targetType);
201-
}
202-
203-
return value;
204-
}
205-
206-
/**
207-
* Returns the target {@link TypeDescriptor} for the given {@link PathInformation} by either inspecting the field or
208-
* property (the latter preferred) to pick up annotations potentially defined for formatting purposes.
209-
*
210-
* @param path must not be {@literal null}.
211-
* @return
212-
*/
213-
private static TypeDescriptor getTargetTypeDescriptor(PathInformation path) {
214-
215-
PropertyDescriptor descriptor = path.getLeafPropertyDescriptor();
216-
217-
Class<?> owningType = path.getLeafParentType();
218-
String leafProperty = path.getLeafProperty();
219-
220-
TypeDescriptor result = descriptor == null //
221-
? TypeDescriptor
222-
.nested(org.springframework.data.util.ReflectionUtils.findRequiredField(owningType, leafProperty), 0)
223-
: TypeDescriptor
224-
.nested(new Property(owningType, descriptor.getReadMethod(), descriptor.getWriteMethod(), leafProperty), 0);
225-
226-
if (result == null) {
227-
throw new IllegalStateException(String.format("Could not obtain TypeDescriptor for PathInformation %s", path));
228-
}
229-
230-
return result;
231-
}
232-
233-
/**
234-
* Returns whether the given collection has exactly one element that is empty (i.e. doesn't contain text). This is
235-
* basically an indicator that a request parameter has been submitted but no value for it.
236-
*
237-
* @param source must not be {@literal null}.
238-
* @return
239-
*/
240-
protected static boolean isSingleElementCollectionWithEmptyItem(List<?> source) {
241-
return source.size() == 1 && ObjectUtils.isEmpty(source.get(0));
242-
}
243-
244-
/**
245-
* Returns the {@link Predicate} from {@link BooleanBuilder}.
246-
*
247-
* @param builder
248-
* @return
249-
*/
250-
protected static Predicate getPredicate(BooleanBuilder builder) {
251-
252-
Predicate predicate = builder.getValue();
253-
return predicate == null ? new BooleanBuilder() : predicate;
254-
}
255111
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package org.springframework.data.querydsl.binding;
2+
3+
import com.querydsl.core.BooleanBuilder;
4+
import com.querydsl.core.types.Path;
5+
import com.querydsl.core.types.Predicate;
6+
import org.springframework.beans.PropertyValues;
7+
import org.springframework.core.convert.ConversionService;
8+
import org.springframework.core.convert.Property;
9+
import org.springframework.core.convert.TypeDescriptor;
10+
import org.springframework.data.mapping.PropertyPath;
11+
import org.springframework.data.querydsl.EntityPathResolver;
12+
import org.springframework.data.util.TypeInformation;
13+
import org.springframework.lang.Nullable;
14+
import org.springframework.util.ClassUtils;
15+
import org.springframework.util.MultiValueMap;
16+
import org.springframework.util.ObjectUtils;
17+
18+
import java.beans.PropertyDescriptor;
19+
import java.util.*;
20+
import java.util.concurrent.ConcurrentHashMap;
21+
22+
/**
23+
* Builder assembling {@link Predicate} out of {@link PropertyValues}.
24+
*
25+
* @author Christoph Strobl
26+
* @author Oliver Gierke
27+
* @author Mark Paluch
28+
* @author Johannes Englmeier
29+
* @since 1.11
30+
*/
31+
@FunctionalInterface
32+
public interface QuerydslPredicateBuilderCustomizer {
33+
/**
34+
* Returns whether the given {@link Predicate} represents an empty predicate instance.
35+
*
36+
* @param predicate
37+
* @return
38+
* @see BooleanBuilder
39+
* @since 2.5.3
40+
*/
41+
static boolean isEmpty(Predicate predicate) {
42+
return new BooleanBuilder().equals(predicate);
43+
}
44+
45+
/**
46+
* Returns the target {@link TypeDescriptor} for the given {@link PathInformation} by either inspecting the field or
47+
* property (the latter preferred) to pick up annotations potentially defined for formatting purposes.
48+
*
49+
* @param path must not be {@literal null}.
50+
* @return
51+
*/
52+
default TypeDescriptor getTargetTypeDescriptor(PathInformation path) {
53+
54+
PropertyDescriptor descriptor = path.getLeafPropertyDescriptor();
55+
56+
Class<?> owningType = path.getLeafParentType();
57+
String leafProperty = path.getLeafProperty();
58+
59+
TypeDescriptor result = descriptor == null //
60+
? TypeDescriptor
61+
.nested(org.springframework.data.util.ReflectionUtils.findRequiredField(owningType, leafProperty), 0)
62+
: TypeDescriptor
63+
.nested(new Property(owningType, descriptor.getReadMethod(), descriptor.getWriteMethod(), leafProperty), 0);
64+
65+
if (result == null) {
66+
throw new IllegalStateException(String.format("Could not obtain TypeDescriptor for PathInformation %s", path));
67+
}
68+
69+
return result;
70+
}
71+
72+
/**
73+
* Returns whether the given collection has exactly one element that is empty (i.e. doesn't contain text). This is
74+
* basically an indicator that a request parameter has been submitted but no value for it.
75+
*
76+
* @param source must not be {@literal null}.
77+
* @return
78+
*/
79+
default boolean isSingleElementCollectionWithEmptyItem(List<?> source) {
80+
return source.size() == 1 && ObjectUtils.isEmpty(source.get(0));
81+
}
82+
83+
/**
84+
* Returns the {@link Predicate} from {@link BooleanBuilder}.
85+
*
86+
* @param builder
87+
* @return
88+
*/
89+
default Predicate getPredicate(BooleanBuilder builder) {
90+
91+
Predicate predicate = builder.getValue();
92+
return predicate == null ? new BooleanBuilder() : predicate;
93+
}
94+
95+
Predicate getPredicate(TypeInformation<?> type, MultiValueMap<String, ?> values, QuerydslBindings bindings);
96+
97+
/**
98+
* Invokes the binding of the given values, for the given {@link PropertyPath} and {@link QuerydslBindings}.
99+
*
100+
* @param dotPath must not be {@literal null}.
101+
* @param bindings must not be {@literal null}.
102+
* @param values must not be {@literal null}.
103+
* @return
104+
*/
105+
default Optional<Predicate> invokeBinding(PathInformation dotPath, QuerydslBindings bindings,
106+
Collection<Object> values, EntityPathResolver resolver,
107+
MultiValueBinding<Path<? extends Object>, Object> defaultBinding) {
108+
Path<?> path = getPath(dotPath, bindings, resolver);
109+
110+
return bindings.getBindingForPath(dotPath).orElse(defaultBinding).bind(path, values);
111+
}
112+
113+
/**
114+
* Returns the {@link Path} for the given {@link PropertyPath} and {@link QuerydslBindings}. Will try to obtain the
115+
* {@link Path} from the bindings first but fall back to reifying it from the PropertyPath in case no specific binding
116+
* has been configured.
117+
*
118+
* @param path must not be {@literal null}.
119+
* @param bindings must not be {@literal null}.
120+
* @return
121+
*/
122+
default Path<?> getPath(PathInformation path, QuerydslBindings bindings, EntityPathResolver resolver) {
123+
Map<PathInformation, Path<?>> paths = new ConcurrentHashMap<>();
124+
125+
Optional<Path<?>> resolvedPath = bindings.getExistingPath(path);
126+
127+
return resolvedPath.orElseGet(() -> paths.computeIfAbsent(path, it -> it.reifyPath(resolver)));
128+
}
129+
130+
/**
131+
* Converts the given source values into a collection of elements that are of the given {@link PropertyPath}'s type.
132+
* Considers a single element list with an empty object an empty collection because this basically indicates the
133+
* property having been submitted but no value provided.
134+
*
135+
* @param source must not be {@literal null}.
136+
* @param path must not be {@literal null}.
137+
* @param conversionService must not be {@literal null}.
138+
* @return
139+
*/
140+
default Collection<Object> convertToPropertyPathSpecificType(List<?> source, PathInformation path, ConversionService conversionService) {
141+
142+
if (source.isEmpty() || isSingleElementCollectionWithEmptyItem(source)) {
143+
return Collections.emptyList();
144+
}
145+
146+
TypeDescriptor targetType = getTargetTypeDescriptor(path);
147+
Collection<Object> target = new ArrayList<>(source.size());
148+
149+
for (Object value : source) {
150+
target.add(getValue(targetType, value, conversionService));
151+
}
152+
153+
return target;
154+
}
155+
156+
@Nullable
157+
default Object getValue(TypeDescriptor targetType, Object value, ConversionService conversionService) {
158+
159+
if (ClassUtils.isAssignableValue(targetType.getType(), value)) {
160+
return value;
161+
}
162+
163+
if (conversionService.canConvert(value.getClass(), targetType.getType())) {
164+
return conversionService.convert(value, TypeDescriptor.forObject(value), targetType);
165+
}
166+
167+
return value;
168+
}
169+
}

src/main/java/org/springframework/data/web/config/QuerydslWebConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,12 @@ public class QuerydslWebConfiguration implements WebMvcConfigurer {
6060
@Lazy
6161
@Bean
6262
public QuerydslPredicateArgumentResolver querydslPredicateArgumentResolver() {
63-
if (beanFactory.containsBean("querydslPredicateBuilder")) { //TODO It might have a better way to check if object exists...
63+
try {
64+
//TODO It might have a better way to check if object exists...
6465
return new QuerydslPredicateArgumentResolver(
6566
beanFactory.getBean("querydslBindingsFactory", QuerydslBindingsFactory.class),
6667
beanFactory.getBean(QuerydslPredicateBuilder.class));
67-
} else {
68+
} catch (Exception ignored) {
6869
return new QuerydslPredicateArgumentResolver(
6970
beanFactory.getBean("querydslBindingsFactory", QuerydslBindingsFactory.class),
7071
conversionService.getIfUnique(DefaultConversionService::getSharedInstance));

0 commit comments

Comments
 (0)