首页 > 代码库 > 详谈javaWeb分页的实现(模拟百度分页)
详谈javaWeb分页的实现(模拟百度分页)
本文出自 http://blog.csdn.net/tjpu_lin/article/details/41050475
最近在开发一个项目,项目中有很多数据展示的模块,所以要用到分页,网上搜了很多分页的例子,但是很多实现的方法和自身的代码实例耦合度太高,导致直接拿来用根本不行。
于是自己只能亲自上阵了,关于分页实现大体逻辑是前台需要和后台相互传递页面参数(例如当前页面,页面大小,总共页数等),后台主要接受前台穿过来的pageNum(当前页码),进行数据查询,然后查完数据后返回给前台的同时也要将页面返回给前台,好让前台结合CSS在分页样式中高亮显示出当前页。
步骤大体可以分为以下几步。
1.后台sql查询数据时(底层我用的是Mysql数据库)
前台只需要传递一个pageNum,然后后台定义个页面大小的常量吧,我是定义到Constant类里面,作为一个常量来使用的。
/** * 分页页面参数 */ public static final Integer PAGESIZE = 10;
使用的时候用Constant.PAGESIZE来调用。
这时候我们需要了解mysql分页的sql的实现是这样的:
在mysql中,我们用limit来实现分页数据的查询,limit A,B 表示从A开始,往后取B个数。
对于我们来说,第一页就是0-9这10条记录(mysql记录的索引是从0开始的),所以我们第一页取的数据对应的sql是 limit 0,10;以此类推第二页是 limit 10,10,第三页是 limit 20,10 ......
开始的索引值需要我们进行一个简单的计算,Integer startIndex = (pageNum-1)*10,,不理解的将pageNum值代入想一想就知道了。
limit startIndex ,PAGESIZE始终都是放在查询的最后面,即前面什么各种where ,group by, order by全部写好后再接limit
2.后台pageVo类的构建
因为前台需要后台的数据比较多,所以我们将它们封装到一个pageVo对象里面。
下面是我pageVo类的定义
package com.bada.core.vo; import java.io.Serializable; import java.util.Map; /** * @author Kevin * 用于分页的类 */ public class PageVo implements Serializable { private int curPage;//当前页 private int pageSize;//每页的大小 private int totalRows;//总记录数 private int totalPages;//总页数 private String queryCondition; //查询条件(字符串),用户将查询条件穿到前台然后再传回来 private Map<String,Object> queryConditions; //查询条件,针对多条件 public Map<String, Object> getQueryConditions() { return queryConditions; } public void setQueryConditions(Map<String, Object> queryConditions) { this.queryConditions = queryConditions; } public int getCurPage() { return curPage; } public void setCurPage(int curPage) { this.curPage = curPage; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getTotalRows() { return totalRows; } public void setTotalRows(int totalRows) { this.totalRows = totalRows; } public int getTotalPages() { return totalPages; } public void setTotalPages(int totalPages) { this.totalPages = totalPages; } public String getQueryCondition() { return queryCondition; } public void setQueryCondition(String queryCondition) { this.queryCondition = queryCondition; } @Override public String toString() { return "PageVo{" + "curPage=" + curPage + ", pageSize=" + pageSize + ", totalRows=" + totalRows + ", totalPages=" + totalPages + ", queryCondition='" + queryCondition + '\'' + ", queryConditions=" + queryConditions + '}'; } }
其中加的queryCondition和queryConditions变量是关于带查询条件的页面分页时存入查询条件,传到前台,然后点击下一页时传到后台时不会因为缺失查询条件而加载出的数据位空了。
后台需要new一个PageVo的对象出来,然后set对应的参数值,curPage就是pageNum,pageSize是页面大小,这里要注意的事,在进行数据查询后,还需要对满足条件的所有记录做一个计数,去获取总数值。一般 select count(ID) from ** where **=**, 一般不要count(*),查询效率会低很多的,count(0)就可以了,当然count(主键)会更快,因为主键是加了索引的。
totalRows对应刚才查询出来的总数,totalPages也是需要我们去计数的。思考一下,一共需要的页面数,我们先弄几个例子来推敲一下,如果有40条记录,根据1页是10条记录,那么就是4页,如果有38条记录,也是4页,其实就是一个总数除以页面大小向上取整,我们写一个方法去实现。
public class FormatUtils { /** * 向上取整 例如: 30条数据,每页8条 一共4页 * @param total * @param pageSize * @return */ public static int getPageTotal(int total,int pageSize){ if(pageSize == 0){//分母不能为0 return 0; } return (int)Math.ceil((double)total/pageSize); } }java自带了Math.ceil用来取大于或等于某个数的最小整数。
如果有一个查询条件就set到queryCondition中去,如果有多个就封装到map再set到queryCondtions里面去、具体前台怎么取在后面诉述、
3.前台jstl构建页面元素。
开始是写在页面上一大串jstl代码来生成html代码,后来直接封装到自定义标签中要方便的多,先展示一下jstl是如何写的。
<div class="page mg-auto"> <ul class="pagination"> <c:if test="${pageVo.totalPages > 0}"> <li><a href="javascript:onSelectPage(${pageVo.curPage - 1})">«</a></li> <c:if test="${pageVo.totalPages <= 10}"> <c:forEach var="i" begin="1" end="${pageVo.totalPages}"> <c:choose> <c:when test="${i == pageVo.curPage}"> <li class="active"><a href="javascript:onSelectPage(${i})">${i}</a></li> </c:when> <c:otherwise> <li><a href="javascript:onSelectPage(${i})">${i}</a></li> </c:otherwise> </c:choose> </c:forEach> </c:if> <c:if test="${pageVo.totalPages > 10}"> <c:if test="${pageVo.curPage < 10}"> <c:forEach var="i" begin="1" end="10"> <c:choose> <c:when test="${i == pageVo.curPage}"> <li class="current"><a href="javascript:onSelectPage(${i})">${i}</a></li> </c:when> <c:otherwise> <li><a href="javascript:onSelectPage(${i})">${i}</a></li> </c:otherwise> </c:choose> </c:forEach> </c:if> <c:if test="${pageVo.curPage >= 10}"> <c:forEach var="j" begin="${pageVo.curPage-5}" end="${pageVo.curPage+4}"> <c:if test="${j <= pageVo.totalPages}"> <c:choose> <c:when test="${j == pageVo.curPage}"> <li class="current"><a href="javascript:onSelectPage(${j})">${j}</a></li> </c:when> <c:otherwise> <li><a href="javascript:onSelectPage(${j})">${j}</a></li> </c:otherwise> </c:choose> </c:if> </c:forEach> </c:if> </c:if> <li><a href="javascript:onSelectPage(${pageVo.curPage + 1})">»</a></li> </c:if> </ul> </div>
后来才发现上面的代码其实可以用自定义标签实现,页面上一行代码就搞定,重用性高多了~ 后面讲这个!!
具体的实现逻辑是模仿百度分页的走的,
需要分析的逻辑如下(步步递进):
1、一条记录都没有,不显示页码。 判断pageVo的totalPages是否大于0,如果大于0,才去生成分页样式
2、如果总页数小于10,则写一个循环,从1开始,循环到总页数,显示N页(N为totalPages),显示这N页的同时,根据pageVo里面的curPage判断哪一页需要高亮显示出来。
3、如果总页数大于10,小于10的部分显示效果如上,大于10的时候,高亮页面始终在中间区域,即以当前页curPage做为条件,它的左边显示5个,右边显示4个。(具体效果可以看看百度搜索完结果的最下方)
一次性最多展示10页,然后高亮显示在中间(页面大于10)。
每一个a标签都要加js函数,当点击的时候跳到下面的函数里面。
<c:if test="${!empty pageVo}"> <%--防止首次进入该页面时没有pageVo而出js错误--%> <script type="text/javascript"> function onSelectPage(curPage){ if(curPage>=1 && curPage<=${pageVo.totalPages}){ if (curPage != ${pageVo.curPage}) { //当前页点击禁用跳转 window.location.href=http://www.mamicode.com/"${pageContext.request.contextPath}/customer/queryAllSalesShippers?pageNum="+curPage;>如果带查询条件的这么去写window.location.href=http://www.mamicode.com/"${pageContext.request.contextPath}/customer/querySalesShipperCustomer?condition=${pageVo.queryCondition}&pageNum="+curPage;
当然window.location.href=http://www.mamicode.com/""后面的连接写你查询数据的那个action路径。>关于对应的前端样式是前端去写的,只要有带高亮样式的分页的html代码就行,这里我也贴出css代码好了。
具体样子
/*------------------------------分页 tag defines-------------------------------*/ .page{width:80%;text-align:center;margin-top:80px;} .pagination { display: inline-block; padding-left: 0; margin: 20px 0; border-radius: 2px;} .pagination>li { display: inline; } :before, :after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } :before, :after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .pagination>li:first-child>a, .pagination>li:first-child>span { margin-left: 0; border-top-left-radius: 2px; border-bottom-left-radius: 2px; } .pagination>li>a, .pagination>li>span { position: relative; float: left; padding: 6px 12px; margin-left: -1px; line-height: 1.42857143; color: #428bca; text-decoration: none; background-color: #fff; border: 1px solid #ddd; } a { text-decoration: none; } a { background: 0 0; } .pagination>.active>a, .pagination>.active>span, .pagination>.active>a:hover, .pagination>.active>span:hover, .pagination>.active>a:focus, .pagination>.active>span:focus { color: #fff; cursor: default; background-color: #428bca; border-color: #428bca; } .pagination>li>a:hover, .pagination>li>span:hover, .pagination>li>a:focus, .pagination>li>span:focus { color: #2a6496; background-color: #eee; border-color: #ddd; } .pagination>.active>a, .pagination>.active>span, .pagination>.active>a:hover, .pagination>.active>span:hover, .pagination>.active>a:focus, .pagination>.active>span:focus { color: #fff; cursor: default; background-color: #428bca; border-color: #428bca; } .pagination>li>a, .pagination>li>span { position: relative; float: left; padding: 6px 12px; margin-left: -1px; line-height: 1.42857143; color: #428bca; text-decoration: none; background-color: #fff; border: 1px solid #ddd; }我写的分页,简单步骤就是3步,后台pageVo构建,前台jstl生成分页模块,然后加个js函数去进行跳转查询
=========================================================我是分割线========================================================
下面说一下关于自定义标签的实现
具体的思路就是用java代码去将html代码打印出来,就跟servlet用输出流打印页面一样。
1.构建自定义标签类
package com.bada.biz.service; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.TagSupport; import com.bada.core.vo.PageVo; /** * Created by Kevin on 2014/11/2. * pageVo的自定义标签类 */ public class PageVoTag extends TagSupport { private PageVo pageVo; @Override public int doStartTag() throws JspException { try { JspWriter out = this.pageContext.getOut(); if(pageVo == null) { return SKIP_BODY; } if (pageVo.getTotalPages() > 0) { out.println("<li><a href=\"javascript:onSelectPage("+(pageVo.getCurPage()-1)+")\">«</a></li>"); if (pageVo.getTotalPages() <= 10) { for (int i = 1; i <= pageVo.getTotalPages(); i++) { if (i == pageVo.getCurPage()) { out.println("<li class=\"active\"><a href=\"javascript:onSelectPage("+i+")\">"+i+"</a></li>"); } else { out.println("<li><a href=\"javascript:onSelectPage("+i+")\">"+i+"</a></li>"); } } } if (pageVo.getTotalPages() > 10) { if (pageVo.getCurPage() < 10) { for (int i = 1; i <= 10; i++) { if (i == pageVo.getCurPage()) { out.println("<li class=\"current\"><a href=\"javascript:onSelectPage("+i+")\">"+i+"</a></li>"); } else { out.println("<li><a href=\"javascript:onSelectPage("+i+")\">"+i+"</a></li>"); } } } if (pageVo.getCurPage() >= 10) { for (int j = pageVo.getCurPage()-5;j <= pageVo.getCurPage()+4; j++) { if (j <= pageVo.getTotalPages()) { if (j == pageVo.getCurPage()){ out.println("<li class=\"current\"><a href=\"javascript:onSelectPage("+j+")\">"+j+"</a></li>"); } else { out.println("<li><a href=\"javascript:onSelectPage("+j+")\">"+j+"</a></li>"); } } } } } out.println("<li><a href=\"javascript:onSelectPage("+(pageVo.getCurPage()+1)+")\">»</a></li>"); } } catch(Exception e) { throw new JspException(e.getMessage()); } return SKIP_BODY; } @Override public int doEndTag() throws JspException { return EVAL_PAGE; } @Override public void release() { super.release(); this.pageVo = null; } public PageVo getPageVo() { return pageVo; } public void setPageVo(PageVo pageVo) { this.pageVo = pageVo; } }可以看到,代码明显比jstl少很多,看来还是ava代码好使哇。这个类可能需要下载对应的jar包,这个自己百度一下。
2.自定义标签文件 命名为 pageVo.tld
<?xml version="1.0" encoding="UTF-8"?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <tlib-version>1.0</tlib-version> <short-name>cc</short-name> <uri>/pageTaglib</uri> <tag> <name>showPaging</name> <tag-class>com.bada.biz.service.PageVoTag</tag-class> <body-content>empty</body-content> <attribute> <name>pageVo</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib>里面对应的tag-class就是上面写的那个类,pageVo为传入的参数,到时候前台用${pageVo}赋值这个参数。
3.web.xml里面的配置
<!--配置自定义标签--> <jsp-config> <taglib> <taglib-uri>/pageTaglib</taglib-uri> <taglib-location>/WEB-INF/tld/pageVo.tld</taglib-location> </taglib> </jsp-config>
加在web-app标签里面一级就行了
4.jsp页面使用
引用标签:uri是上面定义的uri,prefix为前缀名,任意取的
<%@ taglib uri="/pageTaglib" prefix="pv"%>
使用标签<div class="page mg-auto"> <ul class="pagination"> <pv:showPaging pageVo="${pageVo}" /> </ul> </div>一行代码搞定。。再也不用不停copy 那一大串jstl代码了。
至此 分页模块搞定了,相关文件下载http://download.csdn.net/detail/tro_picana/8151805
详谈javaWeb分页的实现(模拟百度分页)