How to implement pagination in spring boot with hibernate

后端 未结 5 1060
野的像风
野的像风 2020-12-15 07:21

I am using spring boot with hibernate and I want to use pagination in my project. I have searched on google and saw many examples but I am unable to implement it in my proje

5条回答
  •  孤城傲影
    2020-12-15 08:00

    How to Implement Dynamic Pagination Using a Native Query

    Here, you can find the repository and service layers and your data transfer object (DTO), which will be used for mapping our result and sending it to the controller layer.

    public interface CustomSomethingRepository {
        List findPagedResultBySomethingElseId(long somethingElseId, int offset, int limit);
    }
    
    public class SomethingRepositoryImpl implements CustomSomethingRepository {
        @Autowired
        private EntityManager em;
    
        @SuppressWarnings("unchecked")
        @Override
        public List findPagedResultBySomethingElseId(long somethingElseId, int offset, int limit) {
            String query = "select s.* from Something s "
                    + "join somethingelse selse on selse.id = s.fk_somethingelse "
                    + "where selse.id = :somethingElseId "
                    + "order by selse.date";
            Query nativeQuery = em.createNativeQuery(query);
            nativeQuery.setParameter("somethingElseId", somethingElseId);
            //Paginering
            nativeQuery.setFirstResult(offset);
            nativeQuery.setMaxResults(limit);
            final List resultList = nativeQuery.getResultList();
            List somethingList = Lists.newArrayList();
            resultList.forEach(object -> somethingList.add(//map obj to something));
            return somethingList;
        }
    }
    

    Hibernate translates your query as follows:

    SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( select TOP(?) t as page0_ from Something s join s.somethingelse as selse order by selse.date ) inner_query ) SELECT page0_ FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?
    
    
    @Service
    public class SomethingService {
        private SomethingRepository somethingRepository;
        @Autowired
        public SomethingService(SomethingRepository somethingRepository){
            this.somethingRepository = somethingRepository;
        }
        @Transactional(readOnly=true)
        public PageDto getSomething(long somethingElseId, int page, int size){
             List somethings = somethingRepository.findBySomethingElseId(somethingElseId, offset, limit);
            return new PagedResult<>(somethings
                    .stream()
                    .map(SomethingDto::createDto)
                    .sorted(comparing(SomethingDto::getDatum))
                    .collect(toList()), somethings.getTotalElements(), somethings.getTotalPages();
        }
    }
    @Controller
    //....
    public class PagedResult {
        public static final long DEFAULT_OFFSET = 0;
        public static final int DEFAULT_MAX_NO_OF_ROWS = 100;
        private int offset;
        private int limit;
        private long totalElements;
        private List elements;
        public PagedResult(List elements, long totalElements, int offset, int limit) {
            this.elements = elements;
            this.totalElements = totalElements;
            this.offset = offset;
            this.limit = limit;
        }
        public boolean hasMore() {
            return totalElements > offset + limit;
        }
        public boolean hasPrevious() {
            return offset > 0 && totalElements > 0;
        }
        public long getTotalElements() {
            return totalElements;
        }
        public int  getOffset() {
            return offset;
        }
        public int getLimit() {
            return limit;
        }
        public List getElements() {
            return elements;
        }
    }
    

    Pros and Cons Pros: Fewer SQL queries will be generated, compared to using Spring Data. These complex queries cannot be written in Spring Data, and we have to specify our query as a native one, which can still be paged by using this methodology.

    Cons: The "object" array must map to a Java object. It is painful and hard to maintain.

    How to Implement OffsetLimit Pagination With Spring Data As far as I know, there is no "out-of-the-box" support for what you need in default Spring Data repositories. But you can create a custom implementation of Pageable objects that will take limit/offset parameters.

    Make a pageable object and pass it to PaginationAndSortingRepository:

    public class OffsetLimitRequest implements Pageable {
        private int limit;
        private int offset;
        public OffsetLimitRequest(int offset, int limit){
            this.limit = limit;
            this.offset = offset;
        }
            @Override
        public int getPageNumber() {
            return 0;
        }
        @Override
        public int getPageSize() {
            return limit;
        }
        @Override
        public int getOffset() {
            return offset;
        }
        ....
    }
    

    It means there is no need to change the repository layer. The only change you would need is to make is to the service layer, as follows:

    @Service
    public class SomethingService {
        private SomethingRepository somethingRepository;
        @Autowired
        public SomethingService(SomethingRepository somethingRepository){
            this.somethingRepository = somethingRepository;
        }
        @Transactional(readOnly=true)
        public PageDto getSomething(long somethingElseId, int page, int size){
            Page somethings = somethingRepository.findBySomethingElseId(somethingElseId, new OffsetLimitRequest(offset, limit));
            return new PageDto(somethings.getContent()
                    .stream()
                    .map(SomethingDto::createDto)
                    .sorted(comparing(SomethingDto::getDatum))
                    .collect(toList()), somethings.getTotalElements(), somethings.getTotalPages();
        }
    }
    

    Note that you don't need to map the result manually, and it will take off a good amount of time from development.

提交回复
热议问题