-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot use Specification<T> interface to create subquery predicates #2689
Comments
Can you provide us with a bit more context along with a bit of code how you intend to use |
Yes. So I have multiple implementations of the Specification class 1: public class SpecificationClass1<T> implements Specification<T> {
// here are some private fields
// here are multiple constructors which initialize the private fields
// and here is the implementation of the interface method called toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder)
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Predicate predicate = criteriabuilder.equal(...
// so here I create the Predicate object which I will return
// I have multiple if statements here, because I would like different predicates based on the values of the private fields
return predicate;
}
} Specification class 2: public class SpecificationClass2<T extends SomeClass> implements Specification<T> {
// this class almost looks like the previous one, so
// here are some private fields
// here is a constructor
// and here is the implementation of the interface method called toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder)
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Predicate predicate = criteriabuilder.equal(...
// so here I create the Predicate object which I will return
// I have multiple if statements here, because I would like different predicates based on the values of the private fields
return predicate;
}
} So these are my 2 Now, in the Provider class I have a query built with Criteria API: public Page<ViewModel> findAll(/* some params */)
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<SomeClass> query = criteriaBuilder.createQuery(SomeClass.class);
Root<SomeEntity> root = query.from(SomeEntity.class);
// here I have a subquery for aggregating something, and in the select clause I select the result of the subQuery, and then even sorting is possible based on the result of this aggregation subquery
query.multiselect(/* fields to select */);
query.groupBy(/* field */);
// and here comes the creation of the Predicate object
// Specification1
SpecificationClass1<SomeEntity> specificationClass1 = new SpecificationClass1<>(/* variables to pass to the constructor */);
// Specification2
SpecificationClass2<SomeEntity> specificationClass2 = new SpecificationClass2<>(/* variables to pass to the constructor */);
// then I use the and method of the Specification<T> interface to combine the two specifications
Specification<SomeEntity> specifications = specificationClass1.and(specificationClass2);
// then I call the toPredicate function on the combined specification
Predicate predicate = specifications.toPredicate(root, query, criteriaBuilder);
// and I pass this predicate object to the where clause of the query
query.where(predicate)
// applying sorting
// execute the query and calling helper functions to make the result of the query pageable Until this time, everything works perfectly. But this query is complex enough to use 2 different specification classes and needs to be paginated and so on. To make this pageable I need to know how many rows fit the criteria. So I need a query, which basically does a COUNT(*) and to do that I need a subQuery, because in one of the Specification classes I have having clause (and the query is grouped) and without using a subQuery I cannot get proper result. An other issue here, is that JPQL and also Criteria API does not support counting the number of rows returned by a subQuery, but this can be easily handled by using IN clause. So I do the following: private long countRows(Specification<SomeEntity> specification) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class);
Root<SomeEntity> root = query.from(SomeEntity.class);
// create the subQuery
SubQuery<SomeEntity> subQuery = query.subquery(SomeEntity.class);
Root<SomeEntity> subRoot = subQuery.from(SomeEntity.class);
subQuery.select(subRoot);
// and create exactly the same predicate like in the findAll query but now for a SubQuery
// I need the number of rows when calling the helper method at the end of findAll function so this function is called from findAll
// so I can pass the Specification<SomeEntity> specification as parameter
// at this point this specification is the combination of two specifications
// it would be very nice if it could be used here
// so what I would like to do is
// calling the toPredicate function on the received specification as parameter passing SubQuery object
Predicate predicate = specification.toPredicate(subRoot, subQuery, criteriaBuilder);
// ...
} And at this point comes the problem. The interface method in the If the This is very long, this is my whole problem. But to make it short, the problem is that if I create a Specification class which implements the |
For future references, this could be approached by using a "dummy" query to create
|
I implemented numerous own Specification classes that implement the interface called
Specification<T>
in theorg.springframework.data.jpa.domain
package. ThisSpecification<T>
interface has aPredicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
interface function which has to be implemented along with implementing the interface. This toPredicate function has 3 parameters, theRoot<T>
, theCriteriaQuery<?>
, and theCriteriabuilder
. As long as I want to create the Predicate object for a CriteriaQuery object it works fine. However I have cases, when I would like to use my own Specification classes to create Predicate objects for subqueries. As I have found out this is currently impossible because the toPredicate function requires CriteriaQuery object as its second parameter instead of requiring the common parent ofCriteriaQuery<T>
andSubquery<T>
that could beAbstractQuery<T>
. It would be nice ifSpecification<T>
interface could be used to create Predicate objects for subqueries too. Right now I have to duplicate the code written in my own Specification classes if I would like to use it for subquery predicates.The text was updated successfully, but these errors were encountered: