Page<> vs Slice<> when to use which?

Page extends Slice and knows the total number of elements and pages available by triggering a count query. From the Spring Data JPA documentation:

A Page knows about the total number of elements and pages available. It does so by the infrastructure triggering a count query to calculate the overall number. As this might be expensive depending on the store used, Slice can be used as return instead. A Slice only knows about whether there’s a next Slice available which might be just sufficient when walking through a larger result set.


The main difference between Slice and Page is the latter provides non-trivial pagination details such as total number of records(getTotalElements()), total number of pages(getTotalPages()), and next-page availability status(hasNext()) that satisfies the query conditions, on the other hand, the former only provides pagination details such as next-page availability status(hasNext()) compared to its counterpart Page. Slice gives significant performance benefits when you deal with a colossal table with burgeoning records.

Let's dig deeper into its technical implementation of both variants.

  • Page
static class PagedExecution extends JpaQueryExecution {
    @Override
    protected Object doExecute(final AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) {

        Query query = repositoryQuery.createQuery(accessor);
        return PageableExecutionUtils.getPage(query.getResultList(), accessor.getPageable(),
                () -> count(repositoryQuery, accessor));
    }

    private long count(AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) {

        List<?> totals = repositoryQuery.createCountQuery(accessor).getResultList();
        return (totals.size() == 1 ? CONVERSION_SERVICE.convert(totals.get(0), Long.class) : totals.size());
    }
}

If you observe the above code snippet, PagedExecution#doExecute method underlyingly calls PagedExecution#count method to get the total number of records satisfying the condition.

  • Slice
    static class SlicedExecution extends JpaQueryExecution {
    @Override
    protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) {

        Pageable pageable = accessor.getPageable();
        Query createQuery = query.createQuery(accessor);

        int pageSize = 0;
        if (pageable.isPaged()) {

            pageSize = pageable.getPageSize();
            createQuery.setMaxResults(pageSize + 1);
        }

        List<Object> resultList = createQuery.getResultList();

        boolean hasNext = pageable.isPaged() && resultList.size() > pageSize;

        return new SliceImpl<>(hasNext ? resultList.subList(0, pageSize) : resultList, pageable, hasNext);

    }
}

If you observe the above code snippet, to findout whether next set of results present or not (for hasNext()) the SlicedExecution#doExecute method always fetch extra one element(createQuery.setMaxResults(pageSize + 1)) and skip it based on the pageSize condition(hasNext ? resultList.subList(0, pageSize) : resultList).

  • Application:
    • Page

      Use when UI/GUI expects to displays all the results at the initial stage of the search/query itself, with page numbers to traverse(ex., bankStatement with pagenumbers)

    • Slice

      Use when UI/GUI expects to doesnot interested to show all the results at the initial stage of the search/query itself, but intent to show the records to traverse based on scrolling or next button click event (ex., facebook feed search)