Java Web -【分页功能】详解
分页简介
分页功能在网页中是非常常见的一个功能,其作用也就是将数据分割成多个页面来进行显示。
- 使用场景: 当取到的数据量达到一定的时候,就需要使用分页来进行数据分割。
当我们不使用分页功能的时候,会面临许多的问题:
- 客户端的问题: 如果数据量太多,都显示在同一个页面的话,会因为页面太长严重影响到用户的体验,也不便于操作,也会出现加载太慢的问题。
- 服务端的问题: 如果数据量太多,可能会造成内存溢出,而且一次请求携带的数据太多,对服务器的性能也是一个考验。
分页的分类
分页的实现分为真分页和假分页两种,也就是物理分页和逻辑分页。
1.真分页(物理分页):
- 实现原理:
SELECT * FROM xxx [WHERE...] LIMIT #{param1}, #{param2}
第一个参数是开始数据的索引位置
第二个参数是要查询多少条数据- 优点: 不会造成内存溢出
- 缺点: 翻页的速度比较慢
2.假分页(逻辑分页):
- 实现原理: 一次性将所有的数据查询出来放在内存之中,每次需要查询的时候就直接从内存之中去取出相应索引区间的数据
- 优点: 分页的速度比较快
- 缺点: 可能造成内存溢出
传统的分页方式
对于假分页的实现方式很简单,只需要准备一个集合保存从数据库中取出的所有数据,然后根据当前页面的码数,取出对应范围的数据显示就好了,我们这里基于物理分页来实现。
分页的原理
- 页面中的数据有:
结果集:通过 SQL 语句查询得来的——List<Student>- 分页条中的数据有:
当前页:用户传递到后台——currentPage
总页数:计算的来——totalPage
上一页:计算的来——prePage
下一页:计算的来——nextPage
尾页:计算的来(总页数)——lastPage
页面大小(即每一页显示的条数):用户传递到后台——count
总条数:通过 SQL 语句查询得来的——totalCount
可以发现页面功能中需要用到的数据有两个是需要通过 SQL 语句查询得来的:一个是页面中显示的数据 List<Student> ,另一个是数据的总条数 totalCount,分别对应以下两条 SQL 语句:
SELECT * FROM student LIMIT #{param1}, #{param2}
SELECT COUNT(*) FROM student
通过计算得到的数据有:
- 总页数:totalPage
总页数 = 总条数 % 页面大小 == 0 ? 总条数 / 页面大小 : 总条数 / 页面大小 + 1- 上一页:prePage
上一页 = 当前页 - 1 > = 1 ? 当前页 - 1 : 1- 下一页:nextPage
下一页 = 当前页 + 1 <= totalPage ? 当前页 + 1 : totalPage- 尾页:lastPage
尾页 = 总条数 % 页面大小 == 0 ? 总条数 - 页面大小 : 总条数 - 总条数 % 页面大小
用户传递的数据:
- 当前页:currentPage
- 页面大小:count
所有我们可以创建一个 Page 工具类备用:
public class Page {int start; // 开始数据的索引int count; // 每一页的数量int total; // 总共的数据量/*** 提供一个构造方法* @param start* @param count*/ public Page(int start, int count) {super();this.start = start;this.count = count;}/*** 判断是否有上一页* @return*/public boolean isHasPreviouse(){if(start==0)return false;return true;}/*** 判断是否有下一页* @return*/public boolean isHasNext(){if(start==getLast())return false;return true;}/*** 计算得到总页数* @return*/public int getTotalPage(){int totalPage;// 假设总数是50,是能够被5整除的,那么就有10页if (0 == total % count)totalPage = total /count;// 假设总数是51,不能够被5整除的,那么就有11页elsetotalPage = total / count + 1;if(0==totalPage)totalPage = 1;return totalPage;}/*** 计算得到尾页* @return*/public int getLast(){int last;// 假设总数是50,是能够被5整除的,那么最后一页的开始就是45if (0 == total % count)last = total - count;// 假设总数是51,不能够被5整除的,那么最后一页的开始就是50elselast = total - total % count;last = last<0?0:last;return last;}/* getter and setter */
}
前台实现分页设计
首先我们在前台需要完成我们分页条的设计,这里可以直接引入 Bootstrap 来完成:
上面是使用 Bootstrap 实现一个分页条的简单例子,如果不熟悉的童鞋可以去菜鸟教程中查看:点这里
简单版本的分页条
为了便于理解,我们先来实现一个简单版本的分页条吧:
- 首页超链:指向了 start 为 0 的首页
<li><a href="?page.start=0"><span>«</span></a>
</li>
- 上一页超链:
<li ><a href="?page.start=${page.start-page.count}"><span>‹</span></a>
</li>
- 下一页超链:
<li ><a href="?page.start=${page.start+page.count}"><span>›</span></a>
</li>
- 最后一页超链:指向了最后一页
<li ><a href="?page.start=${page.last}"><span>»</span></a>
</li>
- 中间页:
<c:forEach begin="0" end="${page.totalPage-1}" varStatus="status"><li><a href="?page.start=${status.index*page.count}" class="current">${status.count}</a></li>
</c:forEach>
- 所以写完看起来会是这样子的:
<nav><ul class="pagination"><li><a href="?page.start=0"><span>«</span></a></li><li ><a href="?page.start=${page.start-page.count}"><span>‹</span></a></li><c:forEach begin="0" end="${page.totalPage-1}" varStatus="status"><li><a href="?page.start=${status.index*page.count}" class="current">${status.count}</a></li></c:forEach><li ><a href="?page.start=${page.start+page.count}"><span>›</span></a></li><li ><a href="?page.start=${page.last}"><span>»</span></a></li></ul>
</nav>
- 存在的问题:
① 没有边界判断,即在首页仍然可以点击前一页,不符合逻辑也影响用户体验
② 会显示完所有的分页,即如果 totalPage 有50页,那么分页栏将会显得特别长,影响体验
改良版本的分页条
1.写好头和尾
<nav class="pageDIV"><ul class="pagination">.....</ul>
</nav>
2.写好«
‹
这两个功能按钮
使用 <c:if>
标签来增加边界判断,如果没有前面的页码了则设置为disable状态
<li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>><a href="?page.start=0"><span>«</span></a></li><li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>><a href="?page.start=${page.start-page.count}"><span>‹</span></a></li>
再通过 JavaScrip 代码来完成禁用功能:
<script>$(function () {$("ul.pagination li.disabled a").click(function () {return false;});});
</script>
3.完成中间页码的编写
<c:forEach begin="0" end="${page.totalPage-1}" varStatus="status"><c:if test="${status.count*page.count-page.start<=30 && status.count*page.count-page.start>=-10}"><li <c:if test="${status.index*page.count==page.start}">class="disabled"</c:if>><ahref="?page.start=${status.index*page.count}"<c:if test="${status.index*page.count==page.start}">class="current"</c:if>>${status.count}</a></li></c:if>
</c:forEach>
从 0
循环到 page.totalPage - 1
,varStatus
相当于是循环变量
- status.count 是从1开始遍历
- status.index 是从0开始遍历
- 要求:显示当前页码的前两个和后两个就可,例如当前页码为3的时候,就显示 1 2 3(当前页) 4 5 的页码
- 理解测试条件:
-10 <= 当前页*每一页显示的数目 - 当前页开始的数据编号 <= 30 - 只要理解了这个判断条件,其他的就都好理解了
- 注意: 测试条件是需要根据项目的需求动态改变的,不是万能的!
后台中的分页
首页在项目中引入上面提到的 Page 工具类,然后我们在 DAO 类中使用 LIMIT 关键字来查询数据库中的信息:
public List<Student> list() {return list(0, Short.MAX_VALUE);
}public List<Student> list(int start, int count) {List<Student> students = new ArrayList<>();String sql = "SELECT * FROM student ORDER BY student_id desc limit ?,?";try (Connection c = DBUtil.getConnection(); PreparedStatement ps = c.prepareStatement(sql)) {ps.setInt(1, start);ps.setInt(2, count);// 获取结果集...} catch (SQLException e) {e.printStackTrace();}return students;
}
在 Servlet 中获取分页参数并使首页显示的 StudentList 用 page 的参数来获取:
// 获取分页参数
int start = 0;
int count = 10;try {start = Integer.parseInt(req.getParameter("page.start"));count = Integer.parseInt(req.getParameter("page.count"));
} catch (Exception e) {
}
Page page = new Page(start, count);List<Student> students = studentDAO.list(page.getStart(), page.getCount());....// 共享数据
req.setAttribute("page", page);
req.setAttribute("students", students);
以上即可完成分页功能,但这是基于 Servlet 的版本,在之前写过的项目(学生管理系统(简易版))中实际的使用了这种方法,感兴趣的可以去看一下。
SSM 中的分页
在 SSM 项目中,我们可以使用 MyBatis 的一款分页插件: PageHelper 来帮助我们更加简单的完成分页的需求,官网在这里: PageHelper
在这里,我们演示一下如何使用上面的工具重构我们之前写过的 SSM 项目 —— 学生管理系统-SSM 版
第一步:添加相关 jar 依赖包
PageHelper 需要依赖两个 jar 包,我们直接在 pom.xml 中增加两个 jar 包依赖:
<!-- pageHelper -->
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.1.2-beta</version>
</dependency><!--jsqlparser-->
<dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>1.0</version>
</dependency>
第二步:配置相关环境
在 MyBatis 的 SessionFactory 配置中新增加一个属性名 plugins 的配置:
<!-- 配置SqlSessionFactory对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 注入数据库连接池 --><property name="dataSource" ref="dataSource"/><!-- 扫描entity包 使用别名 --><property name="typeAliasesPackage" value="cn.wmyskxz.entity"/><!-- 扫描sql配置文件:mapper需要的xml文件 --><property name="mapperLocations" value="classpath:mapper/*.xml"/><!-- 让MyBatis支持PageHelper插件 --><property name="plugins"><array><bean class="com.github.pagehelper.PageInterceptor"><property name="properties"><!--使用下面的方式配置参数,一行配置一个 --><value></value></property></bean></array></property>
</bean>
第三步:重构项目
首先我们把 LIMIT 关键字从映射文件中干掉:
<!-- 查询从start位置开始的count条数据-->
<select id="list" resultMap="student">SELECT * FROM student ORDER BY student_id desc
</select>
然后注释掉查询数据总条数的 SQL 语句:
<!--<!– 查询数据条目 –>-->
<!--<select id="getTotal" resultType="int">--><!--SELECT COUNT(*) FROM student-->
<!--</select>-->
在 Dao 类和 Service 类中修改相应的地方:
然后修改掉 StudentController 中的方法:
@RequestMapping("/listStudent")
public String listStudent(HttpServletRequest request, HttpServletResponse response) {// 获取分页参数int start = 0;int count = 10;try {start = Integer.parseInt(request.getParameter("page.start"));count = Integer.parseInt(request.getParameter("page.count"));} catch (Exception e) {}Page page = new Page(start, count);// 使用 PageHelper 来设置分页PageHelper.offsetPage(page.getStart(),page.getCount());List<Student> students = studentService.list();// 使用 PageHelper 来获取总数int total = (int) new PageInfo<>(students).getTotal();page.setTotal(total);request.setAttribute("students", students);request.setAttribute("page", page);return "listStudent";
}
重启服务器,能看到也能够正确的使用分页功能。
总结
其实我自己对于这个工具比较无感..因为只是弱化了少一部分的功能,并没有我想象中的那样 “智能” ,也没有看到什么好的博文能够点通我的认知,希望了解的大大们能无私分享一下,谢谢!
欢迎转载,转载请注明出处!
简书ID:@我没有三颗心脏
作者:我没有三颗心脏
链接:https://www.jianshu.com/p/d108d0cd9acf
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Java Web -【分页功能】详解相关推荐
- java 8 新功能详解_Java 8和Java 14之间的新功能
java 8 新功能详解 从版本9开始,Java每6个月就有一次新功能,因此很难跟踪这些新更改. 互联网上的大多数信息都描述了最近2个Java版本之间的变化. 但是,如果您的情况与我相似,则说明您使用 ...
- java 8 新功能详解_Java 8的8个新功能
java 8 新功能详解 注意:确保还检查了我们的详细教程Java 8 Features – ULTIMATE Guide . Jdk 1.8(又名Java 8)今天发布,这意味着它的通用发布版本已经 ...
- Java web.xml 配置详解
在项目中总会遇到一些关于加载的优先级问题,近期也同样遇到过类似的,所以自己查找资料总结了下,下面有些是转载其他人的,毕竟人家写的不错,自己也就不重复造轮子了,只是略加点了自己的修饰. 首先可以肯定的是 ...
- 读《Tomcat与Java Web开发技术详解》
作 者: 孙卫琴, 李洪成 编著 出 版 社: 电子工业出版社 出版时间: 2004-4-1 字 数: 723200 版 次: 1 页 数: 438 印刷时间: 2004/04/01 开 本: 印 次 ...
- Java Web(五) JSP详解(四大作用域九大内置对象等)
前面讲解了Servlet,了解了Servlet的继承结构,生命周期等,并且在其中的ServletConfig和ServletContext对象有了一些比较详细的了解,但是我们会发现在Servlet中编 ...
- dede 模板 php sql,dedecms模板中使用{dede:sql=””}实现分页功能详解
相信很多使用织梦dedecms的朋友在网上查找关于dede:sql标签进行分页的解决方案时都不尽如人意,尤其是在列表页使用dede:sql调用外部数据(所谓调用外部数据就是指在后台只是创建个空栏目,然 ...
- Java Web开发技术详解~Web的概念
Web 是一种分布式应用架构,旨在共享分布在网络上的各个Web服务器中的所有互相链接的信息. Web 采用客户/服务器通信模式,客户与服务器之间用HTTP协议通信. Web 使用超级文本技术(HTML ...
- Tomcat与Java Web开发技术详解 总结(1)
1.tomcat组件架构图 2.startup和shutdown Startup.bat 执行的是catalina.bat文件.与命令catalina start 一样 Shutdown.bat执行的 ...
- 【Java Web】@WebServlet详解
Servlet3.0里引入了注解 注解@WebServlet用来定义Web应用程序中的一个Servlet. @WebServlet注解属性 asyncSupported:声明Servlet是否支持异步 ...
- Java Web开发技术详解~MIME类型
MIME(Multipurpose Internet Mail Extension)是指多用途网络邮件扩展协议,这里的邮件不单纯值E-Mail,还可以包括通过各种应用层协议在网络上传输的数据. 遵守M ...
最新文章
- 利用上下文常识,让AI读懂不完整人类指令
- 两张图看懂GDT、GDTR、LDT、LDTR的关系
- linux shell find depth,搞定 Linux Shell 文本处理工具,看完这篇集锦就够了
- Dataset之HiggsBoson:Higgs Boson(Kaggle竞赛)数据集的简介、下载、案例应用之详细攻略
- python 字典定义日志用法_python中字典(Dictionary)用法实例详解
- 使用回溯法解决编辑距离问题(C语言)
- session 的 源码
- 快速运行python虚拟环境_快速入门Python 最新最流行的pipenv虚拟环境
- c语言选修课程设计,C语言课程设计--学生选修课系统.doc
- C++_结构体的定义和使用_结构体数组---C++语言工作笔记025
- LaTeX中宋体中文无法加粗的情况
- scp实现mac与linux服务器之间文件传输
- Noise,Error,wighted pocket Algorithm
- D365 ERP流程笔记
- Ubuntu20.04 虚拟机 联网
- lisp填挖横断面提取_如何在别人提供的cad横断面设计图中提取横断面地面线数据...
- idea插件开发--组件--编程久坐提醒
- r语言quantmond_R中的关于极值理论的包
- Kubernetes网络自学系列 | 千呼万唤始出来:veth pair
- MSF给正常程序添加后门
热门文章
- redis的hash操作在集中式session中的应用
- 元宇宙iwemeta:元宇宙率先开百万年薪抢人
- Deep Learning的基础概念
- 2020年一季度中小微企业收入平均下降69.5%
- 谷歌深度学习四大教训:应用、系统、数据及原理(附数据集列表)
- Visual Studio 2013开发 mini-filter driver step by step 应用层与内核通讯(8)
- Redis进阶-Jedis以及Spring Boot操作 Redis 5.x Cluster
- 白话Elasticsearch68-ES生产集群部署重要的操作系统设置
- Oracle-锁解读
- Linux-find命令