Leaders Used
Spring Download 1.3.2.RELEASE
QueryDSL 3.7.2
QueryDSL Maven Plugin 1.1.3
Sleep mode 4.3.11. The final
Problem
I currently have a Spring boot application that has some basic CRUD functions using Spring Data JPA (running Hibernate) and auditing using Spring Data Envers. I also have the following endpoint for getting a list of objects from:
http: // localhost: 8080 / test-app / list
Now I wanted to use the new QueryDSL support that Spring offers through the @QuerydslPredicate
annotation. This works great for most fields or sub-entities, but it doesn't seem to work for collections of sub-entities. The documentation, blog posts, etc., do not seem to cover this case - and the only information I can find is that it supports "in" for simple collections (ie String Collections, etc. .).
So my entity is set up something like this:
Person.java
@Data @Entity @Audited public class Person { @Id private long id; private String name; private List<Pet> pets = new ArrayList<>(); }
Pet.java
@Data @Entity @Audited public class Pet { @Id private long id; private int age; }
I generate my Q classes using com.mysema.maven:apt-maven-plugin
, which generates my QPerson
with the following field:
public final ListPath<com.test.Pet, com.test.QPet> pets = this.<com.test.Pet, com.test.QPet>createList("pets", com.test.Pet.class, com.test.QPet.class, PathInits.DIRECT2);
If I try to execute a request for this, I get an exception:
Query:
http: // localhost: 8080 / test-app / list? pets.age = 5
An exception:
10:21:37,523 ERROR [org.springframework.boot.context.web.ErrorPageFilter] (http-/127.0.0.1:8080-1) Forwarding to error page from request [/list] due to exception [null]: java.lang.NullPointerException at org.springframework.util.ReflectionUtils.getField(ReflectionUtils.java:143) [spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE] at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.reifyPath(QuerydslPredicateBuilder.java:185) [spring-data-commons-1.11.2.RELEASE.jar:] at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.reifyPath(QuerydslPredicateBuilder.java:188) [spring-data-commons-1.11.2.RELEASE.jar:] at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPath(QuerydslPredicateBuilder.java:167) [spring-data-commons-1.11.2.RELEASE.jar:] at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.invokeBinding(QuerydslPredicateBuilder.java:136) [spring-data-commons-1.11.2.RELEASE.jar:] at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPredicate(QuerydslPredicateBuilder.java:111) [spring-data-commons-1.11.2.RELEASE.jar:] at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.resolveArgument(QuerydslPredicateArgumentResolver.java:106) [spring-data-commons-1.11.2.RELEASE.jar:] at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.resolveArgument(QuerydslPredicateArgumentResolver.java:48) [spring-data-commons-1.11.2.RELEASE.jar:] at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:78) [spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
Now this query looks like it is trying to solve the Person.pets.age
property. It correctly identifies Person.pets
as a ListPath
, and then tries to identify CompanyAddress.addressLine1
(which seems correct). The problem is that he is trying to use the entity path to get the class, which is ListPath
instead of QPet
:
Field field = ReflectionUtils.findField(entityPath.getClass(), path.getSegment()); Object value = ReflectionUtils.getField(field, entityPath);
The following query works as expected:
http: // localhost: 8080 / test-app / list? name = Bob
I was expecting the following predicate to be built using ?pets.age=5
:
QPerson.person.pets.any().age.eq(5)
Is this possible with Spring QuerydslPredicate support? Or do I need to manually build predicates from query parameters?
Additional question
As an additional question, you can use QuerydslPredicate. Let's say I have firstName and lastName for pets, and I want to run the query only with name=Bob
:
http: // localhost: 8080 / test-app / pet / list? name = Bob
I would like the query predicate to be constructed as follows:
final BooleanBuilder petBuilder = new BooleanBuilder(); petBuilder.and(QPet.firstName.equals("Bob").or(QPet.lastName.equals("Bob")));
Is it possible? From a look at the QuerydslBinderCustomizer
configuration QuerydslBinderCustomizer
this does not look like you need to bind a Q class field. I assume that what I want to do is not supported.
If this is not possible, I will stick with it manually, creating a predicate and passing it to the repository.