SpringBoot + Spring Data JPA + Thmeleaf 分页实现仿哔哩哔哩

之前介绍过一次 SpringBoot + Spring Data JPA + Thymeleaf 分页,但是不够详细,这次再重新发一次。

 

先看效果图,动态图,可以点击放大。

因为数据有限,效果图里是设置一页显示一条记录。

SpringBoot + Spring Data JPA + Thmeleaf 分页实现仿哔哩哔哩

 

这里是以评论列表作为例子,主要贴出和分页有关的代码,至于 Dao 和 Service 这里省略。

 

一、Controller

  1. /**
  2.     * ajax 获取评论页面数据
  3.     *
  4.     * @param postId
  5.     * @param model
  6.     * @return
  7.     */
  8.    @GetMapping
  9.    public ModelAndView listComments(
  10.            @RequestParam(value = "postId") Long postId,
  11.            @RequestParam(value = "async", required = false, defaultValue = "new") Boolean async,
  12.            @RequestParam(value = "order", required = false, defaultValue = "new") String order,
  13.            @RequestParam(value = "pageIndex", required = false, defaultValue = "1") Integer pageIndex,
  14.            @RequestParam(value = "pageSize", required = false, defaultValue = "1") Integer pageSize,
  15.            Model model) {
  16.        Page<Comment> commentPage = null;
  17.        Post post = postService.getPostById(postId);
  18.        String commentOrder = "new";
  19.        try {
  20.            if (order.equals("hot")) { // 最热查询
  21.                Sort sort = new Sort(Sort.Direction.DESC, "zanSize""id");  //根据点赞数量排序
  22.                Pageable pageable = new PageRequest(pageIndex - 1, pageSize, sort);
  23.                commentPage = commentService.listCommentByPost(post, pageable);
  24.                commentOrder = "hot";
  25.            } else if (order.equals("new")) { // 最新查询
  26.                Sort sort = new Sort(Sort.Direction.DESC, "id");
  27.                Pageable pageable = new PageRequest(pageIndex - 1, pageSize, sort);
  28.                commentPage = commentService.listCommentByPost(post, pageable);
  29.            }
  30.        } catch (Exception e) {
  31.            Pageable pageable = new PageRequest(010);
  32.            commentPage = commentService.listCommentByPost(post, pageable);
  33.        }
  34.        Integer commentSize = post.getCommentSize();
  35.        model.addAttribute("page", commentPage);
  36.        model.addAttribute("commentSize", commentSize);
  37.        model.addAttribute("commentOrder", commentOrder);
  38.        model.addAttribute("post", post);
  39.        return new ModelAndView(async == true ? "home/post_detail :: #comment" : "home/post_detail");
  40.    }

 

二、HTML 代码

1、post_detail.html

本项目使用了 Spring Security ,开启了 CSRF 防护,所以需要在 head 里引入

  1. <meta name="_csrf" th:content="${_csrf.token}"/>
  2. <meta name="_csrf_header" th:content="${_csrf.headerName}"/>

 

这是 Thymeleaf 的替换标签

  1. <!--分页-->
  2. <div th:replace="~{home/fragments/page :: header-page}"></div>
  3. <!--/分页-->

 

页面底部,引入 JS,将文章的id postId 传到 js 里

  1. <script th:inline="javascript">
  2.     var postId = [[${post.id}]];
  3.     var postUrl = '/' + [[${post.user.username}]] + '/posts/' + [[${post.id}]];
  4. </script>

 

page.html

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport"
  6.           content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  7.     <meta http-equiv="X-UA-Compatible" content="ie=edge">
  8.     <title>分页</title>
  9. </head>
  10. <body>
  11. <div data-th-fragment="header-page">
  12.     <div class="header-page paging-box"
  13.          th:if="${(page.totalPages gt 0) && (page.totalPages le 7)}"
  14.          data-th-attr="data-order=${commentOrder}">
  15.         <span class="result">共[[${page.totalPages}]]页</span>
  16.         <a href="javascript:void(0)" class="page-link prev"
  17.            th:classappend="${page.first?'disabled':''}"
  18.            data-th-attr="pageIndex=${page.number}">上一页</a>
  19.         <a href="javascript:void(0)" class="page-link"
  20.            th:classappend="${(page.number+1) eq i} ?'current':''"
  21.            th:each="i: ${#numbers.sequence(1, page.totalPages)}"
  22.            data-th-attr="pageIndex=${i}">[[${i}]]</a>
  23.         <a href="javascript:void(0)" class="page-link next"
  24.            th:classappend="${page.last?'disabled':''}"
  25.            data-th-attr="pageIndex=${page.number+2}">下一页</a>
  26.     </div>
  27.     <div class="header-page paging-box"
  28.          th:if="${page.totalPages gt 7}"
  29.          data-th-attr="data-order=${commentOrder}">
  30.         <span class="result">共[[${page.totalPages}]]页</span>
  31.         <!--上一页-->
  32.         <a href="javascript:void(0)" class="page-link prev"
  33.            th:classappend="${page.first?'disabled':''}"
  34.            data-th-attr="pageIndex=${page.number}">上一页</a>
  35.         <!--首页-->
  36.         <a href="javascript:void(0)" class="page-link"
  37.            th:classappend="${(page.number+1) eq 1} ?'current':''"
  38.            data-th-attr="pageIndex=1">
  39.             1
  40.         </a>
  41.         <!-- 当前页面小于等于4 -->
  42.         <a href="javascript:void(0)" class="page-link"
  43.            th:if="${(page.number+1) le 4}"
  44.            th:classappend="${(page.number+1) eq i} ?'current':''"
  45.            data-th-each="i : ${#numbers.sequence(2,5)}"
  46.            data-th-attr="pageIndex=${i}">[[${i}]]</a>
  47.         <span class="dian" data-th-if="${(page.number + 1) le 4}">...</span>
  48.         <!-- 最后一页与当前页面之差,小于等于3 -->
  49.         <span class="dian"
  50.               data-th-if="${(page.totalPages-(page.number + 1)) le 3}">...</span>
  51.         <a href="javascript:void(0)" class="page-link"
  52.            th:if="${(page.totalPages-(page.number + 1)) le 3}"
  53.            th:classappend="${(page.number+1) eq i} ?'current':''"
  54.            th:each="i : ${#numbers.sequence(page.totalPages-4, page.totalPages-1)}"
  55.            data-th-attr="pageIndex=${i}">[[${i}]]</a>
  56.         <!-- 最后一页与当前页面之差大于3,且当前页面大于4-->
  57.         <span class="dian"
  58.               data-th-if="${((page.number + 1) gt 4) && ((page.totalPages-(page.number + 1)) gt 3 )}">...</span>
  59.         <a href="javascript:void(0)" class="page-link"
  60.            th:if="${((page.number + 1) gt 4) && ((page.totalPages-(page.number + 1)) gt 3 )}"
  61.            data-th-attr="pageIndex=${page.number}">[[${page.number}]]</a>
  62.         <a href="javascript:void(0)" class="page-link current"
  63.            th:if="${((page.number + 1) gt 4) && ((page.totalPages-(page.number + 1)) gt 3 )}"
  64.            data-th-attr="pageIndex=${page.number+1}">[[${page.number
  65.             +1 }]]</a>
  66.         <a href="javascript:void(0)" class="page-link"
  67.            th:if="${((page.number + 1) gt 4) && ((page.totalPages-(page.number + 1)) gt 3 )}"
  68.            data-th-attr="pageIndex=${page.number+2}">[[${page.number
  69.             +2 }]]</a>
  70.         <span class="dian" data-th-if="${((page.number + 1) gt 4) && ((page.totalPages-(page.number + 1)) gt 3 )}">...</span>
  71.         <!--尾页-->
  72.         <a href="javascript:void(0)" class="page-link"
  73.            th:classappend="${(page.number+1) eq page.totalPages} ?'current':''"
  74.            data-th-attr="pageIndex=${page.totalPages}">
  75.             [[${page.totalPages}]]
  76.         </a>
  77.         <!--下一页-->
  78.         <a href="javascript:void(0)" class="page-link next"
  79.            th:classappend="${page.last?'disabled':''}"
  80.            data-th-attr="pageIndex=${page.number+2}">下一页</a>
  81.     </div>
  82. </div>
  83. <div data-th-fragment="bottom-page">
  84.     <div class="bottom-page paging-box-big" th:if="${(page.totalPages gt 0) && (page.totalPages le 7)}"
  85.          data-th-attr="data-order=${commentOrder}">
  86.         <a href="javascript:void(0)" class="page-link prev"
  87.            th:classappend="${page.first?'disabled':''}"
  88.            data-th-attr="pageIndex=${page.number}">上一页</a>
  89.         <a href="javascript:void(0)" class="page-link"
  90.            th:classappend="${(page.number+1) eq i} ?'current':''"
  91.            th:each="i: ${#numbers.sequence(1, page.totalPages)}"
  92.            data-th-attr="pageIndex=${i}">[[${i}]]</a>
  93.         <a href="javascript:void(0)" class="page-link next"
  94.            th:classappend="${page.last?'disabled':''}"
  95.            data-th-attr="pageIndex=${page.number+2}">下一页</a>
  96.         <div class="page-jump"><span>[[${page.totalPages}]]</span>页,跳至
  97.             <input type="number" class="jump-page-size" th:max="${page.totalPages}">
  98.         </div>
  99.     </div>
  100.     <div class="bottom-page paging-box-big"
  101.          th:if="${page.totalPages gt 7}"
  102.          data-th-attr="data-order=${commentOrder}">
  103.         <!--上一页-->
  104.         <a href="javascript:void(0)" class="page-link prev"
  105.            th:classappend="${page.first?'disabled':''}"
  106.            data-th-attr="pageIndex=${page.number}">上一页</a>
  107.         <!--首页-->
  108.         <a href="javascript:void(0)" class="page-link"
  109.            th:classappend="${(page.number+1) eq 1} ?'current':''"
  110.            data-th-attr="pageIndex=1">
  111.             1
  112.         </a>
  113.         <!-- 当前页面小于等于4 -->
  114.         <a href="javascript:void(0)" class="page-link"
  115.            th:if="${(page.number+1) le 4}"
  116.            th:classappend="${(page.number+1) eq i} ?'current':''"
  117.            data-th-each="i : ${#numbers.sequence(2,5)}"
  118.            data-th-attr="pageIndex=${i}">[[${i}]]</a>
  119.         <span class="dian" data-th-if="${(page.number + 1) le 4}">...</span>
  120.         <!-- 最后一页与当前页面之差,小于等于3 -->
  121.         <span class="dian"
  122.               data-th-if="${(page.totalPages-(page.number + 1)) le 3}">...</span>
  123.         <a href="javascript:void(0)" class="page-link"
  124.            th:if="${(page.totalPages-(page.number + 1)) le 3}"
  125.            th:classappend="${(page.number+1) eq i} ?'current':''"
  126.            th:each="i : ${#numbers.sequence(page.totalPages-4, page.totalPages-1)}"
  127.            data-th-attr="pageIndex=${i}">[[${i}]]</a>
  128.         <!-- 最后一页与当前页面之差大于3,且当前页面大于4-->
  129.         <span class="dian"
  130.               data-th-if="${((page.number + 1) gt 4) && ((page.totalPages-(page.number + 1)) gt 3 )}">...</span>
  131.         <a href="javascript:void(0)" class="page-link"
  132.            th:if="${((page.number + 1) gt 4) && ((page.totalPages-(page.number + 1)) gt 3 )}"
  133.            data-th-attr="pageIndex=${page.number}">[[${page.number}]]</a>
  134.         <a href="javascript:void(0)" class="page-link current"
  135.            th:if="${((page.number + 1) gt 4) && ((page.totalPages-(page.number + 1)) gt 3 )}"
  136.            data-th-attr="pageIndex=${page.number+1}">[[${page.number
  137.             +1 }]]</a>
  138.         <a href="javascript:void(0)" class="page-link"
  139.            th:if="${((page.number + 1) gt 4) && ((page.totalPages-(page.number + 1)) gt 3 )}"
  140.            data-th-attr="pageIndex=${page.number+2}">[[${page.number
  141.             +2 }]]</a>
  142.         <span class="dian" data-th-if="${((page.number + 1) gt 4) && ((page.totalPages-(page.number + 1)) gt 3 )}">...</span>
  143.         <!--尾页-->
  144.         <a href="javascript:void(0)" class="page-link"
  145.            th:classappend="${(page.number+1) eq page.totalPages} ?'current':''"
  146.            data-th-attr="pageIndex=${page.totalPages}">
  147.             [[${page.totalPages}]]
  148.         </a>
  149.         <!--下一页-->
  150.         <a href="javascript:void(0)" class="page-link next"
  151.            th:classappend="${page.last?'disabled':''}"
  152.            data-th-attr="pageIndex=${page.number+2}">下一页</a>
  153.         <div class="page-jump"><span>[[${page.totalPages}]]</span>页,跳至
  154.             <input type="number" name="pageIndex" class="jump-page-size" min="1" th:max="${page.totalPages}">
  155.         </div>
  156.     </div>
  157.     <script>
  158.         var pageIndex = [[${page.number+1}]]
  159.     </script>
  160. </div>
  161. </body>
  162. </html>

因为很多页面都要使用分页,所以完全可以把分页的代码剥离出来,公共使用。只需要将分页的数据的 model 写成 page 就行。这里分页分成了两种,一种是分页少于等于7个的,我们直接显示 1234567;另一种是大于7 的,则中间以..分隔。

 

三、JS 代码

  1. $(function () {
  2.     // 获取评论列表
  3.     function getComment(postId, pageIndex, order) {
  4.         var _ctx = $("meta[name='ctx']").attr("content");
  5.         // 获取 CSRF Token
  6.         var token = $("meta[name='_csrf']").attr("content");
  7.         var header = $("meta[name='_csrf_header']").attr("content");
  8.         $.ajax({
  9.             url: _ctx + '/comments',
  10.             type: 'GET',
  11.             data: {
  12.                 "async"true,
  13.                 "postId": postId,
  14.                 "pageIndex": pageIndex,
  15.                 "order": order
  16.             },
  17.             beforeSend: function (request) {
  18.                 request.setRequestHeader(header, token); // 添加  CSRF Token
  19.             },
  20.             success: function (data) {
  21.                 $("#comment-wrapper").html(data);
  22.             },
  23.             error: function () {
  24.                 layer.msg("出现错误,请尝试刷新页面!", {icon: 2, anim: 6});
  25.             }
  26.         });
  27.     };
  28.     //切换评论排序规则
  29.     $(document).on('click', '.tabs-order .new-sort', function () {
  30.         var pageIndex = $(this).attr("pageIndex");
  31.         getComment(postId, pageIndex, "new");
  32.     });
  33.     $(document).on('click', '.tabs-order .hot-sort', function () {
  34.         var pageIndex = $(this).attr("pageIndex");
  35.         getComment(postId, pageIndex, "hot");
  36.     });
  37.     //分页获取评论列表
  38.     $(document).on('click', '.tcd-number', function () {
  39.         var order = $(this).parents(".paging-box").attr("data-order");
  40.         if ($(this).hasClass('current')) {
  41.             return false;
  42.         }
  43.         var pageIndex = $(this).attr("pageIndex");
  44.         getComment(postId, pageIndex, order);
  45.     });
  46.     //跳转到指定的页号
  47.     $(document).on('keydown', '.jump-page-size', function (event) {
  48.         var max = parseInt($(this).attr("max"));
  49.         var pageIndex = parseInt($(this).val());
  50.         var order = $('.paging-box').attr('data-order');
  51.         if (event.keyCode == "13") {//keyCode=13是回车键
  52.             if (pageIndex == "" || pageIndex == null) {
  53.                 return false;
  54.             }
  55.             if (!/^\d+$/.test(pageIndex)) {
  56.                 pageIndex = 1;
  57.             }
  58.             if (pageIndex < 1) {
  59.                 pageIndex = 1;
  60.             }
  61.             if (pageIndex > max) {
  62.                 pageIndex = max;
  63.             }
  64.             getComment(postId, pageIndex, order);
  65.         }
  66.     })
  67. // 初始化 博客评论列表
  68.     getComment(postId, 1, "new");
  69. })

 

四、CSS 代码

为了方便大家的使用,这里同时提供 CSS 代码。

注:本模板是扒自哔哩哔哩,侵删

page.css

  1. .paging-box {
  2.     font-size12px
  3. }
  4. .paging-box .disabled {
  5.     cursor: not-allowed
  6. }
  7. .paging-box .current, .paging-box .dian, .paging-box .next, .paging-box .prev, .paging-box .tcd-number {
  8.     color#222;
  9.     cursorpointer;
  10.     text-aligncenter;
  11.     margin: 0 4px;
  12.     text-decorationnone;
  13.     line-height26px
  14. }
  15. .paging-box .current:hover, .paging-box .dian:hover, .paging-box .next:hover, .paging-box .prev:hover, .paging-box .tcd-number:hover {
  16.     color#00a1d6
  17. }
  18. .paging-box .current {
  19.     color#00a1d6;
  20.     font-weight: 700
  21. }
  22. .paging-box .dian {
  23.     cursordefault
  24. }
  25. .paging-box .dian:hover {
  26.     color#222
  27. }
  28. .paging-box .result {
  29.     padding-right10px
  30. }
  31. .paging-box-big {
  32.     font-size12px
  33. }
  34. .paging-box-big .disabled {
  35.     displaynone
  36. }
  37. .paging-box-big .current, .paging-box-big .dian, .paging-box-big .next, .paging-box-big .prev, .paging-box-big .tcd-number {
  38.     color#222;
  39.     cursorpointer;
  40.     text-aligncenter;
  41.     border-radius: 4px;
  42.     background-color#fff;
  43.     border1px solid #ddd;
  44.     background-imagenone;
  45.     transition: all .2s;
  46.     font-size14px;
  47.     min-width15px;
  48.     margin: 0 2px;
  49.     padding: 0 13px;
  50.     displayinline-block;
  51.     height36px;
  52.     line-height36px;
  53.     text-decorationnone
  54. }
  55. .paging-box-big .current, .paging-box-big .current:hover, .paging-box-big .dian:hover, .paging-box-big .next:hover, .paging-box-big .prev:hover, .paging-box-big .tcd-number:hover {
  56.     background#00a1d6;
  57.     color#fff;
  58.     border1px solid #00a1d6
  59. }
  60. .paging-box-big .dian {
  61.     cursordefault;
  62.     border-color#fff
  63. }
  64. .paging-box-big .dian:hover {
  65.     background#fff;
  66.     color#222;
  67.     border1px solid #fff
  68. }
  69. .paging-box-big .next, .paging-box-big .prev {
  70.     padding: 0 15px
  71. }
  72. .paging-box-big .page-jump {
  73.     floatrightright;
  74.     color#99a2aa;
  75.     line-height36px
  76. }
  77. .paging-box-big .page-jump input {
  78.     margin: 0 5px;
  79.     padding: 0 2px;
  80.     height24px;
  81.     line-height24px;
  82.     margin-top7px;
  83.     font-size12px;
  84.     box-shadow: none;
  85.     width40px;
  86.     border-radius: 4px;
  87.     border1px solid #ddd;
  88.     outline: 0;
  89.     text-aligncenter
  90. }
  91. .paging-box-big .page-jump input:focus {
  92.     border-color#00a1d6
  93. }

 

 

五、补充

1、Spring Data JPA 提供了分页器,可以直接使用。

  1. /**
  2.  * 根据文章获取评论,分页显示
  3.  *
  4.  * @param post
  5.  * @param pid
  6.  * @return
  7.  */
  8. Page<Comment> findByPostAndPid(Post post, Long pid, Pageable pageable);

 

其中 pageable 可以通过创建 PageRequest 对象获得,需要传入 page 页码,size 一页显示数量。sort 是排序规则,相当于 sql 语句里的 order by,可选。

  1. Sort sort = new Sort(Sort.Direction.DESC, "zanSize""id");
  2. Pageable pageable = new PageRequest(pageIndex - 1, pageSize, sort);

 

2、将现成 List 转成 Page,也可以这样

  1. List<Comment> commentList = post.getCommentList()
  2. PageRequest pageRequest = new PageRequest(pageIndex,pageSize);
  3. Page<Comment> commentPage = new PageImpl<Comment>(commentList,pageRequest,commentList.size());

 

3、page 对象的几个属性

  1. 总共页数:${userDTOPage.totalPages}
  2. 记录总数:${userDTOPage.totalElements}
  3. 当前页号:${userDTOPage.number}
  4. 是否为首页:${userDTOPage.first}
  5. 是否为尾页:${userDTOPage.last}
  6. 每页显示的数量:${userDTOPage.numberOfElements}

 

 

六、其他分页内容

SpringBoot Spring Data JPA Thymeleaf 实现关注者和粉丝,互相关注分页显示

SpringMVC+Spring Data JPA + JSP + Bootstrap 分页实现和模糊查询分页

SSM + JSP 自定义分页

 

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
言曌

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

目前评论:2   其中:访客  2   博主  0

    • avatar 远风

      Spring Boot 2.0之后已经废弃new PageRequest()方法,换用静态方法PageRequest.of(pageNum,pageSize,Sort.Direction…..)

      • avatar q

        你好,有源码吗或者github