3

I am using JpaRepository Pageable query for pagination. All the things are working fine except the sort field case sensitive issue. Following query is used for getting list.

Pageable pageable = null;
if (paginationRequestDTO.getSortOrder().equalsIgnoreCase("desc"))
    pageable = new PageRequest(page, size, Sort.Direction.DESC, sortfiled);
else
    pageable = new PageRequest(page, size, Sort.Direction.ASC, sortfiled);

Page<Audi> audiPage = null;
audiencePage = audiRepository.search(paginationRequestDTO.getSearchKey(), pageable);

Audi table values are: apple,az,Ajay,Bala. when i search with sortorder of asc and sort field name, original output : Ajay,Bala,apple,az. Expected output: Ajay,apple,az,Bala.

I am using mysql database. table engine - Innodb,characterst-utf8,collate-utf8_bin.

Please note that its not duplicate question.i didn't get exact answer for this question.thanks in advance.

Rakesh
  • 4,004
  • 2
  • 19
  • 31
Balasubramanian
  • 700
  • 7
  • 26
  • 1
    Using `collate-utf8_bin` makes all the difference. Check https://stackoverflow.com/questions/5526334/what-effects-does-using-a-binary-collation-have – harsh Nov 09 '18 at 14:08
  • I changed collation in table to utf8_general_ci still not working. – Balasubramanian Nov 10 '18 at 06:06

1 Answers1

-1

Edited: as harsh pointed out correctly, this needs to be solved on database level, using correct collation. This is important, because you probably want to have an index on the sort column for best performance.

But there are other use cases, which could combine filtering together with sorting by something other, than a pure column value, e.g. by length of description, sum or average of a column, etc. For that reason, I am including a JPA solution:

I struggled with this recently and I am afraid, that the Pageable interface does not support this out of box.

The solution was to use EntityManager, CriteriaBuilder, CriteriaQuery, Specification and implement the paging manually. You can find the solution here.

You need to construct the Page object manually:

public Page<Audi> getPage(int pageNumber, int pageSize, String descriptionFilter, Sorting sorting) {
    return new PageImpl<>(
            getPageItems(pageNumber, pageSize, descriptionFilter, sorting),
            PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.ASC, sorting.name())),
            getTotalCount(descriptionFilter)
    );
}

getPageItems selects the page using LIMIT and OFFSET

private List<Audi> getPageItems(int pageNumber, int pageSize, String descriptionFilter, Sorting sorting) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Audi> query = cb.createQuery(Audi.class);
    Root<Audi> root = query.from(Audi.class);
    query.where(createSpecification(descriptionFilter).toPredicate(root, query, cb));

    if (sorting.equals(Sorting.descriptionCaseInsensitive)) {
        query.orderBy(cb.asc(cb.lower(root.get("description"))));
    } else {
        throw new UnsupportedOperationException("Unsupported sorting: " + sorting.name());
    }
    query.select(root);

    return em.createQuery(query)
            .setFirstResult(pageNumber * pageSize)
            .setMaxResults(pageSize)
            .getResultList();
}

getTotalCount selects count(distinct(*)),

private long getTotalCount(String descriptionFilter) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Long> query = cb.createQuery(Long.class);
    Root<Audi> root = query.from(Audi.class);
    query.where(createSpecification(descriptionFilter).toPredicate(root, query, cb));
    query.select(cb.countDistinct(root));

    // getSingleResult can return null, if no rows fulfill the predicate
    return Optional.ofNullable(em.createQuery(query).getSingleResult()).orElse(0L);
}

Both reuse the same predicate, which filters rows:

private Specification<Audi> createSpecification(String descriptionFilter) {
    return Specification.where(
            (root, query, criteriaBuilder) ->
                    criteriaBuilder.like(criteriaBuilder.lower(root.get("description")), "%" + descriptionFilter.toLowerCase() + "%")
    );
}
ygor
  • 1,726
  • 1
  • 11
  • 23
  • This issue has nothing to do with `Pageable` and its about the collation type being used – harsh Nov 09 '18 at 14:12
  • I see your point. I totally forgot about collation. My solution would work, however, it would not be able to make use of any indexes, because the generated SQL would contain ORDER by LOWER(column). – ygor Nov 09 '18 at 14:18
  • Thanks for your replay. i have to use only pageable not EntityManager, CriteriaBuilder, CriteriaQuery, Specification. so changed collation also, still not working. – Balasubramanian Nov 10 '18 at 07:39