本文出自 http://blog.csdn.net/tjpu_lin/article/details/41050475
最近在开发一个项目,项目中有很多数据展示的模块,所以要用到分页,网上搜了很多分页的例子,但是很多实现的方法和自身的代码实例耦合度太高,导致直接拿来用根本不行。
于是自己只能亲自上阵了,关于分页实现大体逻辑是前台需要和后台相互传递页面参数(例如当前页面,页面大小,总共页数等),后台主要接受前台穿过来的pageNum(当前页码),进行数据查询,然后查完数据后返回给前台的同时也要将页面返回给前台,好让前台结合CSS在分页样式中高亮显示出当前页。
步骤大体可以分为以下几步。
1.后台sql查询数据时(底层我用的是Mysql数据库)
前台只需要传递一个pageNum,然后后台定义个页面大小的常量吧,我是定义到Constant类里面,作为一个常量来使用的。
/**
* 分页页面参数
*/
public static final Integer PAGESIZE = 10;这时候我们需要了解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 +
'}';
}
}
后台需要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="${pageContext.request.contextPath}/customer/queryAllSalesShippers?pageNum="+curPage;
}
}
}
</script>
</c:if>如果带查询条件的这么去写
window.location.href="${pageContext.request.contextPath}/customer/querySalesShipperCustomer?condition=${pageVo.queryCondition}&pageNum="+curPage;
具体样子
/*------------------------------分页 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;
}
下面说一下关于自定义标签的实现
具体的思路就是用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包,这个自己百度一下。
<?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>
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
原文:http://blog.csdn.net/tjpu_lin/article/details/41050475