Spring Data JPA 分页和排序示例

在本教程中,我将指导您使用 Spring Data JPA 为现有 Spring Boot 应用程序编写分页和排序功能。如您所知,分页允许用户一次查看一小部分数据(一页),排序允许用户以更有条理的方式查看数据。分页和排序都可以帮助用户更轻松、更方便地消费信息。

我将从可以从本教程下载的 ProductManager 项目开始,该项目基于 Spring Data JPA、Hibernate、Thymeleaf 和 MySQL 数据库。

 

1. 了解 Spring Data JPA 的 Pagination API

要使用 Spring Data JPA 提供的分页和排序 API,您的存储库接口必须扩展PagingAndSortingRepository 接口,该接口定义了以下几个方法(T指的是实体类):

1
2
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);

请注意,JpaRepositoryPagingAndSortingRepository 的子类型,因此如果您的存储库接口是JpaRepository类型,则不必对其进行更改。

以下代码示例从数据库中获取第一页,每页包含 10 个项目:

1
2
3
4
5
int pageNumber = 1;
int pageSize = 10;
Pageable pageable = PageRequest.of(pageNumber, pageSize);
 
Page<Product> page = repository.findAll(pageable);

然后就可以得到实际内容如下:

1
List<Product> listProducts = page.getContent();

使用Page对象,您可以根据给定的页面大小了解数据库中的总行数和总页数:

1
2
long totalItems = page.getTotalElements();
int totalPages = page.getTotalPages();

此信息对于使用 Thymeleaf 模板在视图中实现分页很有用。


 

 

2.实现分页

现在,让我们更新 ProductManger 项目,为产品列表添加分页。

更新分页服务类

在分页之前,列出所有产品的方法在ProductService类中实现如下:

1
2
3
4
5
6
7
8
9
@Service
public class ProductService {
    @Autowired
    private ProductRepository repo;
     
    public List<Product> listAll() {
        return repo.findAll();
    }  
}

现在,为了实现分页功能,更新这个方法如下:

1
2
3
4
5
6
7
public Page<Product> listAll(int pageNum) {
    int pageSize = 5;
     
    Pageable pageable = PageRequest.of(pageNum - 1, pageSize);
     
    return repo.findAll(pageable);
}

如您所见,我们更新listAll()方法以获取将从控制器传递的页码参数。此方法返回Page<Product>而不是List<Product>。

请注意,分页 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<Product> page = service.listAll(pageNum);
     
    List<Product> listProducts = page.getContent();
     
    model.addAttribute("currentPage", pageNum);
    model.addAttribute("totalPages", page.getTotalPages());
    model.addAttribute("totalItems", page.getTotalElements());
    model.addAttribute("listProducts", listProducts);
     
    return "index";
}

如您所见,页码是这样添加到 URL 中的:/page/1page/2/page/3 ...

除了Product对象的List之外,我们还在模型中存储了 3 个附加属性以用于分页:currentPagetotalPagestotalItems

并修改首页的handler方法显示首页如下:

1
2
3
4
@RequestMapping("/")
public String viewHomePage(Model model) {
    return viewPage(model, 1);
}

 

更新 Thymeleaf 模板视图以进行分页

我们将更新视图以显示分页导航链接,例如第一页、上一页、从 1 到总页数的页码、下一页和最后一页。

现在,对于视图模板(index.html),我们使用 Thymeleaf 表达式显示总行数,如下所示:

1
Total Items: [[${totalItems}]]

要显示允许用户导航到第一页的超链接:

1
2
<a th:if="${currentPage > 1}" th:href="/@{'/page/1'}">First</a>
<span th:unless="${currentPage > 1}">First</span>

请注意,如果当前页面为 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>
    &nbsp;
</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 来测试分页。

 

3. 了解 Spring Data JPA 的排序 API

接下来,我将指导您结合分页实现排序功能。用户将能够通过单击表格的列标题对产品列表进行排序。

首先,像这样创建一个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<Product> page = repo.findAll(pageable);

如您所见,我们可以在分页的同时巧妙地应用排序。

 

4.实现排序

现在,除了我们之前完成的分页之外,让我们更新 ProjectManager 项目以实现排序。为简单起见,我们允许用户仅按单个字段对产品列表进行排序。

更新用于排序的服务类

修改ProductService类中的listAll()方法,将sortFieldsortDir作为附加参数,如下:

1
2
3
4
5
6
7
8
9
public Page<Product> listAll(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<Product> page = service.listAll(pageNum, sortField, sortDir);
     
    List<Product> listProducts = page.getContent();
     
    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 个附加属性以在视图中用于排序:sortFieldsortDirreverseSortDir。reverseSortDir属性用于在用户单击列标题时切换排序顺序

修改首页的handler方法,默认显示结果的第一页,按名称升序排列:

1
2
3
4
@RequestMapping("/")
public String viewHomePage(Model model) {
    return viewPage(model, 1"name""asc");
}

 

更新 Thymeleaf 模板视图以进行排序

我们通过使用以下代码添加超链接来使表格的标题列可排序:

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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值