• Spring Boot 中的Thymeleaf分页和排序示例


    在上一篇文章中,我们已经知道如何构建Spring Boot Thymeleaf示例。今天,我将继续使用 Spring Data 和 Bootstrap 进行 Thymeleaf 分页和排序(按列标题)。

    百里香叶分页和排序示例

    假设我们在数据库中有这样的教程表:

     

    我们的 Spring 启动应用程序将按升序或降序处理分页和排序请求。以下是一些网址示例(带/不带过滤器):

    • /api/tutorials
      分页 [页面=0,大小=6](默认)和按 [id,升序] 排序(默认)
    • /api/tutorials?sort=title,asc
      分页 [页面=0,大小=6](默认)并按 [标题,升序] 排序
    • /api/tutorials?page=2&size=6&sort=title,desc
      分页 [页=2,大小=6] 并按 [标题,降序] 排序
    • /api/tutorials?keyword=data&page=1&size=6&sort=title,asc
      分页和按包含“数据”的标题过滤,按[标题,升序]排序

    检索具有默认页面 (1)、大小 (6) 并按 [id, 升序] 排序的教程:

     

    更改页面和大小(每页项目数)并按 [id,升序] 排序(默认):

     

    单击表标题(级别)以创建分页和按级别排序列:

     

    继续单击该表标题(级别)以降序排序:

     

    带过滤的分页和排序:

     

     

    Spring 数据分页和排序存储库

    为了帮助我们处理这种情况,Spring Data JPA 提供了使用PagingAndSortingRepository 实现分页和排序的方法。

    PagingAndSortingRepository扩展了 CrudRepository,以提供使用排序抽象检索实体的其他方法。因此,您可以向查询方法添加特殊的 Sort 参数。

    1. public interface PagingAndSortingRepository extends CrudRepository {
    2. Iterable findAll(Sort sort);
    3. }

    findAll(Sort sort):返回满足对象提供的排序条件的 AOF 实体。IterableSort

    您还可以使用 additionalparameter 定义更多派生和自定义查询方法。例如,以下方法返回标题包含给定字符串的教程列表:Sort

    List findByTitleContaining(String title, Sort sort);

    您可以在此处的方法名称中找到更多支持的关键字。

    让我们继续探索类。Sort

    弹簧数据排序和排序

    Sort类为数据库查询提供了排序选项,在选择单个/多个排序列和方向(升序/降序)时具有更大的灵活性。

    例如,我们使用,,方法来创建对象并将其传递给:by()descending()and()SortRepository.findAll()

    1. // order by 'level' column - ascending
    2. List tutorials =
    3. tutorialRepository.findAll(Sort.by("level"));
    4. // order by 'level' column, descending
    5. List tutorials =
    6. tutorialRepository.findAll(Sort.by("level").descending());
    7. // order by 'level' column - descending, then order by 'title' - ascending
    8. List tutorials =
    9. tutorialRepository.findAll(Sort.by("level").descending().and(Sort.by("title")));

    我们还可以使用对象列表创建一个新对象。SortOrder

    1. List orders = new ArrayList();
    2. Order order1 = new Order(Sort.Direction.DESC, "level");
    3. orders.add(order1);
    4. Order order2 = new Order(Sort.Direction.ASC, "title");
    5. orders.add(order2);
    6. List tutorials = tutorialRepository.findAll(Sort.by(orders));

    将分页和排序结合在一起

    如果我们想对数据进行排序和分页怎么办?

    CrudRepository还提供了使用分页/排序抽象检索实体的其他方法。

    1. public interface PagingAndSortingRepository extends CrudRepository {
    2. Page findAll(Pageable pageable);
    3. }

    findAll(Pageable pageable):返回满足对象提供的分页条件的 AOF 实体。PagePageable

    Spring Data 还支持从方法名称创建许多有用的查询,我们将使用这些方法名称来过滤此示例中的结果,例如:

    1. Page findByPublished(boolean published, Pageable pageable);
    2. Page findByTitleContaining(String title, Pageable pageable);

    您可以在此处的方法名称中找到更多支持的关键字。
    例如:JPA 存储库查询示例

    要使用分页对多个字段进行排序,请访问教程:
    Spring Data JPA 按多列排序/排序|弹簧启动

    让我们注意上面存储库方法中的可分页参数。Spring 数据基础设施将自动识别此参数,以将分页和排序应用于数据库。

    该接口包含有关所请求页面的信息,例如大小、页面编号或对信息进行排序。PageableSort

    1. public interface Pageable {
    2. int getPageNumber();
    3. int getPageSize();
    4. long getOffset();
    5. Sort getSort();
    6. Pageable next();
    7. Pageable previousOrFirst();
    8. Pageable first();
    9. boolean hasPrevious();
    10. ...
    11. }

    因此,当我们想在结果中进行分页和排序(带或不带过滤器)时,我们只需将方法的定义添加为参数。Pageable

    1. Page findAll(Pageable pageable);
    2. Page findByPublished(boolean published, Pageable pageable);
    3. Page findByTitleContaining(String title, Pageable pageable);

    这就是我们使用实现接口的PageRequest类创建对象的方式:PageablePageable

    Pageable paging = PageRequest.of(page, size, sort);
    • page:从零开始的页面索引,不得为负数。
    • size:页面中要返回的项目数必须大于 0。
    • sort:对象。Sort

    您可以在这篇文章中找到有关分页和过滤器的更多详细信息:
    Spring Boot 分页和过滤器示例

    创建百里香叶分页和排序项目

    您可以按照步骤操作,或获取本文中的源代码:
    Spring Boot Thymeleaf CRUD 示例

    Spring 项目包含的结构,我们只需要添加一些更改即可使分页和排序正常工作。

     

    或者,您可以在本教程结束时获取新的 Github 源代码(包括分页和排序)。

    数据模型

    这是我们将要处理的教程实体:

    模型/教程.java

    1. package com.bezkoder.spring.thymeleaf.pagingsorting.entity;
    2. import javax.persistence.*;
    3. @Entity
    4. @Table(name = "tutorials")
    5. public class Tutorial {
    6. @Id
    7. @GeneratedValue(strategy = GenerationType.AUTO)
    8. private Integer id;
    9. @Column(length = 128, nullable = false)
    10. private String title;
    11. @Column(length = 256)
    12. private String description;
    13. @Column(nullable = false)
    14. private int level;
    15. @Column
    16. private boolean published;
    17. public Tutorial() {
    18. }
    19. public Tutorial(String title, String description, int level, boolean published) {
    20. this.title = title;
    21. this.description = description;
    22. this.level = level;
    23. this.published = published;
    24. }
    25. // getters and setters
    26. }

    分页和排序存储库

    在本教程的早期,我们知道,但是在这个例子中,为了保持连续性和利用Spring Data JPA,我们继续使用扩展接口的JpaRepositoryPagingAndSortingRepositoryPagingAndSortingRepository

    存储库/教程存储库.java

    1. package com.bezkoder.spring.thymeleaf.pagingsorting.repository;
    2. import javax.transaction.Transactional;
    3. import org.springframework.data.domain.Page;
    4. import org.springframework.data.domain.Pageable;
    5. import org.springframework.data.jpa.repository.JpaRepository;
    6. import org.springframework.data.jpa.repository.Modifying;
    7. import org.springframework.data.jpa.repository.Query;
    8. import org.springframework.stereotype.Repository;
    9. import com.bezkoder.spring.thymeleaf.pagingsorting.entity.Tutorial;
    10. @Repository
    11. @Transactional
    12. public interface TutorialRepository extends JpaRepository {
    13. Page findByTitleContainingIgnoreCase(String keyword, Pageable pageable);
    14. // ...
    15. }

    在上面的代码中,我们使用 addparameter 和 Spring查询创建来查找标题包含输入字符串的所有教程。pageable

    更多派生查询:
    Spring 引导中的 JPA 存储库查询示例

    带有注释的自定义查询:
    Spring JPA @Query示例:Spring 引导中的自定义查询@Query

    带分页和排序功能的控制器

    通常,在HTTP请求URL中,分页和排序参数是可选的。因此,我们应该提供默认值以使分页和排序工作,即使UI View没有指定这些参数。

    为了获取排序请求参数,我们使用。我们还需要转换/转换为/用于与类一起工作。@RequestParam String[] sortdefaultValue="id,asc""asc""desc"Sort.Direction.ASCSort.Direction.DESSort.Order

    控制器/教程控制器.java

    1. package com.bezkoder.spring.thymeleaf.pagingsorting.controller;
    2. import java.util.ArrayList;
    3. import java.util.List;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.data.domain.Page;
    6. import org.springframework.data.domain.PageRequest;
    7. import org.springframework.data.domain.Pageable;
    8. import org.springframework.data.domain.Sort;
    9. import org.springframework.data.domain.Sort.Direction;
    10. import org.springframework.data.domain.Sort.Order;
    11. import org.springframework.stereotype.Controller;
    12. import org.springframework.ui.Model;
    13. import org.springframework.web.bind.annotation.GetMapping;
    14. import org.springframework.web.bind.annotation.PathVariable;
    15. import org.springframework.web.bind.annotation.PostMapping;
    16. import org.springframework.web.bind.annotation.RequestParam;
    17. import org.springframework.web.servlet.mvc.support.RedirectAttributes;
    18. import com.bezkoder.spring.thymeleaf.pagingsorting.entity.Tutorial;
    19. import com.bezkoder.spring.thymeleaf.pagingsorting.repository.TutorialRepository;
    20. @Controller
    21. public class TutorialController {
    22. @Autowired
    23. private TutorialRepository tutorialRepository;
    24. @GetMapping("/tutorials")
    25. public String getAll(Model model, @RequestParam(required = false) String keyword,
    26. @RequestParam(defaultValue = "1") int page,
    27. @RequestParam(defaultValue = "6") int size,
    28. @RequestParam(defaultValue = "id,asc") String[] sort) {
    29. try {
    30. List tutorials = new ArrayList();
    31. String sortField = sort[0];
    32. String sortDirection = sort[1];
    33. Direction direction = sortDirection.equals("desc") ? Sort.Direction.DESC : Sort.Direction.ASC;
    34. Order order = new Order(direction, sortField);
    35. Pageable pageable = PageRequest.of(page - 1, size, Sort.by(order));
    36. Page pageTuts;
    37. if (keyword == null) {
    38. pageTuts = tutorialRepository.findAll(pageable);
    39. } else {
    40. pageTuts = tutorialRepository.findByTitleContainingIgnoreCase(keyword, pageable);
    41. model.addAttribute("keyword", keyword);
    42. }
    43. tutorials = pageTuts.getContent();
    44. model.addAttribute("tutorials", tutorials);
    45. model.addAttribute("currentPage", pageTuts.getNumber() + 1);
    46. model.addAttribute("totalItems", pageTuts.getTotalElements());
    47. model.addAttribute("totalPages", pageTuts.getTotalPages());
    48. model.addAttribute("pageSize", size);
    49. model.addAttribute("sortField", sortField);
    50. model.addAttribute("sortDirection", sortDirection);
    51. model.addAttribute("reverseSortDirection", sortDirection.equals("asc") ? "desc" : "asc");
    52. } catch (Exception e) {
    53. model.addAttribute("message", e.getMessage());
    54. }
    55. return "tutorials";
    56. }
    57. // other CRUD methods
    58. }

    在上面的代码中,我们接受使用注释的分页参数,,.默认情况下,教程将从页面索引中的数据库获取(UI 视图上的 page=1),排序依据(降序)。@RequestParampagesizesort60id

    接下来,我们创建一个对象,,.
    然后检查参数是否存在。Pageablepagesizesorttitle

    • 如果它为 null,我们调用 Repositorywithis 上面的对象。findAll(pageable)pageablePageable
    • 如果客户端发送请求,请使用。titlefindByTitleContainingIgnoreCase(title, pageable)

    这两种方法都返回一个对象。我们称之为:Page

    • getContent()以检索页面中的项目列表。
    • getNumber()对于当前页面。
    • getTotalElements()用于数据库中存储的总项目。
    • getTotalPages()总页数。

    我们还需要返回一些模型属性,例如:,,,.pageSizesortFieldsortDirectionreverseSortDirection

    导入引导程序

    打开pom.xml并添加以下依赖项:

    1. <dependency>
    2. <groupId>org.webjarsgroupId>
    3. <artifactId>bootstrapartifactId>
    4. <version>4.6.2version>
    5. dependency>
    6. <dependency>
    7. <groupId>org.webjarsgroupId>
    8. <artifactId>jqueryartifactId>
    9. <version>3.6.1version>
    10. dependency>
    11. <dependency>
    12. <groupId>org.webjarsgroupId>
    13. <artifactId>webjars-locator-coreartifactId>
    14. dependency>

    然后打开包含分页栏的HTML文件,导入Thymeleaf片段,Bootstrap,jQuery和Font Awesome:

    教程.html

    1. html>
    2. <html xmlns:th="http://www.thymeleaf.org">
    3. <head>
    4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    5. <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0" />
    6. <title>BezKoder - Spring Boot Thymeleaf Pagination and Sorting exampletitle>
    7. <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" />
    8. <link rel="stylesheet" type="text/css" th:href="@{/css/style.css}" />
    9. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
    10. integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
    11. crossorigin="anonymous" referrerpolicy="no-referrer" />
    12. <script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}">script>
    13. <script type="text/javascript" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}">script>
    14. head>
    15. <body>
    16. <div th:replace="fragments/header :: header">div>
    17. -- list, pagination and sorting--
    18. <div th:replace="fragments/footer :: footer">div>
    19. body>
    20. html>

    百里香叶分页和排序模板

    我们将使用百里香叶片段()来重用页面链接和表格的公共部分,按列标题排序。
    让我们为它们编写 HTML 代码。th:fragment

    片段/分页.html

    1. <a th:fragment="paging(pageNum, label, tooltip)" class="page-link"
    2. th:href="@{'/tutorials?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''}
    3. + 'page=' + ${pageNum} + '&size=' + ${pageSize}
    4. + ${sortField!=null ? '&sort=' + sortField + ',' + sortDirection : ''}}"
    5. th:title="${tooltip}" rel="tooltip">
    6. [[${label}]]
    7. a>

    片段/排序.html

    1. <th scope="col" th:fragment="sorting(field, label)">
    2. <a th:href="@{'/tutorials?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''}
    3. + 'page=' + ${currentPage} + '&size=' + ${pageSize}
    4. + ${sortField!=null ? '&sort=' + field + ',' + (sortField == field ? reverseSortDirection : sortDirection) : ''}}">
    5. [[${label}]] a>
    6. <span th:if="${sortField == field}"
    7. th:class="${sortDirection == 'asc' ? 'fas fa-arrow-down-short-wide' : 'fas fa-arrow-down-wide-short'}">span>
    8. th>

    现在我们修改 Thymeleaf 模板教程.html,它显示基于模型属性的分页和排序的教程列表:,,,,从类返回。tutorialspageSizesortFieldsortDirectionreverseSortDirectionTutorialController

    教程.html

    1. <div th:if="${tutorials.size() > 0}">
    2. <table class="table table-hover table-responsive-xl">
    3. <thead class="thead-light">
    4. <tr>
    5. <th th:replace="fragments/sorting :: sorting('id','Id')">th>
    6. <th th:replace="fragments/sorting :: sorting('title','Title')">th>
    7. <th th:replace="fragments/sorting :: sorting('description','Description')">th>
    8. <th th:replace="fragments/sorting :: sorting('level','Level')">th>
    9. <th>Publishedth>
    10. <th>Actionsth>
    11. tr>
    12. thead>
    13. <tbody>
    14. <tr th:each="tutorial : ${tutorials}">
    15. <th scope="row">[[${tutorial.id}]]th>
    16. <td>[[${tutorial.title}]]td>
    17. <td>[[${tutorial.description}]]td>
    18. <td>[[${tutorial.level}]]td>
    19. <td>
    20. <a th:if="${tutorial.published == true}" class="fa-regular fa-square-check"
    21. th:href="@{'/tutorials/' + ${tutorial.id} + '/published/false'}" title="Disable this tutorial">a>
    22. <a th:if="${tutorial.published == false}" class="fa-regular fa-square icon-dark"
    23. th:href="@{'/tutorials/' + ${tutorial.id} + '/published/true'}" title="Enable this tutorial">a>
    24. td>
    25. <td>
    26. <a th:href="@{'/tutorials/' + ${tutorial.id}}" title="Edit this tutorial"
    27. class="fa-regular fa-pen-to-square icon-dark">a>
    28. <a th:href="@{'/tutorials/delete/' + ${tutorial.id}}" th:tutorialTitle="${tutorial.title}" id="btnDelete"
    29. title="Delete this tutorial" class="fa-regular fa-trash-can icon-dark btn-delete">a>
    30. td>
    31. tr>
    32. tbody>
    33. table>
    34. div>
    35. <div class="" th:unless="${tutorials.size() > 0}">
    36. <span>No tutorials found!span>
    37. div>
    38. <nav aria-label="Pagination" th:if="${totalPages > 0}">
    39. <ul class="pagination justify-content-center">
    40. <li class="page-item" th:classappend="${currentPage == 1} ? 'disabled'">
    41. <a th:replace="fragments/paging :: paging(1, '<<', 'First Page')">a>
    42. li>
    43. <li class="page-item font-weight-bold" th:classappend="${currentPage == 1} ? 'disabled'">
    44. <a th:replace="fragments/paging :: paging(${currentPage - 1}, 'Prev', 'Previous Page')">a>
    45. li>
    46. <li class="page-item disabled" th:if="${currentPage - 2 > 1}">
    47. <a class="page-link" href="#">...a>
    48. li>
    49. <li class="page-item" th:classappend="${page == currentPage} ? 'active'"
    50. th:each="page : ${#numbers.sequence(currentPage > 2 ? currentPage - 2 : 1, currentPage + 2 < totalPages ? currentPage + 2 : totalPages)}">
    51. <a th:replace="fragments/paging :: paging(${page}, ${page}, 'Page ' + ${page})">a>
    52. li>
    53. <li class="page-item disabled" th:if="${currentPage + 2 < totalPages}">
    54. <a class="page-link" href="#">...a>
    55. li>
    56. <li class="page-item font-weight-bold" th:classappend="${currentPage == totalPages} ? 'disabled'">
    57. <a th:replace="fragments/paging :: paging(${currentPage + 1},'Next', 'Next Page')">a>
    58. li>
    59. <li class="page-item" th:classappend="${currentPage == totalPages} ? 'disabled'">
    60. <a th:replace="fragments/paging :: paging(${totalPages}, '>>', 'Last Page')">a>
    61. li>
    62. ul>
    63. nav>

    然后我们继续使用输入关键字和页面大小修改搜索表单:

    教程.html

    1. <div>
    2. <form th:action="@{/tutorials}" id="searchForm">
    3. <div class="row d-flex">
    4. <div class="col-md-6 mt-2">
    5. <div class="search">
    6. <i class="fa fa-search">i>
    7. <input id="keyword" type="search" name="keyword" th:value="${keyword}" required class="form-control"
    8. placeholder="Enter keyword">
    9. <button type="submit" class="btn btn-secondary">Searchbutton>
    10. div>
    11. div>
    12. <div class="col-md-3 input-group mt-2">
    13. <div class="input-group-prepend">
    14. <label class="input-group-text" for="pageSize">Items per page:label>
    15. div>
    16. <select form="searchForm" name="size" th:value="${pageSize}" onchange="changePageSize()" class="size-select"
    17. id="pageSize">
    18. <option th:each="s : ${ {3, 6, 9} }" th:value="${s}" th:text="${s}" th:selected="${s == pageSize}">option>
    19. select>
    20. div>
    21. <div class="col-md-1 mt-2">
    22. <button id="btnClear" class="btn btn-info">Clearbutton>
    23. div>
    24. div>
    25. form>
    26. div>
    27. <script type="text/javascript">
    28. $(document).ready(function () {
    29. // ...
    30. $("#btnClear").on("click", function (e) {
    31. e.preventDefault();
    32. $("#keyword").text("");
    33. window.location = "[[@{/tutorials}]]";
    34. });
    35. });
    36. function changePageSize() {
    37. $("#searchForm").submit();
    38. }
    39. script>

    运行春季启动百里香叶分页和排序示例

    使用命令运行 Spring Boot 应用程序:。mvn spring-boot:run

    结论

    在这篇文章中,我们学习了如何使用 Bootstrap 和 Spring Data JPA 在 Spring Boot 示例中按列标题对 Thymeleaf 进行分页和排序表。

    我们还看到它支持一种很好的方法,无需样板代码即可使用过滤器方法进行服务器端分页和排序。JpaRepository

    更多派生查询:
    Spring 引导中的 JPA 存储库查询示例

    带有注释的自定义查询:
    Spring JPA @Query示例:Spring 引导中的自定义查询@Query

    按多个字段排序/排序:
    Spring 数据 JPA 按多列排序/排序

    此 Rest API 的句柄异常是必需的:
    –Spring Boot @ControllerAdvice & @ExceptionHandler 示例–Spring Boot 中@RestControllerAdvice示例

    或者编写单元测试的方法:
    –JPA 存储库的弹簧引导单元测试
    – 其余控制器的弹簧引导单元测试

    祝你学习愉快!再见。

    源代码

    您可以在Github 上找到本教程的完整源代码。

    延伸阅读

    –Spring Boot Thymeleaf CRUD 示例 – Spring Boot 分页和排序 REST API 示例

    全栈 CRUD 应用程序:
    –Vue + 弹簧引导示例–角度 8 + 弹簧引导示例–角度 10 + 弹簧引导示例–角度 11 + 弹簧引导示例–角度 12 + 弹簧引导示例–角度 13 + 弹簧引导示例–角度 14 + 弹簧引导示例
    –反应 + 弹簧启动示例





     

    全栈分页:
    –Spring Boot + React:分页示例–Spring Boot + Angular:分页示例

    更多实践:
    –Spring Boot with Spring Security & JWT Authentication
    –Spring Boot Rest XML 示例–Spring Boot Multipart File 上传示例

    Spring Boot 按多列排序/排序

    关联:
    –使用 JPA 的 Spring Boot 一对一示例,使用 JPA 的 Hibernate–Spring Boot One To More 示例,使用 JPA 的 Hibernate

    Spring Boot Many to More 示例与 JPA,Hibernate

  • 相关阅读:
    原生js 多用途三元表达式 点赞按钮
    K8S云原生渗透实战
    Pytorch_course1
    【云原生 • Docker】docker 环境搭建、docker 与容器常用指令大全
    基于JAVA个人信息管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    Springboot、maven 打包瘦身,去除依赖的jar【springboot外置jar、配置文件】
    Linux学习记录——삼십이 协议、序列化和反序列化
    现代控制理论课程实验二:利用状态观测器实现状态反馈的系统设计
    SpringCloud--nacos 入门使用
    js正则案例
  • 原文地址:https://blog.csdn.net/allway2/article/details/128068882