106

I am trying to convert list to page in spring. I have converted it using

new PageImpl(users, pageable, users.size());

But now I having problem with sorting and pagination itself. When I try passing size and page, the pagination doesn't work.

Here's the code I am using.

My Controller

    public ResponseEntity<User> getUsersByProgramId(
        @RequestParam(name = "programId", required = true) Integer programId Pageable pageable) {

    List<User> users = userService.findAllByProgramId(programId);
    Page<User> pages = new PageImpl<User>(users, pageable, users.size());

    return new ResponseEntity<>(pages, HttpStatus.OK);
}

Here is my user Repo

public interface UserRepo extends JpaRepository<User, Integer>{

public List<User> findAllByProgramId(Integer programId);

Here is my service

    public List<User> findAllByProgramId(Integer programId);
user3127109
  • 3,421
  • 8
  • 24
  • 33
  • 2
    Seems like you use it in a wrong way. You retrive all elements of the table, then wrap 'em into `PageImpl`. But you should pass `PageRequest` to the repository inside your service to make it work. Can you also post the code of your service method and repository? – Vladimir Vagaytsev Jun 10 '16 at 13:35
  • Yes sure I will post them but they are quite length regarding the Impl method. But I will post the repository one – user3127109 Jun 10 '16 at 13:43

17 Answers17

160

I had the same problem. I used subList:

final int start = (int)pageable.getOffset();
final int end = Math.min((start + pageable.getPageSize()), users.size());
final Page<User> page = new PageImpl<>(users.subList(start, end), pageable, users.size());
Michael Peterson
  • 10,383
  • 3
  • 54
  • 51
shilaimuslm
  • 2,005
  • 1
  • 10
  • 11
  • @gotson — `PageImpl` is just not working that way. The answer of @dubonzi actually has it right. – Oliver Drotbohm Dec 11 '16 at 12:44
  • 5
    @OliverGierke i agree when you can retrieve the `Page` directly from the repository, but it's not always the case, for instance if your entity has a OneToMany association that gets you a `List`, and you want to expose that as a `Page`. – gotson Dec 11 '16 at 12:45
  • 5
    Have not tried it, but even if it works, it's not efficient. It's retrieving all the users from the database and returns a sub-list. The ideal would be to retrieve from the database only the paginated users that will be served from the request. – drumaddict Dec 31 '17 at 11:18
  • 8
    `org.springframework.data.domain.Pageable.getOffset()` returns a long, so I think that you need either cast `pageable.getOffset()` to long or change the type of `start` – chomp Jul 28 '19 at 21:58
  • 1
    end can be repaced with `Math.min` ;) like `int end = Math.min((start + pageable.getPageSize()), users.size());` – Gaspar May 11 '20 at 15:16
  • Yeah, intellij also told me to replace this with Math.min. – Dimitri Kopriwa May 24 '20 at 20:05
  • don't forget that PageImpl doesn't sort, so sort value from the Pageable has no effect – ema Dec 01 '20 at 20:12
  • 1
    Updated solution to deal with casting and use of min(). – Michael Peterson Feb 25 '21 at 04:02
  • 1
    Works great for my use case, also added a step where i sort the list before calling sublist (to have consistent results between database calls) – manfall19 Dec 27 '21 at 03:29
  • not good perfectly sometimes says itemsize 20 but it is 18 then it crashes @MichaelPeterson. did you come across with that problem. – newUser Jul 15 '22 at 09:33
36

There is a Page implementation for that:

Page<Something> page = new PageImpl<>(yourList);
Sachin Gaur
  • 679
  • 7
  • 6
23

As indicated in the reference documentation, Spring Data repositories support pagination on query methods by simply declaring a parameter of type Pageable to make sure they're only reading the data necessary for the requested Page.

Page<User> page = findAllByProgramId(Integer programId, Pageable pageable);

That would return a Page object with the page size/settings defined in your Pageable object. No need to get a list and then try to create a page out of it.

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
dubonzi
  • 1,492
  • 1
  • 21
  • 31
  • 14
    Solves OP's problem but not the question. Suppose we have a database object we need to transform (e.g. a `User` but we want to send pages of a `UserDetails` DTO which contains some subset of that data to the frontend). In that case creating pages manually might be necessary. – Matt Mar 24 '20 at 20:11
  • OP doesn't mention anything related to DTO's or transforming the data, so I dont see how it doesn't answer the question. – dubonzi Mar 25 '20 at 21:07
19

You should do it like advised by the dubonzi's answer.

If you still want to use pagination for a given List use PagedListHolder:

List<String> list = // ...

// Creation
PagedListHolder page = new PagedListHolder(list);
page.setPageSize(10); // number of items per page
page.setPage(0);      // set to first page

// Retrieval
page.getPageCount(); // number of pages 
page.getPageList();  // a List which represents the current page

If you need sorting, use another PagedListHolder constructor with a MutableSortDefinition.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
7
Try This:

public Page<Patient> searchPatientPage(SearchPatientDto patient, int page, int size){
        List<Patient> patientsList = new ArrayList<Patient>();
        Set<Patient> list=searchPatient(patient);
        patientsList.addAll(list);
        int start =  new PageRequest(page, size).getOffset();
        int end = (start + new PageRequest(page, size).getPageSize()) > patientsList.size() ? patientsList.size() : (start + new PageRequest(page, size).getPageSize());

        return new PageImpl<Patient>(patientsList.subList(start, end), new PageRequest(page, size), patientsList.size());
    }
naveen kumar
  • 81
  • 1
  • 3
  • Great Code but I guess it is better to check start and end as below if(start <= end ) then return new PageImpl(patientsList.subList(start, end), new PageRequest(page, size), patientsList.size()); since if start biggr than end it wont wont trully – Meysam Fathee Panah Apr 21 '19 at 19:36
7

This could be the solution. Sorting and pagination will work too this way:

Controller:

public ResponseEntity<User> getUsersByProgramId(
   @RequestParam(name = "programId", required = true) Integer programId Pageable pageable) {
       Page<User> usersPage = userService.findAllByProgramId(programId, pageable);
       Page<User> pages = new PageImpl<User>(usersPage.getContent(), pageable, usersPage.getTotalElements());

       return new ResponseEntity<>(pages, HttpStatus.OK);
}

Service:

Page<User> findAllByProgramId(Integer programId, Pageable pageable);

Repository:

public interface UserRepo extends JpaRepository<User, Integer>{
       public Page<User> findAllByProgramId(Integer programId, Pageable pageable);
}

This way, we can also return different page of entity too.

Ashish Sharma
  • 617
  • 6
  • 15
6

In the JHipster framework there is an interface for such things PageUtil:

static <T> Page<T> createPageFromList(List<T> list, Pageable pageable) {
    if (list == null) {
        throw new IllegalArgumentException("To create a Page, the list mustn't be null!");
    }

    int startOfPage = pageable.getPageNumber() * pageable.getPageSize();
    if (startOfPage > list.size()) {
        return new PageImpl<>(new ArrayList<>(), pageable, 0);
    }

    int endOfPage = Math.min(startOfPage + pageable.getPageSize(), list.size());
    return new PageImpl<>(list.subList(startOfPage, endOfPage), pageable, list.size());
}
ema
  • 891
  • 11
  • 21
3

Implemented based on @shilaimuslm comment. In this case an exception will not be thrown if the start > end in subList.

List<User> users = // ...

Pageable paging = PageRequest.of(pagePagination, sizePagination);
int start = Math.min((int)paging.getOffset(), users.size());
int end = Math.min((start + paging.getPageSize()), users.size());

Page<User> page = new PageImpl<>(users.subList(start, end), paging, users.size());
Paul Rozhkin
  • 61
  • 1
  • 6
3

You can use this generic function for converting List to page.

public static<T> Page<T> convertToPage(List<T> objectList, Pageable pageable){
    int start = (int) pageable.getOffset();
    int end = Math.min(start+pageable.getPageSize(),objectList.size());
    List<T> subList = start>=end?new ArrayList<>():objectList.subList(start,end);
    return new PageImpl<>(subList,pageable,objectList.size());
}
1
//1) For a boot application create a paging repository interface
public interface PersonRepository extends PagingAndSortingRepository<Person, 
String> {
// Common CURD method are automatically implemented
}
//2) create a service Interface
public interface PersonService {
    Page<Person> listAllByPage(Pageable pageable); // Use common CURD findAll() method
    Page<Object> listSpecByPage(Pageable pageable, String x);

}
//3) create a service Impl Class of service interface
@Service
public class PersonServiceImpl implements PersonService {

    final PersonRepository personRepository;

    @Autowired
    PersonServiceImpl(PersonRepository personRepository){
    this.personRepository = personRepository;
    }

    @Override
    public Page<Person> listAllByPage(Pageable pageable) {
          return personRepository.findAll(pageable);
    }
    @Override
    public Page<Object> listSpecByPage(Pageable pageable, String path) {
        List<Object> objectlist = new ArrayList<Object>();
        // Do your process to get output in a list by using node.js run on a *js file defined in 'path' varriable
        Page<Object> pages1 = new PageImpl<Object>(objectlist, pageable, objectlist.size());
        return pages1;
    }

}
//4) write your controller
public class PersonController {

    final PersonService personService;

    @Autowired
    PersonController( PersonService personService ){
        this.personService = personService;
    }

    @GetMapping("/get") // Use of findALL() function
    Page<Person> listed( Pageable pageable){
        Page<Person> persons = personService.listAllByPage(pageable);
        return persons;
    } 
    @GetMapping("/spec") // Use of defined function
    Page<Object> listSpec( Pageable pageable, String path){
        Page<Object> obj = personService.listSpecByPage(pageable, path);
        return obj;
   } 

}
1

u didn't made paged result

new PageImpl<User>(users, pageable, users.size()); does not make paged result implicitly,

in this context, pageable argument just makes meta-data of Page object like page, offset, size... etc

So you have to use Repository method like

Page<User>findAllByProgramId(Integer programId, Pageable pageable);

Implermine
  • 323
  • 3
  • 8
1

From SpringBoot 2.4 you can use PageableExecutionUtils as below:

        Query query = new Query();
        query.addCriteria(Criteria.where("ownedParkByOwner").ne(true));

        List<PlaceV2> placeV2s = mongoTemplate.find(query, PlaceV2.class);

        Page<PlaceV2> placeV2Page = PageableExecutionUtils.getPage(
            placeV2s,
            pageable,
            placeV2s::size);

sendon1982
  • 9,982
  • 61
  • 44
0

Thanks guys below code is working in my case

    int start = pageble.getOffset();
    int end = (start + pageble.getPageSize()) > vehicleModelsList.size() ? vehicleModelsList.size() : (start + pageble.getPageSize());
ram
  • 21
  • Welcome to StackOverflow! In what way is this answer different from [this answer](http://stackoverflow.com/a/37771947/5488275)? If it's quite the same, please rethink your answer (whether it should still be here) and upvote the already given answer. – Nander Speerstra Mar 27 '17 at 13:32
0

Have you tried extending your repository to PagingAndSortingRepository?

public interface UserRepo extends PagingAndSortingRepository<Ticket, Integer> {
    Page<User> findAllByProgramId(Integer programId, Pageable pageable);
}

Service

Page<User> findAllByProgramId(Integer programId, Pageable pageable);

I assume you are using interface to the service:

Borgy Manotoy
  • 1,960
  • 5
  • 27
  • 42
0

Instead of returing complete array list take subblist as per your requirement. You will get 'offset' and size from 'pageable' object in request body.

new PageImpl<User>(users.subList(start, end), pageable, users.size());

sreerag
  • 131
  • 1
  • 5
0

This is the correct answer to pagging a list

public ResponseEntity<User> getUsersByProgramId(
            @RequestParam(name = "programId", required = true) Integer programId, Pageable pageable) {
        List<User> users = userService.findAllByProgramId(programId);

        final int toIndex = Math.min((pageable.getPageNumber() + 1) * pageable.getPageSize(),
                    bidList.size());
        final int fromIndex = Math.max(toIndex - pageable.getPageSize(), 0);
        Page<User> pages = new PageImpl<User>(users.subList(fromIndex, toIndex), pageable, users.size());
    
        return new ResponseEntity<>(pages, HttpStatus.OK);
}
0

I was facing the same problem. I solved it by first obtaining the total value, then setting PageImpl

Long total =  users.size();
Page<User> pages = new PageImpl<User>(users, pageable, total );