一 分页概述
1 介绍
将大量数据分段显示,避免一次性加载造成的内存溢出风险
2 真假分页
① 真分页
一次性查询出所有数据存到内存,翻页从内存中获取数据,性能高但易造成内存溢出
② 假分页
每次翻页从数据库中查询数据,性能低,但不易造成内存溢出
二 分页设计
1 分页传递参数 – 需用户传入的参数
currentPage:当前页,跳转到第几页,int类型,设置默认值,比如1
pageSize:每页最多多少条数据,int类型,设置默认值,比如 5
2 分页展示数据 – 分页依赖数据(分页条)
beginPage:首页
prevPage:上一页
nextPage:下一页
totalPage:总页数/末页
totalCount/rows:总条数
currentPage:当前页
pageSize:每页显示多少条数据
3 分页展示数据的来源
① 来源于用户传入:
currentPage:当前页,int类型
pageSize:每页显示多少条数据,int类型
② 来源于两条SQL查询:
totalCount/rows:数据总条数,int类型
data/list:每一页的结果集数据,List类型
③ 来源于程序计算:
totalPage:总页数/末页,int类型
prevPage:上一页,int类型
nextPage:下一页,int类型
4 结果总条数与结果集
结果总数(totalCount/rows)和结果集(data/list)来源于两条SQL:
// 查询符合条件的结果总数(totalCount/rows)
SELECT COUNT(*)FROM 表名[WHERE 条件]
// 第一个?:从哪一个索引的数据开始查询(默认从0开始)
// 第二个?:查询多少条数据
SELECT*FROM 表名[WHERE 条件]LIMIT ?,?
第二条SQL中的两个参数,第一个取值为(currentPage-1)* pageSize,第二个取值为pageSize,都来源于用户传递的参数
5 总页数与上下翻页
总页数与上下翻页,来自程序计算
// 优先计算 totalPage
int totalPage = rows % pagesize == 0 ? rows / pagesize : rows / pagesize +1;
int prevpage = currentPage -1>=1 ? currentPage - 1:1;
int nextpage = currentPage +1 <= totalpage ? currentPage + 1: totalpage;
三 分页实现(后台)
1 流程
2 数据封装
为实现流程效果,需将查询回的分页数据封装成一个对象传递给JSP,这样只需要共享一个对象即可
3 封装对象 – PageResult
@Setter
@Getter
public class PageResult<T> {
//上一页
private Integer prevPage;
//下一页
private Integer nextPage;
//总条数
private Integer totalCount;
//总页数
private Integer totalPage;
//当前页码
private Integer currentPage;
//每页显示条数
private Integer pageSize;
//查询的集合
private List<T> list;
/*初始化分页对象有参构造*/
public PageResult(Integer currentPage,Integer pageSize,Integer totalCount,List<T> list){
this.currentPage=currentPage;
this.pageSize=pageSize;
this.totalCount=totalCount;
this.list=list;
//总页数=总条数/每页显示条数 当求模运算值不等于0的时候总页数加1
this.totalPage=(this.totalCount%this.pageSize==0)?(this.totalCount/this.pageSize):(this.totalCount/this.pageSize+1);
//上一页
this.prevPage=(this.currentPage==1)?(1):(this.currentPage-1);
//下一页
this.nextPage=(this.currentPage==this.totalPage)?(this.totalPage):(this.currentPage+1);
}
}
4 Dao层实现
为实现分页Dao层需增加两个方法,一个是查询数据总量,一个查询当前页的数据
// 数据总量
int queryCount(int currentPage,int pageSize);
// 结果集
List<Employee> queryPage(int currentPage,int pageSize);
MyBatis提供的操作方法传入执行SQL的参数只能是1个,此时两个参数需要传递两个分页实参,可以使用Map封装参数,或者使用JavaBean封装参数
5 封装分页参数 – QueryObject
@Setter
@Getter
public class QueryObject {
private Integer currentPage=1;
private Integer pageSize=3;
public int getStart(){
return (currentPage-1)*pageSize;//计算查询的起始索引
}
}
6 编辑对应的Dao接口
// EmployeeDao
/*查询总条数*/
int queryCount(QueryObject qo);
/*分页查询*/
List<Employee> queryPage(QueryObject qo);
7 编辑Dao的实现类
@Override
public int queryCount(QueryObject qo) {
return sqlSession.selectOne("cn.tj.mapper.EmployeeMapper.queryCount",qo);
}
@Override
public List<Employee> queryPage(QueryObject qo) {
return sqlSession.selectList("cn.tj.mapper.EmployeeMapper.queryPage",qo);
}
8 编辑对应Mapper文件
<!--查询总条数-->
<select id="queryCount" resultType="int">
SELECT count(*) from employee
</select>
<!--分页查询-->
<select id="queryPage" resultType="Employee">
SELECT * from employee limit #{start},#{pageSize}
</select>
9 业务层实现
Servlet调用时返回分页查询的结果,返回PageResult对象,形参类型为QueryObject
// service层接口
/*分页查询*/
PageResult<Employee> queryByPage(QueryObject qo);
// service层实现类
/*分页查询*/
@Override
public PageResult<Employee> queryByPage(QueryObject qo) {
//查询总条数
int totalCount = employeeDao.queryCount(qo);
if(totalCount==0){
return new PageResult<>(qo.getCurrentPage(),qo.getPageSize(),
0, Collections.emptyList());
}
//查询分页集合
List<Employee> list = employeeDao.queryPage(qo);
//创建分页对象
PageResult<Employee> pageResult=new PageResult<>(qo.getCurrentPage(),qo.getPageSize(),
totalCount,list);
return pageResult;
}
四 分页实现(前台)
1 流程
前台主要指Servlet及JSP,Servlet处理请求,调用业务方法,把查询到数据共享到JSP中,展示给用户看。
浏览器发出分页请求参数(去往第几页/每页多少条数据),在Servlet中接收这些参数,并封装到QueryObject对象,调用Service中分页查询方法(query).
把得到的分页查询结果对象(PageResult)共享在请求作用域中,跳转到JSP,显示即可。
修改JSP页面,编写出分页条信息(分页条中的信息来源于PageResult 对象)。
2 EmployeeServlet
// 获取页面传递的分页参数,执行查询,将结果共享到请求作用域,请求转发回到list.jsp页面
@WebServlet("/employee")
public class EmployeeServlet extends HttpServlet {
private EmployeeService employeeService=new EmployeeServiceImpl();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码格式
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
// 登录权限判断 操作员工需要先登录
HttpSession session = req.getSession();
Users users = (Users) session.getAttribute("USER_IN_SESSION");
if (users==null){//如果没有登录,跳转登录页面,给出提示
req.setAttribute("msg","请先登录再操作!");
req.getRequestDispatcher("/login.jsp").forward(req,resp);
return;
}
//获取请求分发的参数
String cmd = req.getParameter("cmd");
if ("delete".equals(cmd)){
//调用删除
delete(req,resp);
}else if("input".equals(cmd)){
//跳转到增加或修改页面
input(req,resp);
} else if("saveOrUpdate".equals(cmd)){
//保存增加或修改
saveOrUpdate(req,resp);
}else{
//查询所有
list(req,resp);
}
}
/*增加修改保存*/
private void saveOrUpdate(HttpServletRequest req, HttpServletResponse resp) {
//封装员工对象
Employee employee=new Employee();
/*将参数的获取及封装对象过程抽取成一个方法*/
req2employee(req,employee);
/*根据请求中是否携带id判断执行增加或者修改操作*/
String id = req.getParameter("id");
if (StringUtil.hasLength(id)){
employee.setId(Long.valueOf(id));
//执行修改
employeeService.update(employee);
}else{
//调用dao方法执行增加保存操作
employeeService.add(employee);
}
try {
//跳转到查询
resp.sendRedirect("/employee");
} catch (IOException e) {
e.printStackTrace();
}
}
/*封装的获取请求参数的方法*/
private void req2employee(HttpServletRequest req, Employee employee) {
//获取请求中表单参数
String name = req.getParameter("name");
//验证输入的表单参数不能为空
if (StringUtil.hasLength(name)){
//编辑员工姓名
employee.setName(name);
}
String salary = req.getParameter("salary");
if (StringUtil.hasLength(salary)){
employee.setSalary(Double.valueOf(salary));
}
}
/*跳转到编辑页面*/
private void input(HttpServletRequest req, HttpServletResponse resp) {
//根据请求中是否携带id判断是增加或者修改操作
String id = req.getParameter("id");
if (StringUtil.hasLength(id)){//请求中携带了id执行修改
//根据id查询员工
Employee employee = employeeService.selectOne(Long.valueOf(id));
//将查询的员工存储到作用域
req.setAttribute("employee",employee);
}
try {
//跳转到WEB-INF下面的页面:input.jsp
req.getRequestDispatcher("/WEB-INF/views/employee/input.jsp").forward(req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
/*分页查询所有*/
private void list(HttpServletRequest req, HttpServletResponse resp) {
try {
/*客户端如果传入了当前页码和每页显示条数则设置给qo对象,如果没有传入则使用默认值*/
String currentPage = req.getParameter("currentPage");
String pageSize = req.getParameter("pageSize");
QueryObject qo=new QueryObject();
if (StringUtil.hasLength(currentPage)){
qo.setCurrentPage(Integer.valueOf(currentPage));
}
if (StringUtil.hasLength(pageSize)){
qo.setPageSize(Integer.valueOf(pageSize));
}
//调用service分页查询的方法
PageResult<Employee> pageResult = employeeService.queryByPage(qo);
//将查询的结果存储到请求作用域
req.setAttribute("pageResult",pageResult);
//转发到列表页面
req.getRequestDispatcher("/WEB-INF/views/employee/list.jsp").forward(req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
/*删除*/
private void delete(HttpServletRequest req, HttpServletResponse resp) {
try {
//获取目标id
String id = req.getParameter("id");
//调用删除方法
int row = employeeService.delete(Long.valueOf(id));
//删除完毕跳转到查询
resp.sendRedirect("/employee");
} catch (IOException e) {
e.printStackTrace();
}
}
}
3 list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入核心标签库--%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>员工信息页面</title>
</head>
<body>
<center>
<h3>员工列表</h3>
<p>当前登录账号:${sessionScope.USER_IN_SESSION.username}</p>
<p>
<%--点击跳转到上传头像的表单页面--%>
<a href="/headImg.jsp">
<%--头像--%>
<img src="/upload/${sessionScope.USER_IN_SESSION.headImg}" width="100px" height="100px">
<p>${sessionScope.USER_IN_SESSION.headImg}</p>
</a>
</p>
<p><a href="/employee?cmd=input">增加员工</a></p>
<p><a href="/user?cmd=logout">退出登录</a></p>
<%--分页跳转的表单--%>
<form action="/employee" method="post">
<table border="1px" cellpadding="0" cellspacing="0"width="800px">
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>工资</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%--动态展示员工列表数据--%>
<c:forEach items="${pageResult.list}" varStatus="vs" var="employee">
<tr class="trClass">
<td>${vs.count}</td>
<td>${employee.name}</td>
<td>${employee.salary}</td>
<td>
<a href="/employee?cmd=input&id=${employee.id}">修改员工信息</a>
<%-- <a href="/employee?cmd=delete&id=${employee.id}">删除员工信息</a>--%>
<a href="#" onclick="deleteTr(${employee.id})">删除员工信息</a>
</td>
</tr>
</c:forEach>
</tbody>
<tfoot>
<tr align="center">
<td colspan="9">
<a href="/employee?currentPage=1&pageSize=${pageResult.pageSize}">首页</a>
<a href="/employee?currentPage=${pageResult.prevPage}&pageSize=${pageResult.pageSize}">上一页</a>
<a href="/employee?currentPage=${pageResult.nextPage}&pageSize=${pageResult.pageSize}">下一页</a>
<a href="/employee?currentPage=${pageResult.totalPage}&pageSize=${pageResult.pageSize}">末页</a>
当前是第 ${pageResult.currentPage}/${pageResult.totalPage}页
一共${pageResult.totalCount}条数据
跳转到第
<input type="text" name="currentPage" value="${pageResult.currentPage}"
onchange="changePagesize()" style="width: 50px">
页
每页
<select name="pageSize" onchange="changePagesize()">
<option value="3" ${pageResult.pageSize==3?'selected':''}>3</option>
<option value="6" ${pageResult.pageSize==6?'selected':''}>6</option>
<option value="9" ${pageResult.pageSize==9?'selected':''}>9</option>
</select>
条数据
</td>
</tr>
</tfoot>
</table>
</form>
</center>
<%--鼠标移动到当前行,实现背景颜色高亮显示--%>
<script>
//获取所有的行元素:不包含表头
var trs = document.getElementsByClassName("trClass");
//遍历行元素集合
for(var i=0;i<trs.length;i++){
//鼠标移入:当前行高亮,背景变成灰色
trs[i].onmouseover=function (){
this.style.backgroundColor="gray";
}
//鼠标移出:恢复原来背景颜色
trs[i].onmouseout=function (){
this.style.backgroundColor="";
}
}
</script>
<%--删除确认表--%>
<script>
function deleteTr(id){
//确认删除
var b= window.confirm("确定要删除选中的员工信息吗?");
if(b){//确定删除,执行后台删除操作
window.location="/employee?cmd=delete&id="+id;
}
}
</script>
<%--分页表单提交函数--%>
<script>
function changePagesize(){
document.forms[0].submit();
}
</script>
</body>
</html>