让我们学习如何使用 在 Spring 数据 JPA 中使用分页和排序有效地处理大量记录。
什么是分页和排序?
大多数情况下,来自数据库的结果集可能会让人不知所措。它可能会让人不知所措,以至于系统崩溃,因为它们无法在一次传递中处理那么多数据。因此,将这些大记录分解为可管理的块总是一个好主意。这正是分页的意义所在。
分页是将大内容拆分为称为“页面”的较小编号块以使整个数据更易于处理的过程。排序不需要任何介绍或定义。这是一种订购一组类似项目的方法。在我们的例子中,我们将坚持从数据库中对记录进行排序。
您可能已经看到了分页和排序。以谷歌为例。查询的结果为每页 10 个链接。结果根据搜索查询的相关性进行排序(顶部的相关性更高)。
谷歌搜索作为分页和排序的例子
为什么使用分页和排序?
想象一下,如果谷歌没有在结果页上使用分页或排序。平均谷歌搜索会产生数百万次点击。因此,明显的问题是页面需要很长时间才能加载。由于我们不对结果进行排序,因此用户不会找到相关结果。即使他们想通过滚动浏览来查找相关结果,他们也会滚动很多,因为所有结果都在一个页面中。
根据我们的学习,我们应该使用分页和排序技术,原因如下。
- 服务器和客户端都更容易处理小结果。
- 较大的结果需要更多的资源(CPU、内存、网络等)
- 排序结果为系统带来秩序
- 排序可以确定项目的优先级
Spring Data JPA 中的分页和排序
Spring Data JPA通过PagingAndSortingRepository提供分页和排序。此存储库接受可分页请求,并在从零开始的索引系统中提供页面。下面是一些重要的类和接口,供您在实现分页和排序之前记住。
- 可分页 – 定义特定页面请求的接口。
- PageRequest –Pageable 的实现。包含要请求的页面、每页的记录数、排序顺序等。
- 页面 – 包含大量结果和有关结果的元信息的页面,例如上一页、下一页、大小、总大小等
要编写可分页查询,您只需将可分页接口作为参数添加到任何 JpaRepository 方法中。
@Repository public interface AccountRepository extends JpaRepository<Account, Integer> { @Query("select a from Account a") Page<Account> findAllAccounts(Pageable pageable); }
Code language: Java (java)
若要使用此存储库方法获取结果,需要将可分页对象作为参数传递。以下是如何做到这一点的一些示例。
可分页,包含页码和大小。
Pageable pageable = PageRequest.of(2, 40); Page<Account> thirdPage = accountRepository.findAllAccounts(pageable); System.out.println(thirdPage.getContent());
Code language: Java (java)
基于 JPA 实体字段进行排序的页面请求。
Pageable pageableWithSort = PageRequest.of(0, 15, Sort.by("balance")); Page<Account> first15AccountsWithLowBalance = accountRepository.findAllAccounts(pageableWithSort); System.out.println(first15AccountsWithLowBalance.getContent());
Code language: Java (java)
对 Spring 数据 JPA 实体字段进行排序和排序方向的分页。
Pageable pageableWithSortDirection = PageRequest.of(0, 15, Sort.by("balance").descending()); Page<Account> largest15AccountsByBalance = accountRepository.findAllAccounts(pageableWithSortDirection); System.out.println(largest15AccountsByBalance);
Code language: Java (java)
此外,您还可以访问当前页面的元数据。
System.out.println("Total results : " + thirdPage.getTotalElements()); System.out.println("Total Pages : " + thirdPage.getTotalPages()); System.out.println("Current Page number : " + thirdPage.getNumber()); System.out.println("Size per page : " + thirdPage.getSize()); System.out.println("Elements in this page : " + thirdPage.getNumberOfElements());
Code language: Java (java)
在春季 JPA 中循环遍历分页
到目前为止,我们已经看到了如何从大量结果中获取特定页面。但是,如果要遍历所有这些结果,则需要多次查询存储库。但幸运的是,春季数据 jpa 有遍历页面的规定。
Pageable pageRequest = PageRequest.of(0, 20); Page<Account> accountsPage; do { accountsPage = accountRepository.findAllAccounts(pageRequest); System.out.println(accountsPage.getContent()); pageRequest = pageRequest.next(); } while (!accountsPage.isLast());
Code language: Java (java)
这里的代码是不言自明的。但是,如果您需要详细信息,只需注意方法即可。这些方法分别有助于进步和打破循环。这样,如果您有大量记录,则可以一次处理 20 条记录。
上一页和下一页
只需在当前页码中添加或减去“1”,即可创建新的可分页对象。但是,借助以下方法,可以更轻松地浏览上一页和下一页。
Pageable previousPageable = page.previousPageable(); Pageable previousOrFirstPageable = page.previousOrFirstPageable(); Pageable nextPageable = page.nextPageable(); Pageable nextOrLastPageable = page.nextOrLastPageable(); // or Pageable previousOrFirst = pageable.previousOrFirst(); Pageable next = pageable.next();
Code language: Java (java)
如您所见,有些方法甚至可以处理这里的边缘情况。
在分页查询方法中一次获取所有结果
如果要一次性获取所有记录,可以将页面大小设置为更大的数字。但不能保证结果小于您给定的大小。这就是为什么Spring Data JPA通过“Pageable.unpaged()”方法提供非分页实现的原因。
该方法返回未分页实现的单一实例对象。此类故意重写 Pageable 的 isPaged() 方法以返回 false,完全绕过分页逻辑。
因此,您所要做的就是通过以下方式调用存储库方法。
Pageable unpaged = Pageable.unpaged(); Page<Account> allAccounts = accountRepository.findAllAccounts(unpaged); System.out.println("All Results in a Single page" + allAccounts.getContent());
Code language: Java (java)
尽管这违背了分页的全部目的,但您应该知道这种方法是可能的。
总结
总而言之,我们以 Spring boot 应用程序为例,学习了如何在 Spring JPA 中正确使用分页和排序。您可以在此GitHub 存储库中找到上述所有示例。
相关
- 春季数据JPA简介
- 带有 Spring 引导的 RESTful JPA 存储库
- Spring 引导|中的白标错误页面完整指南
- 春季数据 JPA @ManyToOne注释
- 在 Spring 引导中更改默认端口号