spring-data-commons icon indicating copy to clipboard operation
spring-data-commons copied to clipboard

QuerydslPredicateBuilder looks at Type properties instead of QType [DATACMNS-1760]

Open spring-projects-issues opened this issue 5 years ago • 5 comments

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)

spring-projects-issues avatar Jul 01 '20 16:07 spring-projects-issues

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?

spring-projects-issues avatar Jul 01 '20 17:07 spring-projects-issues

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

spring-projects-issues avatar Jul 09 '20 14:07 spring-projects-issues

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.

OrangeDog avatar Oct 14 '22 08:10 OrangeDog

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)
        ...

OrangeDog avatar Jan 24 '24 15:01 OrangeDog