在Spring中将列表转换为页面

107

我正在尝试在Spring中将列表转换为页面。我已经使用

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

将其转换,但现在我遇到了排序和分页本身的问题。当我尝试传递大小和页面时,分页不起作用。

这是我正在使用的代码。

我的控制器

    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);
}

这是我的用户资料库

public interface UserRepo extends JpaRepository<User, Integer>{

public List<User> findAllByProgramId(Integer programId);

这是我的服务

    public List<User> findAllByProgramId(Integer programId);

2
似乎你使用的方式不正确。你检索了表的所有元素,然后将它们包装成 PageImpl。但是你应该在服务内部将 PageRequest 传递给存储库以使其正常工作。你能否也发布一下你的服务方法和存储库的代码? - Vladimir Vagaytsev
是的,我会发布它们,但是关于Impl方法的内容相当长。不过我会发布存储库。 - user3127109
17个回答

162

我有同样的问题。我使用了 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());

5
@OliverGierke 我同意,当您可以直接从存储库中检索“Page”时,但并非总是如此。例如,如果您的实体具有一个OneToMany关联,该关联会获取一个“List”,并且您想将其公开为“Page”。 - gotson
5
尽管没有尝试过,但即使它有效,也不是很高效。它会从数据库检索所有用户并返回一个子列表。理想情况下,只需从请求中提供的分页用户中检索数据即可。 - drumaddict
8
org.springframework.data.domain.Pageable.getOffset() 返回一个long类型的值,因此我认为你需要将pageable.getOffset()强制转换为long类型或更改start的数据类型。 - chomp
1
可以用 Math.min 替换 end,例如 int end = Math.min((start + pageable.getPageSize()), users.size()); - Gaspar
1
更新的解决方案来处理强制转换和min()函数的使用。 - Michael Peterson
显示剩余5条评论

36

对于这个问题,有一个相关的Page实现

Page<Something> page = new PageImpl<>(yourList);

4
注意,此解决方案完全没有使用Pageable对象。 - chomp
@chomp,您能否进一步解释为什么? - Tharindu Eranga
@TharinduEranga 因为,例如,如果您收到一个Pageable对象,其中pageSize = 20或pageNumber = 10,则它将忽略该设置并返回所有结果。 - chomp
如果您喜欢自己构建PageImpl,我认为@shilaimuslm的答案更准确。 - chomp
我只需要为测试模拟一个页面对象。所以这正是要点。 - Wit
这已经足够用于我只想返回一个空结果的情况了。 - rineez

23

正如参考文档所示,Spring Data仓库通过简单地声明类型为Pageable的参数支持查询方法上的分页,以确保他们仅读取请求的Page所需的数据。

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

这会返回一个Page对象,其中包含在您的Pageable对象中定义的页面大小/设置。无需获取列表,然后尝试从中创建页面。


14
解决了问题,但并未回答问题。假设我们有一个需要转换的数据库对象(例如一个User),但我们希望将一些子集数据包含在UserDetailsDTO页面中发送到前端。在这种情况下,手动创建页面可能是必要的。 - Matt
OP没有提到任何与DTO或数据转换相关的内容,所以我不明白它为什么不能回答问题。 - dubonzi

19

你应该按照dubonzi的回答所建议的方式去做。

如果你仍然想要对给定的List进行分页,使用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

如果您需要排序,请使用另一个PagedListHolder构造函数,并使用MutableSortDefinition


8
你能解释一下在这种情况下如何使用 PagedListHolderMutableSortDefinition 吗? - M. Dudek

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());
    }

代码很棒,但我想最好按照以下方式检查开始和结束:如果(start <= end),则返回new PageImpl<Patient>(patientsList.subList(start, end), new PageRequest(page, size), patientsList.size()); 因为如果开始大于结束,它就不会真正起作用。 - Meysam Fathee Panah

7
这可能是解决方案。采用这种方式,排序和分页也可以正常工作:
控制器:
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);
}

服务:

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

代码库:

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

这样,我们还可以返回实体的不同页面。

6
在JHipster框架中有一个用于此类事情的接口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());
}

3
您可以使用此通用函数将列表转换为页面。
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());
}

3

根据@shilaimuslm的评论实现。在这种情况下,如果subList中的start > end,则不会抛出异常。

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());

非常感谢,这是一个非常好的例子! - Sherzod

1

你没有制作分页结果

new PageImpl<User>(users, pageable, users.size());并不会隐式地制作分页结果,

在这个情境下,pageable参数只是制作Page对象的元数据,例如页面、偏移量、大小等等。

所以你必须使用Repository方法,例如

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接