Boot JPA和自定义的分页
注意:本页面内容为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_mobilephone":"18899998888","id":1,"age":8,"studentClass":"三班"},{"parent_name":"李寻欢","grade":"五年级","name":"李四","parent_mobilephone":"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_mobilephone": "18899998888",
- "id": 1,
- "age": 8,
- "studentClass": "三班"
- },
- {
- "parent_name": "李寻欢",
- "grade": "五年级",
- "name": "李四",
- "parent_mobilephone": "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>");
- 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”,就可以看到最终效果啦:
注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue 发布时间:2019/10/10 15:02:01