前言
在web应用开发中,数据分页是个常见的功能,也是一个极其重要的功能,可以说随处可见。当然对于一个新手来说也是一个难点,下面是我在平常的练习结合一些视频教程,对分页模型的一个拙见。
提示:以下是本篇文章正文内容,下面案例可供参考
一、使用到的开发工具和技术
IDEA ,SQLServer2012/Mysql ,Tomcat,JSP。
二、MVC模式分页处理
1.Web层(应用层,Servlet层)
1)这里,我使用了反射机制,通过反射获取页面的参数,根据获取的参数,寻找对应的Servlet类的相应处理方法。 代码如下(示例):
public abstract class BaseServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String action=req.getParameter("action");
try {
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
2)编写对应的处理类,该类继承了上面的BaseServlet,目的就是一个具有相同类型的Servlet,可以使用不同的方法处理不同的业务,进而不用多次写不同的Servlet。(例如:图书的功能:添加图书,修改图书,删除图书… 此时我们至需要写一个BookServlet,在BookServlet编写添加,修改,删除的方法,而怎样确定一个请求是修改图书找到对应的修改方法而不是其他方法呢?这就要用到反射机制,要求我们的方法名必须与请求过来传递的标识参数名称一致。如这儿的page方法,就对应于 String action=req.getParameter(“action”);) 代码如下(示例):
public class OrderTableServlet extends BaseServlet {
private OrderTableService orderTableService= new OrderTableServiceImpl();
protected void page(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int pageNo= WebUtils.parseInt(request.getParameter("pageNo"), 1);
int pageSize= WebUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);
Page<OrderTable> page= orderTableService.page(pageNo,pageSize);
page.setUrl("/orderTableServlet.do?action=page");
request.setAttribute("page",page);
request.getRequestDispatcher("/back/admin/ordertable_list.jsp").forward(request,response);
}
}
2.Service层(业务层)
1)这里是分页模型的重点和难点,说它难其实也不至于,无非就是一个公式的计算。 以上图片借鉴与尚硅谷王振国老师的课堂笔记。分析可知,难点在于获取总的页码数和每页应该显示的内容。这里需要注意的是:总记录数/每页显示的数量 如果不能整除,则页数要多加1页。即:总记录数%每页数量>0 ,总页数=总记录数/每页数量 + 1,如果总记录数%每页数量 ==0(刚好够均分,总页数=总记录数/每页数量 。 而每页的开始页码是多少,这又是一个困惑的人,下面给出公式:每页开始显示的记录 = (当前页码 - 1) * 每页显示的数量;
分页的业务层代码如下(示例):
public Page<OrderTable> page(int pageNo, int pageSize) {
Page<OrderTable> page = new Page<OrderTable>();
page.setPageSize(pageSize);
Integer pageTotalCount = orderTableDao.queryForPageTotalCount();
page.setPageTotalCount(pageTotalCount);
Integer pageTotal = pageTotalCount / pageSize;
if (pageTotalCount % pageSize > 0) {
pageTotal+=1;
}
page.setPageTotal(pageTotal);
page.setPageNo(pageNo);
int begin = (page.getPageNo() - 1) * pageSize;
List<OrderTable> items = orderTableDao.queryForPageItems(begin,pageSize);
page.setItems(items);
return page;
}
3.Dao层(持久层)
需要指出的是:在SQLServer 和 Mysql 的分页查询他们的语法有点区别;常见的是: 1)SQLServer的分页:
set statistics time on;
-- 分页查询(通用型)
select top pageSize *
from (select row_number()
over(order by sno asc) as rownumber,*
from student) temp_row
where rownumber>((pageIndex-1)*pageSize);
set statistics time on;
-- 分页查询第2页,每页有10条记录
select top 10 *
from (select row_number()
over(order by sno asc) as rownumber,*
from student) temp_row
where rownumber>10;
2)Mysql 的分页: 代码如下(示例):
select * from 表名 order by 属性 desclimit m,n;
select * from dept order by deptno desc limit 3,3;
这里引用别人关于两种分页的多种方法【SQL server分页的四种方法和Mysql数据库分页查询讨论专题】,感兴趣的可以看看:
https://blog.csdn.net/weixin_37610397/article/details/80892426 https://blog.csdn.net/bandaoyu/article/details/89844673
代码如下(示例):这里我封装了数据库,用的是阿里巴巴的【commons-dbutils-1.6.jar】这个包。
public abstract class BaseDao {
private QueryRunner queryRunner = new QueryRunner();
public int update(String sql, Object... args) {
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.update(connection, sql, args);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection);
}
return -1;
}
public <T> T queryForOne(Class<T> type, String sql, Object... args) {
Connection con = JdbcUtils.getConnection();
try {
return queryRunner.query(con, sql, new BeanHandler<T>(type), args);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(con);
}
return null;
}
public <T> List<T> queryForList(Class<T> type, String sql, Object... args) {
Connection con = JdbcUtils.getConnection();
try {
return queryRunner.query(con, sql, new BeanListHandler<T>(type), args);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(con);
}
return null;
}
public Object queryForSingleValue(String sql, Object... args){
Connection conn = JdbcUtils.getConnection();
try {
return queryRunner.query(conn, sql, new ScalarHandler(), args);
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.close(conn);
}
return null;
}
}
public class OrderTableDaoImpl extends BaseDao implements OrderTableDao {
@Override
public Integer queryForPageTotalCount() {
String sql="select count(*) from ordertable";
Number count=(Number)queryForSingleValue(sql);
return count.intValue();
}
@Override
public List<OrderTable> queryForPageItems(int begin, int pageSize) {
String sql="select top 4 orderid,number,kid,chanchenpengid,orderdate \n" +
"from (select row_number() \n" +
"over(order by orderid asc) as rownumber,* \n" +
"from ordertable) temp_row\n" +
"where rownumber>?";
String sql2="select top "+pageSize+"orderid,number,kid,chanchenpengid,orderdate \n" +
"from (select row_number() \n" +
"over(order by orderid asc) as rownumber,* \n" +
"from ordertable) temp_row\n" +
"where rownumber>"+begin;
String sql3="select orderid,number,kid,chanchenpengid,orderdate " +
"from ordertable where kid >"+begin;
return queryForList(OrderTable.class,sql2);
}
}
4.数据展示
通过Web层的 request.setAttribute(“page”,page);我们将查询出来的分页对象存放在Request域中,在前端就可以通过 ${requestScope.page.属性l} 【eg: ${requestScope.page.pageTotal} 】 获取对应的属性。 代码如下(示例):
<table class="tablelist" style="margin-top: 36px">
<thead>
<tr>
<th style="text-align: center"><input type="checkbox" value="" name="selectAll"
class="selectAllOrdertable"></th>
<th style="text-align: center">订单id</th>
<th style="text-align: center">数量</th>
<th style="text-align: center">客户id</th>
<th style="text-align: center">产品代码</th>
<th style="text-align: center">产品单位</th>
<th style="text-align: center">下单时间</th>
<th style="text-align: center">操作</th>
</tr>
</thead>
<tbody>
<c:forEach items="${requestScope.page.items}" var="ordertable" varStatus="i">
<tr>
<td><input type="checkbox" id="checke" value="${ordertable.orderid }"></td>
<td class="td-manage">${ordertable.orderid}</td>
<td class="center">${ordertable.number}</td>
<td class="center">${ordertable.kid}</td>
<td class="center">${ordertable.chanchenpengid}</td>
<td class="center">${ordertable.chanpindanwei}</td>
<td class="center">
<fmt:formatDate value="${ordertable.orderdate }" pattern="yyyy-MM-dd HH:mm:ss" /></td>
<td class="td-manage"><a title="${ordertable.orderid}" href="#"
class="updateclass" style="text-decoration: none"> <i
class="layui-icon">编辑</i>
</a> <%--<c:if test="${ordertable.orderid != 1 &&ordertable.orderid != 2}">--%>
<a title="${ordertable.orderid}" href="#" class="delclass"
style="text-decoration: none"> <i class="layui-icon">删除</i>
</a>
<%--</c:if>--%></td>
</tr>
</c:forEach>
</tbody>
</table>
<div>
<nav aria-label="Page navigation">
<ul class="pagination" >
<div width="100%" height:="10px" margin="auto" text-align="center">
<c:if test="${requestScope.page.pageNo>1}">
<a href="${pageContext.request.contextPath}/orderTableServlet.do?action=page&pageNo=1">首页</a>
<a href="${pageContext.request.contextPath}/orderTableServlet.do?action=page&pageNo=${requestScope.page.pageNo-1}">上一页</a>
</c:if>
<%--页码输出的开始--%>
<c:choose>
<%--情况1:如果总页码小于等于5的情况,页码的范围是:1-总页码--%>
<c:when test="${ requestScope.page.pageTotal <= 5 }">
<c:set var="begin" value="1"/>
<c:set var="end" value="${requestScope.page.pageTotal}"/>
</c:when>
<%--情况2:总页码大于5的情况--%>
<c:when test="${requestScope.page.pageTotal > 5}">
<c:choose>
<%--小情况1:当前页码为前面3个:1,2,3的情况,页码范围是:1-5.--%>
<c:when test="${requestScope.page.pageNo <= 3}">
<c:set var="begin" value="1"/>
<c:set var="end" value="5"/>
</c:when>
<%--小情况2:当前页码为最后3个,8,9,10,页码范围是:总页码减4 - 总页码--%>
<c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}">
<c:set var="begin" value="${requestScope.page.pageTotal-4}"/>
<c:set var="end" value="${requestScope.page.pageTotal}"/>
</c:when>
<%--小情况3:4,5,6,7,页码范围是:当前页码减2 - 当前页码加2--%>
<c:otherwise>
<c:set var="begin" value="${requestScope.page.pageNo-2}"/>
<c:set var="end" value="${requestScope.page.pageNo+2}"/>
</c:otherwise>
</c:choose>
</c:when>
</c:choose>
<c:forEach begin="${begin}" end="${end}" var="i">
<c:if test="${i == requestScope.page.pageNo}">
【${i}】
</c:if>
<c:if test="${i != requestScope.page.pageNo}">
<a href="${pageContext.request.contextPath}/orderTableServlet.do?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
<%--页码输出的结束--%>
<c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}">
<a href="${pageContext.request.contextPath}/orderTableServlet.do?action=page&pageNo=${requestScope.page.pageNo+1}">下一页</a>
<a href="${pageContext.request.contextPath}/orderTableServlet.do?action=page&pageNo=${requestScope.page.pageTotal}">末页</a>
</c:if>
   共 ${requestScope.page.pageTotal} 页,${requestScope.page.pageTotalCount} 条记录  跳转到 <input style="width:25px; height:12px" value="${param.pageNo}" name="pn" id="pn_input"/>页
<input id="searchPageBtn" type="button" value="确定">
<script type="text/javascript">
$("#searchPageBtn").click(function () {
var pageNo = $("#pn_input").val();
location.href = "${pageScope.basePath}${ requestScope.page.url }&pageNo=" + pageNo;<%--${ requestScope.page.url }--%>
});
</script>
</div>
</li>
</ul>
</nav>
</div>
分页数据的展示代码臃肿,如果在要用的地方都这样写的话,一是麻烦重复,二是代码不简洁。因此我们抽取出来分页的显示代码,要在使用分页功能的地方,通过静态包含的方式引入。 例如:
<%--静态包含页脚内容--%>
<%@include file="/pages/common/page_nav.jsp"%>
三、总结
分页功能是个很常见的模块,对于写业务程序员想必是必须会分页的 对于平常很少练习的学生来说,独立的编写程序,的确有点困难。但大多数学生都是跟着视频敲代码,又缺乏思考,盲目的做个机器,当然我也是这样的。因此,要想真正的掌握并且培养独立思考的能力,就要有目的的练习和勤于思考的习惯。
|