分页查询
分页查询将数据库中庞大的数据分段显示,每页显示用户自定义的行数,提高用户体验度,最主要的是如果一次性从服务器磁盘中读出全部数据到内存,有内存溢出的风险
真假分页
假分页: 其原理还是将所有的数据读到内存中,翻页从内存中读取数据, 优点: 实现简单,性能高 缺点:如果数据大容易造成内存溢出
真分页: 每次翻页从数据库查询数据(即磁盘) , 优点 : 不容易造成内存溢出 缺点: 实现复杂,性能相对低一些
分页效果
一般分页的功能包括: 首页 上一页 下一页 末页 当前是多少页 总共多少页 一共多少行数据 跳转到第几页 每页多少条数据 我们需要将这个数据查询出来封装到一个对象中,体现了封装思想,也节省了很多复杂代码
分页需要传递的参数
需要用户传入的参数:
currentpage: 当前页,跳转到第几页, 第一次访问,我们创建一个对象,默认值是1
pagesize: 每页显示多少行数据, 第一次我们也给个默认值 比如10条
分页需要展示的数据
1.当前页的货品信息
2.首页是第几页
3.上一页是第几页
4.下一页是第几页
5.一共有多少页,和末页的值是一样的
6.数据一共有多少条(行)
7.当前是第几页
8.每页显示多少条信息
分页需要展示的数据的来源
来源于用户上传: 当前页 , 每页显示多少条数据
来源于数据库查询 : 数据总条数 , 每一页需要展示的商品信息
来源于根据上面的已知信息计算 : 总页数 , 上一页 , 下一页
书写从数据库查询的sql语句
第一条sql 查询数据库中有多少条数据,count后面不能有空格
select count(*) from 表名
第二条sql 根据传入的参数查询第几页,一页多少条数据的结果集
# 第一个 ?:从哪一个索引的数据开始查询(默认从 0 开始) # 第二个 ?:查询多少条数据 select * from 表名 limit ?, ?
接下来分析第二条 sql 中两个 ? 取值来源:
假设 product 表中有 21 条数据,每页分 5 条数据:
查询第一页数据:select * from product limit 0, 5
查询第二页数据:select * from product limit 5, 5
查询第三页数据:select * from product limit 10, 5
查询第四页数据:select * from product limit 15, 5
通过寻找规律发现:第一个 ? 取值来源于 (currentpage - 1) * pagesize;第二个 ? 取值来源于
pagesize,即都来源于用户传递的分页参数。
总页数,上一页和下一页
// 优先计算总页数 int totalpage = rows % pagesize == 0 ? rows / pagesize : rows / pagesize + 1; //上一页等于当前页-1,但不能超过1页界限 int prevpage = currentpage - 1 >= 1 ? currentpage - 1 : 1; //下一页等于当前页+1,但不能超过总页数界限 int nextpage = currentpage + 1 <= totalpage ? currentpage + 1 : totalpage;
分页查询实现
访问流程:
封装需要展示的数据
如果不封装数据,每个数据都需要存到作用域中,数据太分散,不方便统一管理
/** * 封装结果数据(某一页的数据) */ @getter public class pageresult<t> { // 两个用户的输入 private int currentpage; // 当前页码 private int pagesize; // 每页显示的条数 // 两条 sql 语句执行的结果 private int totalcount; // 总条数 private list<t> data; // 当前页结果集数据 // 三个程序计算的数据 private int prevpage; // 上一页页码 private int nextpage; // 下一页页码 private int totalpage; // 总页数/末页页码 // 分页数据通过下面构造期封装好 public pageresult(int currentpage, int pagesize, int totalcount, list<t> data) { this.currentpage = currentpage; this.pagesize = pagesize; this.totalcount = totalcount; this.data = data; // 计算三个数据 this.totalpage = totalcount % pagesize == 0 ? totalcount / pagesize : totalcount / pagesize + 1; this.prevpage = currentpage - 1 >= 1 ? currentpage - 1 : 1; this.nextpage = currentpage + 1 <= this.totalpage ? currentpage + 1 : this.totalpage; } }
持久层dao
mybatis提供的操作方法只能传入一个参数执行sql任务,而我们现在查询某一页的数据,需要知道是第几页和每页多少条数据这两个参数,所以我们需要将这两个参数封装在一个对象里面
编写一个类(起名叫查询对象类)来封装这些查询数据
@setter @getter /** * 封装分页查询需要的两个请求传入的分页参数 */ public class queryobject { private int currentpage = 1; // 当前页码,要跳转到哪一页的页码(需要给默认值) private int pagesize = 3; // 每页显示条数(需要给默认值) }
再书写持久层dao接口和实现类
//dao接口提供两个根据查询对象的查询方法 int queryforcount(); list<product> queryforlist(queryobject qo); //dao实现类 @override //查询数据库总数据条数 public int queryforcount() { sqlsession session = mybatisutil.getsession(); int totalcount = session.selectone("cn.xxx.mapper.productmapper.queryforcount"); session.close(); return totalcount; } @override //查询某一页的结果集 public list<product> queryforlist(queryobject qo) { sqlsession session = mybatisutil.getsession(); list<product> products = session.selectlist("cn.xxx.mapper.productmapper.queryforlist",qo); session.close(); return products; }
修改productmapper.xml
<select id="queryforcount" resulttype="int"> select count(*) from product </select> <select id="queryforlist" resulttype="cn.xxx.domain.product"> select * from product limit #{start}, #{pagesize} </select>
修改queryobject.java
给这个类增加getstart方法,返回根据当前页,每页大小需要从数据库从第几行开始显示
@setter @getter /** * 封装分页查询需要的两个请求传入的分页参数 */ public class queryobject { private int currentpage = 1; // 当前页码,要跳转到哪一页的页码(需要给默认值) private int pagesize = 3; // 每页显示条数(需要给默认值) // 用于 limit 子句第一个 ? 取值 public int getstart(){ return (currentpage - 1) * pagesize; } }
业务层productservice
调用持久层dao完成数据的查询,并将多个数据封装到一个对象中
//iproductservice接口 public interface iproductservice { /** * 完成查询某一页的业务逻辑功能 */ pageresult<product> query(queryobject qo); } //service实现类 public class productserviceimpl implements iproductservice { private iproductdao productdao = new productdaoimpl(); @override public pageresult<product> query(queryobject qo) { // 调用 dao 查询数据数量 int totalcount = productdao.queryforcount(); // 为了性能加入判断,若查询的数据数量为 0,说明没有数据,返回返回空集合,即集合中没有 元素 if(totalcount == 0){ return new pageresult(qo.getcurrentpage(), qo.getpagesize(), totalcount, collections.emptylist()); } // 执行到这里代表有数据,查询当前页的结果数据 list<product> products = productdao.queryforlist(qo); return new pageresult(qo.getcurrentpage(), qo.getpagesize(), totalcount, products); } }
前台分页功能实现
1.必须先完成业务层组件,保证后台测试通过。
2.遵循 mvc 思想。
3.浏览器发出分页请求参数(去往第几页/每页多少条数据),在 servlet 中接收这些参数,并封装
4.到 queryobject 对象,调用 service 中分页查询方法(query)。
5.把得到的分页查询结果对象(pageresult)共享在请求作用域中,跳转到 jsp,显示即可。
6.修改 jsp 页面,编写出分页条信息(分页条中的信息来源于 pageresult 对象)。
修改productservlet.java和展示jsp
1.获取页面请求参数,判断是查询操作,调用查询方法,获取分页参数
2.将参数封装成查询对象queryobject
3.调用业务层方法查询某一页数据
4.将查询出来的结果存到作用域中
5.转发到展示页面jsp
6.在jsp从作用域中将结果取出来响应到浏览器
//创建业务层对象 private iproductservice productservice = new productserviceimpl(); protected void list(httpservletrequest req, httpservletresponse resp) throws servletexception, ioexception { queryobject qo = new queryobject(); // 获取请求参数 currentpage,并转型封装 string currentpage = req.getparameter("currentpage"); if(stringutil.haslength(currentpage)) { qo.setcurrentpage(integer.valueof(currentpage)); } // 获取请求参数 pagesize,并转型封装 string pagesize = req.getparameter("pagesize"); if(stringutil.haslength(pagesize)) { qo.setpagesize(integer.valueof(pagesize)); } // 调用业务层方法来处理请求查询某一页数据 pageresult<product> pageresult = productservice.query(qo); // 把数据共享给 list.jsp req.setattribute("pageresult", pageresult); // 控制跳转到 list.jsp 页面 req.getrequestdispatcher("/web-inf/views/product/list.jsp").forward(req, resp); }
修改jsp文件,使用jstl+el获取作用域中的数据
<%@ page contenttype="text/html;charset=utf-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>产品列表</title> <script type="text/javascript"> window.onload = function () { var trclzs = document.getelementsbyclassname("trclassname"); for(var i = 0; i < trclzs.length; i++){ trclzs[i].onmouseover = function () { console.log(1); this.style.backgroundcolor = "gray"; } trclzs[i].onmouseout = function () { console.log(2); this.style.backgroundcolor = ""; } } } // 分页 js function changepagesize() { document.forms[0].submit(); } </script> </head> <body> <a href="/replaceimg.jsp" rel="external nofollow" ><img src="${user_in_session.headimg}" title="更换头 像"/></a><br/> <a href="/product?cmd=input" rel="external nofollow" >添加</a> <form action="/product"> <table border="1" cellspacing="0" cellpadding="0" width="80%"> <tr> <th>编号</th> <th>货品名</th> <th>分类编号</th> <th>零售价</th> <th>供应商</th> <th>品牌</th> <th>折扣</th> <th>进货价</th> <th>操作</th> </tr> <c:foreach var="product" items="${pageresult.data}" varstatus="status"> <tr class="trclassname"> <td>${status.count}</td> <td>${product.productname}</td> <td>${product.dir_id}</td> <td>${product.saleprice}</td> <td>${product.supplier}</td> <td>${product.brand}</td> <td>${product.cutoff}</td> <td>${product.costprice}</td> <td> <a href="/product?cmd=delete&id=${product.id}" rel="external nofollow" >删除</a> <a href="/product?cmd=input&id=${product.id}" rel="external nofollow" >修改</a> </td> </tr> </c:foreach> <tr align="center"> <td colspan="9"> <a href="/product?currentpage=1" rel="external nofollow" >首页</a> <a href="/product?currentpage=${pageresult.prevpage}" rel="external nofollow" >上一页</a> <a href="/product?currentpage=${pageresult.nextpage}" rel="external nofollow" >下一页</a> <a href="/product?currentpage=${pageresult.totalpage}" rel="external nofollow" >尾页</a> 当前第 ${pageresult.currentpage} / ${pageresult.totalpage} 页 一共 ${pageresult.totalcount} 条数据 跳转到<input type="number" onchange="changepagesize()" name="currentpage" value="${pageresult.currentpage}" style="width: 60px;"页 每页显示 <select name="pagesize" onchange="changepagesize()"> <option value="3" ${pageresult.pagesize == 3 ? 'selected' : ''}> 3 </option> <option value="5" ${pageresult.pagesize == 5 ? 'selected' : ''}> 5 </option> <option value="8" ${pageresult.pagesize == 8 ? 'selected' : ''}> 8 </option> </select>条数据 </td> </tr> </table> </form> </body> </html>
常见问题
若开始翻页操作成功,翻了几页不能翻了,只能重启tomcat才能翻,问题: dao中没有关闭sqlsession对象
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。