Using the JPA Criteria API, I want to sort by a column that is created as part of the select clause.
Example in SQL:
SELECT m.name as name,
LOWER(m.name) as lowerName
FROM MyEntityTable as m
WHERE ...
Here, lowerName is only part of the selection.
At the moment, my intention is to use the Specification API of JPA because I want to compose complex conditions dynamically.
Thus, I have something like this (hasName is just an example of more complex condition chains):
mySearchResultRepository.findAll(
Specification.allOf(
MySearchResultRepository.hasName("foo"),
// further conditions depending on context ...
),
pageRequest // Wants to sort by 'lowerName'
);
mySearchResultRepositoryimplementsJpaSpecificationExecutor, which yields thefindBymethod.- The
pageRequestcontains the column to be sorted by. In the example, this islowerName.
Problem
- At the moment
lowerNameis not part of the entity such that Hibernate complains that it cannot sort it:No property 'lowerName' found for type 'MyEntity'.
Question
- How to express the additional selection of
LOWER(obj.name) as lowerNamewhen using the Specification API of JPA?
Solution Attempt
I found a lower method in criteriaBuilder, so I tried this:
static Specification<MyEntitySearchResult> augmentWithLowerName() {
return (entity, criteriaQuery, criteriaBuilder) -> {
// Add an alias for lowerName as side effect (does not work)
Selection<String> lowerNameSelection = criteriaBuilder.lower(entity.get("name")).alias("lowerName");
Selection<?> oldSelection = criteriaQuery.getSelection();
criteriaQuery.multiselect(oldSelection, lowerNameSelection); work
// Only executed for the side effect, so return true-predicate
return criteriaBuilder.and();
};
}
mySearchResultRepository.findAll(
Specification.allOf(
MySearchResultRepository.hasName("foo"),
// further conditions depending on context ...
// Add virtual 'lowerName' column to selection for sorting
MySearchResultRepository.augmentWithLowerName()
),
pageRequest // Wants to sort by 'lowerName'
);
However, this does not work.
What is the proper way to extend the selection when using the Specification API? Is this possible at all using the Criteria API?
The
JpaSpecificationExecutoronly provides a handful of methods, which may be too limited to express everything.However,
Specification.toPredicateallows to create a Predicate that can be used with the regular JPA Criteria API.Example: