文章目录
- 前言
- 一、使用到的开发工具和技术
- 二、MVC模式分页处理
- 1.Web层(应用层,Servlet层)
- 2.Service层(业务层)
- 3.Dao层(持久层)
- 4.数据展示
- 三、总结
前言
在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 {
// 解决post请求中文乱码问题
// 一定要在获取请求参数之前调用才有效
req.setCharacterEncoding("UTF-8");
String action=req.getParameter("action");
try {
// 获取action业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
// System.out.println(method);
// 调用目标业务 方法
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();
/**
* 处理分页
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void page(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、获取请求的参数pageNo 和pageSize
int pageNo= WebUtils.parseInt(request.getParameter("pageNo"), 1);
int pageSize= WebUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);
//2、OrderTableServlet.page(pageNo,pageSize)
Page<OrderTable> page= orderTableService.page(pageNo,pageSize);
page.setUrl("/orderTableServlet.do?action=page");
//3、保存Page对象到request域中
request.setAttribute("page",page);
//4、请求转发
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 {
//使用DbUtils操作数据库
private QueryRunner queryRunner = new QueryRunner();
/**
* update() 方法用来执行:Insert\Update\Delete语句
*
* @return 如果返回-1,说明执行失败<br/>返回其他表示影响的行数
*/
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;
}
/**
* 查询返回一个javaBean的sql语句
*
* @param type 返回的对象类型
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @param <T> 返回的类型的泛型
* @return
*/
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;
}
/**
* 查询返回多个javaBean的sql语句
*
* @param type 返回的对象类型
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @param <T> 返回的类型的泛型
* @return
*/
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;
}
/**
* 执行返回一行一列的sql语句
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @return
*/
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 {
/**
* 查询记录
* @return
*/
@Override
public Integer queryForPageTotalCount() {
String sql="select count(*) from ordertable";
Number count=(Number)queryForSingleValue(sql);
return count.intValue();
}
/**
* 处理每页显示的数据
* @param begin
* @param pageSize
* @return
*/
@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();
// javaScript语言中提供了一个location地址栏对象
// 它有一个属性叫href.它可以获取浏览器地址栏中的地址
// href属性可读,可写
location.href = "${pageScope.basePath}${ requestScope.page.url }&pageNo=" + pageNo;<%--${ requestScope.page.url }--%>
/*location.href = "${pageContext.request.contextPath}/orderTableServlet.do?action=page&pageNo=" + pageNo;*/
});
</script>
</div>
</li>
</ul>
</nav>
</div>
分页数据的展示代码臃肿,如果在要用的地方都这样写的话,一是麻烦重复,二是代码不简洁。因此我们抽取出来分页的显示代码,要在使用分页功能的地方,通过静态包含的方式引入。
例如:
<%--静态包含页脚内容--%>
<%@include file="/pages/common/page_nav.jsp"%>