Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save tuhin47/18aba4e66d366cd4e7b5f72e572d45c5 to your computer and use it in GitHub Desktop.
Save tuhin47/18aba4e66d366cd4e7b5f72e572d45c5 to your computer and use it in GitHub Desktop.
Spring boot Server Side Pagination Using JPA

GenericSpecification.java

public class GenericSpecification<T> implements Specification<T> {
    private static final long serialVersionUID = 1900581010229669687L;

    private List<SearchCriteria> list;

    public GenericSpecification() {
        this.list = new ArrayList<>();
    }

    public void add(SearchCriteria criteria) {
        if (criteria.getValue() != null && !criteria.getValue().toString().equals("")) list.add(criteria);
    }

    private Join<?, ?> recursiveRootBuilder(Join<?, ?> joinColumn, String[] keys, int index) {
        if (index == keys.length - 2) return joinColumn;
        return recursiveRootBuilder(joinColumn.join(keys[index], JoinType.LEFT), keys, index + 1);
    }

    private Path<String> getStringPath(Root<T> root, String key) {
        if (key.contains(".")) {
            String[] fetchKey = key.split("\\.");
            String lastKey = fetchKey[fetchKey.length - 1];
            return recursiveRootBuilder(root.join(fetchKey[0], JoinType.LEFT), fetchKey, 0).get(lastKey);
        }
        return root.get(key);
    }


    @Override
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

        //create a new predicate list
        List<Predicate> predicates = new ArrayList<>();
        //add add criteria to predicates
        for (SearchCriteria criteria : list) {
            Path<String> path = null;
            if (criteria.getOperation() != SearchOperation.FETCH) path = getStringPath(root, criteria.getKey());
            if (path == null) {
                log.warn("No path variable found");
            } else
                switch (criteria.getOperation()) {
                    case GREATER_THAN:
                        predicates.add(builder.greaterThan(
                                path, criteria.getValue().toString()));
                        break;
                    case LESS_THAN:
                        predicates.add(builder.lessThan(
                                path, criteria.getValue().toString()));
                        break;
                    case GREATER_THAN_EQUAL:
                        predicates.add(builder.greaterThanOrEqualTo(
                                path, criteria.getValue().toString()));
                        break;
                    case LESS_THAN_EQUAL:
                        predicates.add(builder.lessThanOrEqualTo(
                                path, criteria.getValue().toString()));
                        break;
                    case NOT_EQUAL:
                        predicates.add(builder.notEqual(
                                path, criteria.getValue()));
                        break;
                    case EQUAL:
                        predicates.add(builder.equal(
                                path, criteria.getValue()));
                        break;
                    case MATCH:
                        predicates.add(builder.like(
                                builder.lower(path),
                                "%" + criteria.getValue().toString().toLowerCase() + "%"));
                        break;
                    case MATCH_END:
                        predicates.add(builder.like(
                                builder.lower(path),
                                criteria.getValue().toString().toLowerCase() + "%"));
                        break;
                    case IN:
                        List<?> values = (List<?>) criteria.getValue();
                        if (values.size() > 0) predicates.add(path.in(values));
                        break;
                    case FETCH:
                        break;
                    default:
                        throw new RuntimeException("Operation not supported yet");
                }
        }

        return builder.and(predicates.toArray(new Predicate[0]));
    }

}

NavigationService.java

public interface NavigationService {
     Page<?> getReportData(Object filterDTO, Pageable pageable);
}

RecordNavigationManager.java

@AllArgsConstructor
public class RecordNavigationManager {


    NavigationService m_navigationService;

    public ResponseEntity<?> getNavigationResponse(Object searchCriteria, HttpServletRequest request) throws JsonProcessingException {
        Page<?> reportData = getNavigationPage(searchCriteria, request);
        return new ResponseEntity<>(reportData, HttpStatus.OK);
    }

    public Page<?> getNavigationPage(Object searchCriteria, HttpServletRequest request) throws JsonProcessingException {
        int size = getValueFromRequest(request, "size");
        int page = getValueFromRequest(request, "page");
        List<Sort.Order> orders = getSortOrders(request);
        Pageable pageable = PageRequest.of(page, size, Sort.by(orders));
        return m_navigationService.getReportData(searchCriteria, pageable);
    }

    private List<Sort.Order> getSortOrders(HttpServletRequest request) throws JsonProcessingException {
        String[] sort = {"id", "desc"};
        String sorts = request.getParameter("sort");

        if (sorts != null) {
//            String decodeURIComponent = EncodingUtil.decodeURIComponent(sorts);
//            sort = new ObjectMapper().readValue(decodeURIComponent,String[].class);
            sort = sorts.split(",");
        }
        List<Sort.Order> orders = new ArrayList<>();
        if (sort[0].contains(",")) {
            for (String sortOrder : sort) {
                String[] _sort = sortOrder.split(",");
                orders.add(new Sort.Order(getSortDirection(_sort[1]), _sort[0]));
            }
        } else {
            orders.add(new Sort.Order(getSortDirection(sort[1]), sort[0]));
        }
        return orders;
    }

    private int getValueFromRequest(HttpServletRequest request, String param) {
        String paramValue = request.getParameter(param);
        if (paramValue == null) {
            switch (param) {
                case "size":
                    return 10;
                case "page":
                    return 0;
            }
        }
        return Integer.parseInt(paramValue);
    }

    private Sort.Direction getSortDirection(String direction) {
        if (direction.equals("asc")) {
            return Sort.Direction.ASC;
        } else if (direction.equals("desc")) {
            return Sort.Direction.DESC;
        }

        return Sort.Direction.ASC;
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment