课程表

Spring Boot课程

工具箱
速查手册

Boot JPA和自定义的分页

当前位置:免费教程 » Java相关 » Spring Boot
注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue  发布时间:2019/10/10 15:02:01

分页是现代Web应用程序中常用的功能,JPA其实提供了非常简便的分页功能。但是,在开发的时候,我们往往会发现JPA的分页过于臃肿,灵活性不佳,w3xue在这里提供了一种完全自定义的分页方式,供大家学习参考。首先,我们来看下JPA的分页功能。


一、JPA分页

JPA的分页使用非常简单,只需要2步即可。第1步,MainService接口中定义一个方法:

Page<MainBean> findByJpaPage(int page, int size);

MainServiceImpl类中实现该方法:

@Override
public Page<MainBean> findByJpaPage(int page, int size){
    return mainDao.findAll(PageRequest.of(page,size));
}

根据需要,你必须引用这2个依赖包(IntelliJ IDEA 会帮你搞定):

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;

在更老的版本中,实现方法可能是这样的:

@SuppressWarnings("unchecked") //强制类型转换,自定义SQL、自定义分页用
public Page<MainBean> findByJpaPage(int page, int size){
    PageRequest pageRequest = new PageRequest(page, size);
    return mainDao.findAll(pageRequest);
}

你会发现2点不同,一是使用@SuppressWarnings注解,让编译器忽略指定的警告;二是申明了一个实体,用这个实体作为参数传给JPA的自定义分页系统,而后来的版本中,是直接使用静态方法体返回一个PageRequest对象来做到这一点的,语法更简洁,实际上老版本的申明实体代码应该会被划上删除线,表明已经不受系统推荐了:PageRequest(page, size)

第2步,在MainRestController类加上一个新的路由和方法体:

@GetMapping("/jpaPage")
public JSONObject jpaPage(@RequestParam("page") int pPage, @RequestParam("size") int pSize){
    Page<MainBean> studentPage =  mainServiceImpl.findByJpaPage(pPage, pSize);
    JSONObject jsonObject = (JSONObject) JSON.toJSON(studentPage);
    return jsonObject;
}

然后重新启动项目,访问“http://localhost:8080/jiaocheng/jpaPage?page=0&size=2”,你会看到出现如下结果:

{"number":0,"last":false,"numberOfElements":2,"size":2,"totalPages":2,"pageable":{"paged":true,"pageNumber":0,"offset":0,"pageSize":2,"unpaged":false,"sort":{"unsorted":true,"sorted":false,"empty":true}},"sort":{"unsorted":true,"sorted":false,"empty":true},"content":[{"parent_name":"张无忌","grade":"二年级","name":"张三","parent_moblephone":"18899998888","id":1,"age":8,"studentClass":"三班"},{"parent_name":"李寻欢","grade":"五年级","name":"李四","parent_moblephone":"17722338899","id":2,"age":11,"studentClass":"六班"}],"first":true,"empty":false,"totalElements":3}

我们使用JSON在线编辑器来整理一下:

{
    "number": 0,
    "last": false,
    "numberOfElements": 2,
    "size": 2,
    "totalPages": 2,
    "pageable": {
        "paged": true,
        "pageNumber": 0,
        "offset": 0,
        "pageSize": 2,
        "unpaged": false,
        "sort": {
            "unsorted": true,
            "sorted": false,
            "empty": true
        }
    },
    "sort": {
        "unsorted": true,
        "sorted": false,
        "empty": true
    },
    "content": [
        {
            "parent_name": "张无忌",
            "grade": "二年级",
            "name": "张三",
            "parent_moblephone": "18899998888",
            "id": 1,
            "age": 8,
            "studentClass": "三班"
        },
        {
            "parent_name": "李寻欢",
            "grade": "五年级",
            "name": "李四",
            "parent_moblephone": "17722338899",
            "id": 2,
            "age": 11,
            "studentClass": "六班"
        }
    ],
    "first": true,
    "empty": false,
    "totalElements": 3
}

你可以看到,http://localhost:8080/jiaocheng/jpaPage?page=0&size=2”这个地址带了2个参数,一个是page,一个是size。page是从0开始的,0就是第1页,1就是第2页,以此类推,而size则是每页数据的多少,这里定义为2,因为迄今为止,我们只添加了3条测试信息。取到这个JSON数据后,前端就可以用来进行无刷新的分页制作了。

可以看出,JPA分页最大的优点是,太方便了,几行代码就搞定了分页,缺点大家也看到了,那一坨JSON数据,不整理一下简直没法看,臃肿不堪,很不灵活。


二、w3xue提供的自定义分页

我们在这里提供一个自定义分页的解决方案。其实,JPA的分页最终利用的也是MySql支持的“LIMIT”语法。我们在这里就利用这一点,搭建我们自定义的方案。本方案相对复杂,但搭建后,使用起来相对灵活。

第1步

我们在MainDao类添加一个自定义的方法体和HQL:

@Query(value = "SELECT * FROM t_w3xue_student order by f_id desc LIMIT :start,:number", nativeQuery = true)
public List<MainBean> getByW3xuePage(@Param("start") Integer start, @Param("number") Integer number);

第2步

我们在MainService接口中定义一个方法:

String findByW3xuePage(int pageid, int size);

然后,在MainServiceImpl类中实现该方法:

@Override
public String findByW3xuePage(int pageid, int size) {
    Integer nStart=(pageid-1)*size; //开始索引
    StringBuilder sb=new StringBuilder();
    try {
        List<MainBean> stuData=mainDao.getByW3xuePage(nStart,size);
        for (MainBean stu:stuData) {
            sb.append("<li><strong>"+stu.getId() +"</strong>号学生:"+stu.getName()+",年级和班级:"+stu.getGrade()+" "+stu.getStudentClass()+",家长姓名:"+stu.getParent_name()+"</li>");
        }
    } catch (Exception e) {
        sb.append("暂时没有数据");
    }
    return sb.toString();
}

第3步

我们在程序代码主目录建一个新的“utils”文件夹,和“bean”、“controller”等平行,然后存放页码显示工具类PageUtil类

package com.w3xue.jiaocheng.utils;

public class PageUtil {
    //显示页码
    public static String showPage(Integer datacount, int maxperpage, String wholepagename, String para, Integer nPage, Integer showlevel) {
        int pageid = 1, dspage;
        boolean pageidIsnum;
        String rank_pageid = "", rank_pageid2 = "", action = "";
        StringBuilder page_result = new StringBuilder();

        if (para.length() > 0)   //在使用分页时带上rank参数,如果无视rank,则注释掉这个模块,如需添加,在下面模块添加
        {
            rank_pageid = "?" + para;
            rank_pageid2 = "&" + para;
        }

        dspage = datacount % maxperpage != 0 ? (int) datacount / maxperpage + 1 : (int) datacount / maxperpage;

        if (nPage < 2)
            pageid = 1;
        else if (pageid > dspage) //页码大于总页数
            pageid = dspage;
        else
            pageid = nPage;

        page_result.append("<b class=\"shenhui qianpageid\">记录数:" + datacount + " 页数:" + pageid + "/" + dspage + "</b>");

        if (pageid != 1 && showlevel < 4) {
            page_result.append("<a class=\"pageid\" href=\"" + wholepagename + rank_pageid + "\" target=\"_self\">首页<a>");
        }


        if (pageid != 1 && pageid != 2) {
            page_result.append("<a class=\"pageid\" href=\"" + wholepagename + "?pageid=" + (pageid - 1) + rank_pageid2 + "\" target=\"_self\">上一页<a>");
        } else if (pageid == 2) {
            page_result.append("<a class=\"pageid\" href=\"" + wholepagename + rank_pageid + "\" target=\"_self\">上一页<a>");
        }

        int l;
        if (pageid > dspage - 5) {
            l = pageid - 5 - (4 - dspage + pageid);
        } else {
            l = pageid - 5;
        }
        if (l < 1) {
            l = 1;
        }
        while (l < pageid && showlevel < 3) {
            if (l == 1) {
                page_result.append("<a class=\"pageid\" href=\"" + wholepagename + rank_pageid + "\" target=\"_self\">" + l + "</a>");
            } else {
                page_result.append("<a class=\"pageid\" href=\"" + wholepagename + "?pageid=" + l + rank_pageid2 + "\" target=\"_self\">" + l + "</a>");
            }
            l = l + 1;
        }

        if (showlevel < 3)
            page_result.append("<b class=\"shenhui pageid dazi\">" + pageid + "</b></a>");

        if (showlevel < 3) {
            int r = pageid + 1;
            if (pageid < 6) {
                while (r <= 10 && r <= dspage && pageid != dspage) {
                    page_result.append("<a class=\"pageid\" href=\"" + wholepagename + "?pageid=" + r + rank_pageid2 + "\" target=\"_self\">" + r + "</a>");
                    r = r + 1;
                }
            } else {
                while (r < (pageid + 5) && r <= dspage && pageid != dspage) {
                    page_result.append("<a class=\"pageid\" href=\"" + wholepagename + "?pageid=" + r + rank_pageid2 + "\" target=\"_self\">" + r + "</a>");
                    r = r + 1;
                }
            }
        }


        if (pageid != dspage && datacount != 0) {
            page_result.append("<a class=\"pageid\" href=\"" + wholepagename + "?pageid=" + (pageid + 1) + rank_pageid2 + "\" target=\"_self\">下一页<a>");
        }
        if (pageid != dspage && datacount != 0 && showlevel < 4) {
            page_result.append("<a class=\"pageid\" href=\"" + wholepagename + "?pageid=" + dspage + rank_pageid2 + "\" target=\"_self\">尾页</a>");
        }
        if (showlevel < 2) {
            page_result.append(" <input type=\"number\" placeholder=\"页码\" id=\"pageinput\" style=\"width:50px;height:22px;margin:5px 4px\">");
            page_result.append("<input type=\"button\" onclick=\"if(document.getElementById('pageinput').value!=''){location.href='" + holepagename + "?pageid='+document.getElementById('pageinput').value+'" + rank_pageid2 + "'}\" style=\"width:50px;height:22px;margin:5px 4px\" value=\"跳转\" />");
        }
        return page_result.toString();
    }
}

这个类只有一个静态的方法:showPage,用来打印页码输出。它的几个参数作用如下:

  • datacount:数据总量,即需要显示的所有数据的条数

  • maxperpage:每页显示的数据量

  • wholepagename:完整的页面名称

  • para:带入的参数名称,如果页面需要其他参数,可以带入

  • nPage:当前页码

  • showlevel:显示等级,等级数值越小,显示的页码组件越多

这个方法是我封装好的,里面还带有CSS样式,可以配合页面的CSS使用。使用时直接将参数带入进去就可以,接下来会有详细的举例。

第4步

MainController类顶部申明一个“EntityManager”类的实体、并自动装配MainServiceImpl类的对象:

@PersistenceContext
EntityManager em;

@Autowired
private MainServiceImpl mainServiceImpl;

这需要使用“@PersistenceContext”注解,这个注解的作用是注入一坨保存实体类状态的数据结构,针对实体类的不同的状态(四种,managedh或detached等)可以做出不同的反应(merge,persist等等),其实就是把数据从数据库里提出,然后在内存里处理的,再返回数据库的法则。

申明“EntityManager”类的实体需要引用依赖包:

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

其他的依赖包,按照提示来即可。

import org.springframework.beans.factory.annotation.Autowired;
import com.w3xue.jiaocheng.service.MainServiceImpl;
import com.w3xue.jiaocheng.utils.PageUtil;
....

然后,我们在MainController类中定义一个路由和相应的方法体:

@RequestMapping("/diyPage")  //w3xue的自定义分页
public ModelAndView diyPage(ModelAndView mv,@RequestParam(name="pageid",defaultValue = "1") int pPage, @RequestParam(name="size",defaultValue = "1") int pSize) {
    Integer nStart=(pPage-1)*pSize; //开始索引

    Query query = em.createNativeQuery("SELECT COUNT(f_id) FROM t_w3xue_student");  //取出总数据量并放入内存
    Object object = query.getResultList().get(0);
    int totalElements = Integer.parseInt(object.toString());

    StringBuilder sbContent=new StringBuilder();
    sbContent.append(mainServiceImpl.findByW3xuePage(pPage,pSize)); //主体内容部分
    StringBuilder sbPage=new StringBuilder();
    sbPage.append(PageUtil.showPage(totalElements, pSize, "/jiaocheng/diyPage", "size="+pSize, pPage, 1)); //页码部分
    //你可以详细的看到showPage这个静态方法的参数作用,分别是:总数据量,每页数据量,页面地址,参数,当前页码,显示等级。你可以把显示等级调低,看看效果
    mv.addObject("content",sbContent.toString());
    mv.addObject("page",sbPage.toString());
    mv.setViewName("/list");
    return mv;
}

这个方法体需要的引用包:

import javax.persistence.Query;

第5步

我们在模板文件夹新建一个H5页面“list.html”,里面附带上关于页码的CSS样式:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .qianpageid  {font-size:14px;color:#333;display:block; float:left; height:25px; padding: 10px 0px 0px 0px; margin:5px 0x 0px 8px}
        .qianpageid2  {font-size:14px;color:#333; display:block; float:left; height:25px; padding: 10px 0px 0px 0px; margin:5px 0x 0px 8px}
        .pageid {font-size:14px;color:white; border:#fff 1px solid; display:block; float:left; background-color:#6C0002; height:30px; padding: 5px 6px 2px 6px; margin:5px 0px 0px 8px}
        a.pageid {text-decoration:none;color:#ddd; float:left;  border:#fff 1px solid; display:block; height:30px; padding: 5px 6px 2px 6px; margin:5px 0px 0px 8px}
        a.pageid:visited  {color:#ddd;}
        a.pageid:hover {text-decoration:underline;color:white; border:#fff 1px solid; float:left; display:block;  background-color:#6C0002; background-image:url('') ;height:30px; padding: 5px 6px 2px 6px; margin:5px 0px 0px 8px}

    </style>
</head>
<body>
<div th:utext="${content}"></div>
<div th:utext="${page}"></div>
</body>
</html>

最后,我们访问“http://localhost:8080/jiaocheng/diyPage?pageid=1&size=2”,就可以看到最终效果啦:

1.jpg

注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue  发布时间:2019/10/10 15:02:01