在本教程中,我将指导您使用 Spring Data JPA 为现有 Spring Boot 应用程序编写分页和排序功能。如您所知,分页允许用户一次查看一小部分数据(一页),排序允许用户以更有条理的方式查看数据。分页和排序都可以帮助用户更轻松、更方便地消费信息。
我将从可以从本教程下载的 ProductManager 项目开始,该项目基于 Spring Data JPA、Hibernate、Thymeleaf 和 MySQL 数据库。
要使用 Spring Data JPA 提供的分页和排序 API,您的存储库接口必须扩展PagingAndSortingRepository 接口,该接口定义了以下几个方法(T指的是实体类):
1
2
|
Iterable
Page
|
请注意,JpaRepository是PagingAndSortingRepository 的子类型,因此如果您的存储库接口是JpaRepository类型,则不必对其进行更改。
以下代码示例从数据库中获取第一页,每页包含 10 个项目:
1
2
3
4
5
|
int pageNumber = 1 ;
int pageSize = 10 ;
Pageable pageable = PageRequest.of(pageNumber, pageSize);
Page
|
然后就可以得到实际内容如下:
1
|
List
|
使用Page对象,您可以根据给定的页面大小了解数据库中的总行数和总页数:
1
2
|
long totalItems = page.getTotalElements();
int totalPages = page.getTotalPages();
|
此信息对于使用 Thymeleaf 模板在视图中实现分页很有用。
现在,让我们更新 ProductManger 项目,为产品列表添加分页。
在分页之前,列出所有产品的方法在ProductService类中实现如下:
1
2
3
4
5
6
7
8
9
|
@Service
public class ProductService {
@Autowired
private ProductRepository repo;
public List
return repo.findAll();
}
}
|
现在,为了实现分页功能,更新这个方法如下:
1
2
3
4
5
6
7
|
public Page int pageNum) {
int pageSize = 5 ;
Pageable pageable = PageRequest.of(pageNum - 1 , pageSize);
return repo.findAll(pageable);
}
|
如您所见,我们更新listAll()方法以获取将从控制器传递的页码参数。此方法返回Page
请注意,分页 API 认为页码是从 0 开始的。在视图中,我们为用户使用从 1 开始的页码,但在代码中我们需要转换为从 0 开始的页码,因此您可以看到pageNum – 1如上。
在 Spring MVC 控制器类中添加一个新方法来处理查看特定产品页面的请求,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@RequestMapping ( "/page/{pageNum}" )
public String viewPage(Model model,
@PathVariable (name = "pageNum" ) int pageNum) {
Page
List
model.addAttribute( "currentPage" , pageNum);
model.addAttribute( "totalPages" , page.getTotalPages());
model.addAttribute( "totalItems" , page.getTotalElements());
model.addAttribute( "listProducts" , listProducts);
return "index" ;
}
|
如您所见,页码是这样添加到 URL 中的:/page/1、page/2、/page/3 ...
除了Product对象的List之外,我们还在模型中存储了 3 个附加属性以用于分页:currentPage、totalPages和totalItems。
并修改首页的handler方法显示首页如下:
1
2
3
4
|
@RequestMapping ( "/" )
public String viewHomePage(Model model) {
return viewPage(model, 1 );
}
|
我们将更新视图以显示分页导航链接,例如第一页、上一页、从 1 到总页数的页码、下一页和最后一页。
现在,对于视图模板(index.html),我们使用 Thymeleaf 表达式显示总行数,如下所示:
1
|
Total Items: [[${totalItems}]]
|
要显示允许用户导航到第一页的超链接:
1
2
|
|
请注意,如果当前页面为 1,则它显示的是文本,而不是链接。
显示链接以导航到上一页的代码:
1
2
|
< a th:if="${currentPage > 1}" th:href="/@{'/page/' + ${currentPage - 1}}">Previous a >
< span th:unless="${currentPage > 1}">Previous span >
|
要显示允许用户导航到特定页面的链接,范围从第 1 页到总页数,请编写如下代码:
1
2
3
4
5
|
< span th:each = "i: ${#numbers.sequence(1, totalPages)}" >
< a th:if = "${currentPage != i}" th:href = "/@{'/page/' + ${i}}" >[[${i}]] a >
< span th:unless = "${currentPage != i}" >[[${i}]] span >
span >
|
显示下一页超链接的代码:
1
2
|
< a th:if = "${currentPage < totalPages}" th:href = "/@{'/page/' + ${currentPage + 1}}" >Next a >
< span th:unless = "${currentPage < totalPages}" >Next span >
|
以及显示最后一页超链接的代码:
1
2
|
< a th:if = "${currentPage < totalPages}" th:href = "/@{'/page/' + ${totalPages}}" >Last a >
< span th:unless = "${currentPage < totalPages}" >Last span >
|
测试分页
假设我们在 products 表中有 15 行,那么我们会看到主页如下:
请参阅页面底部的分页导航链接。您可以单击链接 First、Previous、特定页码、Next 和 Last 来测试分页。
接下来,我将指导您结合分页实现排序功能。用户将能够通过单击表格的列标题对产品列表进行排序。
首先,像这样创建一个Sort 对象:
1
|
Sort sort = Sort.by(“fieldName”).ascending();
|
这将按fieldName升序对结果进行排序。fieldName必须与实体类中声明的字段名称匹配。我们还可以按多个字段排序,例如:
1
2
|
Sort sort = Sort.by( "brand" ).ascending().
and(Sort.by( "price" ).descending());
|
这将首先按品牌列升序排列产品列表,然后按降序排列价格。
然后我们通过Sort对象来创建一个Pageable,如下所示:
1
2
|
Pageable pageable = PageRequest.of(pageNum - 1 , pageSize, sort);
Page
|
如您所见,我们可以在分页的同时巧妙地应用排序。
现在,除了我们之前完成的分页之外,让我们更新 ProjectManager 项目以实现排序。为简单起见,我们允许用户仅按单个字段对产品列表进行排序。
修改ProductService类中的listAll()方法,将sortField和sortDir作为附加参数,如下:
1
2
3
4
5
6
7
8
9
|
public Page int pageNum, String sortField, String sortDir) {
int pageSize = 5 ;
Pageable pageable = PageRequest.of(pageNum - 1 , pageSize,
sortDir.equals( "asc" ) ? Sort.by(sortField).ascending()
: Sort.by(sortField).descending()
);
return repo.findAll(pageable);
}
|
这允许我们参数化排序字段和排序方向。
接下来,更新控制器类中的viewPage()处理程序方法,以从 URL 中的查询参数中读取排序字段和排序方向,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@RequestMapping ( "/page/{pageNum}" )
public String viewPage(Model model,
@PathVariable (name = "pageNum" ) int pageNum,
@Param ( "sortField" ) String sortField,
@Param ( "sortDir" ) String sortDir) {
Page
List
model.addAttribute( "currentPage" , pageNum);
model.addAttribute( "totalPages" , page.getTotalPages());
model.addAttribute( "totalItems" , page.getTotalElements());
model.addAttribute( "sortField" , sortField);
model.addAttribute( "sortDir" , sortDir);
model.addAttribute( "reverseSortDir" , sortDir.equals( "asc" ) ? "desc" : "asc" );
model.addAttribute( "listProducts" , listProducts);
return "index" ;
}
|
因此分页和排序的 URL 如下所示:
/page/1?sortField=name&sortDir=asc
我们还在模型中存储了 3 个附加属性以在视图中用于排序:sortField、sortDir和reverseSortDir。reverseSortDir属性用于在用户单击列标题时切换排序顺序。
修改首页的handler方法,默认显示结果的第一页,按名称升序排列:
1
2
3
4
|
@RequestMapping ( "/" )
public String viewHomePage(Model model) {
return viewPage(model, 1 , "name" , "asc" );
}
|
我们通过使用以下代码添加超链接来使表格的标题列可排序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
< th >
< a th:href = "/@{'/page/' + ${currentPage} + '?sortField=id&sortDir=' + ${reverseSortDir}}" >Product ID a >
th >
< th >
< a th:href = "/@{'/page/' + ${currentPage} + '?sortField=name&sortDir=' + ${reverseSortDir}}" >Name a >
th >
< th >
< a th:href = "/@{'/page/' + ${currentPage} + '?sortField=brand&sortDir=' + ${reverseSortDir}}" >Brand a >
th >
< th >
< a th:href = "/@{'/page/' + ${currentPage} + '?sortField=madein&sortDir=' + ${reverseSortDir}}" >Made In a >
th >
< th >
< a th:href = "/@{'/page/' + ${currentPage} + '?sortField=price&sortDir=' + ${reverseSortDir}}" >Price a >
th >
|
请注意,超链接中的排序顺序与当前排序方向相反,以允许用户切换方向(升序/降序)。
添加以下代码以显示当前字段按哪个方向排序:
1
|
< div >< i >Sorted by [[${sortField}]] in [[${sortDir}]] order i > div >
|
对于分页导航链接,将以下值附加到每个 URL:
1
|
'?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}
|
这允许用户在仍然应用排序时导航到结果的不同页面。
启动应用程序并刷新主页。您会看到表格的列标题现在可以单击,如以下屏幕截图所示:
单击每一列以测试排序,然后单击底部的导航链接以测试分页和排序。
到目前为止,您已经学会了使用 Spring Data JPA、Hibernate、Thymeleaf 和 MySQL 数据库为现有 Spring Boot 应用程序实现分页和排序功能。你看 Spring 让它变得简单、简单和方便。
作为参考,您可以下载下面随附的示例项目。
ProductManagerPagingAndSorting.zip | 【示例Spring Boot分页排序项目】 | 75 KB |