QuerydslPredicateBuilder looks at Type properties instead of QType [DATACMNS-1760]
James Howe opened DATACMNS-1760 and commented
When constructing a Querydsl predicate from request parameters, the source type is inspected to determine which parameter names to use, instead of the query type.
This results in exceptions for properties that exist but are not queryable. Instead they should behave the same as properties that don't exist, and be ignored.
@Data
@Entity
public class Example {
private String one;
@QueryType(PropertyType.NONE)
private String two;
private transient String three;
}
@Controller
public class ExampleController {
@RequestMapping("/")
public void test(@QuerydslPredicate(root = Example.class) Predicate predicate) {
}
}
GET /?one=foo -> success
GET /?two=foo -> error
GET /?three=foo -> error
GET /?four=foo -> success
java.lang.IllegalArgumentException: Unable to find field two on class com.example.QExample!
at org.springframework.data.util.ReflectionUtils.findRequiredField(ReflectionUtils.java:211)
at org.springframework.data.querydsl.binding.PropertyPathInformation.lambda$reifyPath$3(PropertyPathInformation.java:139)
at java.base/java.util.Optional.orElseGet(Optional.java:369)
at org.springframework.data.querydsl.binding.PropertyPathInformation.reifyPath(PropertyPathInformation.java:135)
at org.springframework.data.querydsl.binding.PropertyPathInformation.reifyPath(PropertyPathInformation.java:125)
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.lambda$null$0(QuerydslPredicateBuilder.java:153)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1705)
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.lambda$getPath$1(QuerydslPredicateBuilder.java:153)
at java.base/java.util.Optional.orElseGet(Optional.java:369)
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPath(QuerydslPredicateBuilder.java:153)
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.invokeBinding(QuerydslPredicateBuilder.java:135)
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPredicate(QuerydslPredicateBuilder.java:116)
at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.resolveArgument(QuerydslPredicateArgumentResolver.java:125)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
... 107 more
Affects: 2.3.1 (Neumann SR1)
Oliver Drotbohm commented
Thanks for reporting this. Just to make sure:
When constructing a Querydsl predicate from request parameters, the source type is inspected to determine which parameter names to use, instead of the query type.
You mean the opposite, don't you? The exception clearly shows that the property is looked up on the query type, but fails, which we of course should handle the way you describe.
As the call for three also fails, does that mean that transient properties are ignored as well?
James Howe commented
You mean the opposite, don't you?
No. Here is where it checks whether the request parameter matches something. Because it's checking the source type, it accepts them all as valid paths.
Then later when it tries to actually build a query it throws the exception, because these paths do not exist on the query object. Transient properties do not exist in the database, so QueryDSL doesn't generate anything for them
There's no easy workaround other than just patching it.
You can subclass QuerydslPredicateArgumentResolver and handle the exception in a different way, but it's already consumed the parameter at that point so it's tricky to get the expected behaviour.
This issue is still present in 3.x but now a different exception is thrown:
java.lang.NullPointerException: Cannot invoke "java.lang.reflect.Field.get(Object)" because "field" is null
at org.springframework.util.ReflectionUtils.getField(ReflectionUtils.java:672)
at org.springframework.data.querydsl.binding.PropertyPathInformation.reifyPath(PropertyPathInformation.java:114)
at org.springframework.data.querydsl.binding.PropertyPathInformation.reifyPath(PropertyPathInformation.java:101)
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.lambda$getPath$0(QuerydslPredicateBuilder.java:164)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.lambda$getPath$1(QuerydslPredicateBuilder.java:164)
at java.base/java.util.Optional.orElseGet(Optional.java:364)
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPath(QuerydslPredicateBuilder.java:164)
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.invokeBinding(QuerydslPredicateBuilder.java:146)
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPredicate(QuerydslPredicateBuilder.java:115)
at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolverSupport.getPredicate(QuerydslPredicateArgumentResolverSupport.java:107)
at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.resolveArgument(QuerydslPredicateArgumentResolver.java:78)
...