网页分页功能的实现

最近在学习JavaWeb的时候,用到了分页功能,现在进行一个记录,以备不时之需

第一步:先完成PageBean的编写

就是对当前页数,每页显示的记录数,总记录数,总页数,分页显示的信息进行封装。作为通用的分页功能的实现,这里用到了泛型

import java.util.List;

/**
 * 分页封装
 *
 */
public class PageBean<T> {
    
    private int currPage;//当前页数
    
    private int pageSize;//每页显示记录数
    
    private int totalCount;//总记录数
    
    private int totalPage;//总页数
    
    private List<T> list;//每页显示的信息 

    public int getCurrPage() {
        return currPage;
    }

    public void setCurrPage(int currPage) {
        this.currPage = currPage;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    public int getTotalPage() {
        return totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public List<T> getList() {
        return list;
    }

    public void setList(List<T> list) {
        this.list = list;
    }

}

第二步:在Action类中编写一个分页方法

Action中通过调用业务层Service类的分页方法,Employee就是具体的信息Bean,之前泛型的使用就在于此,在不同项目中使用不同的信息Bean可以完成多种类信息的分页,employeeService就是具体的业务层Service,Service中我们也有一个叫findAll的方法。这里的currPage生成其set/get方法,等用户点击了页面及具体的页数,Struts2会获得具体的页数,然后将currPage传给Service

    // 分页(当前页)这里等于1是为了使第一页为默认页
    private int currPage = 1;
    public int getCurrPage() {
        return currPage;
    }

    public void setCurrPage(int currPage) {
        this.currPage = currPage;
    }
   /**
     * 分页查询
     */
    public String findAll() {
        PageBean<Employee> pageBean = employeeService.findAll(currPage);
        ActionContext.getContext().getValueStack().push(pageBean);
        return "findAll";
    }

第三步:编写Service中的分页方法

先生成一个PageBean对象(注意泛型),之后开始封装这个Bean。pageSize(每页显示的记录数)这里设置为3,就是每页显示3条记录,totalCount(总记录数)通过Dao中的findCount()方法来查到,totalPage(总页数)=totalCount(总记录数)/pageSize(每页大小),Math.ceil可以获得一个double的近似值(大于等于),之后我们通过Double包装类的intValue再转成int型,begin(每一页的开头的序号),list的数据也通过Dao的findPage(int,int)方法获得

public PageBean<Employee> findAll(int currPage) {
        PageBean<Employee> pageBean = new PageBean<>();
        // 封装pageBean
        pageBean.setCurrPage(currPage);
        int pageSize = 3;
        pageBean.setPageSize(pageSize);
        int totalCount = employeeDao.findCount();
        pageBean.setTotalCount(totalCount);
        Double totalPage = Math.ceil((double) totalCount / pageSize);
        pageBean.setTotalPage(totalPage.intValue());
        int begin = (currPage - 1) * pageSize;
        List<Employee> list = employeeDao.findPage(begin, pageSize);
        pageBean.setList(list);
        return pageBean;
    }

第四步:编写Dao中的方法

这之前在Service中使用的findCount()和findPage(int,int),值得注意的就是findCount()中的hql语句使用count(*),还有就是findPage(int,int)中使用了org.hibernate.criterion.DetachedCriteria类,查询时不是用find方法,而是findByCriteria来查询

   /**
     * 查询总记录数
     */
    public int findCount() {
        String hql="select count(*) from Employee";
        List<Long> list = (List<Long>) hibernateTemplate.find(hql);
        if(list.size()>0){
            return list.get(0).intValue();
        }
        return 0;
    }


    /**
     * 分页信息
     */
    public List<Employee> findPage(int begin, int pageSize) {
        DetachedCriteria criteria=DetachedCriteria.forClass(Employee.class);
        List<Employee> list = (List<Employee>) hibernateTemplate.findByCriteria(criteria, begin, pageSize);
        return list;
    }

基本上通过以上四步就完成了分页功能逻辑代码的编写,接下来就是对视图层页面的编写

下面是个分页的简单显示,处于首页时,只显示下一页和尾页,处于尾页时,只显示首页和上一页,其他页就都显示。这里使用的Struts2的标签库,所以不要忘了要加上

<%@ taglib uri="/struts-tags" prefix="s" %>

为什么我们可以直接currPage,totalPage等属性?是因为我们之前将PageBean对象放入了值栈中

<table border="0" cellspacing="0" cellpadding="0"  width="900px">
<tr>
<td align="right">
   <span><s:property value="currPage"/>/<s:property value="totalPage"/></span>&nbsp;&nbsp;
   <span>总记录数:<s:property value="totalCount"/>/每页显示:<s:property value="pageSize"/></span>&nbsp;&nbsp;
   <span>
   <s:if test="currPage!=1">
       <a href="department_findAll.action?currPage=1">[首页]</a>&nbsp;&nbsp;
       <a href="department_findAll.action?currPage=<s:property value="currPage-1"/>">[上一页]</a>&nbsp;&nbsp;
   </s:if>  
   <s:if test="currPage!=totalPage">
       <a href="department_findAll.action?currPage=<s:property value="currPage+1"/>">[下一页]</a>&nbsp;&nbsp;
       <a href="department_findAll.action?currPage=<s:property value="totalPage"/>">[尾页]</a>&nbsp;&nbsp;
   </s:if>  
   </span>
</td>
</tr>
</table>

效果图:

可以使用了struts2的<s:iterator>标签来进行迭代显示数据,这里给个参考

<s:iterator value="list" var="d">
<tr>
<td align="center"><s:property value="#d.dname"/></td>
<td align="center"><a href="">编辑</a></td>
<td align="center"><a href="">删除</a></td>
</tr>
</s:iterator>

 注意:一定要在Struts.xml文件中配置转跳到我们编写的action上(在这里是department_findAll.action),不然打开要分页的页面时,不会进行分页操作,只有你选择了页数才会分页

后记

我在使用的过程中发现,要使用分页功能的地方不少,在某些地方,使用上面的会出错:例如对条件查询之后的结果进行分页,第一页很OK,但是你点击下一页/某一页的时候会出现查询条件的丢失问题,再进行的查询结果分页是没有条件的,就会出错

解决办法(我使用Servlet+JDBC实现的,原理是一样的):

在我们的POJO中添加一个url属性,表示查询条件,因为POST方式条件是放在请求头中的,很不方便,所以再进行条件查询的的表单使用GET方式:

<form action="<c:url value='/customerServlet'/>" method="get">

我们需要获得这个url(包括项目名+Servlet名+条件),获得这个url封装到POJO中

改进后的PageBean:

import java.util.List;

public class PageBean<T> {
    //当前页
    private int currPage;
    //每页记录数
    private int pageSize;
    //总记录数
    private int totalCount;
    //数据集合
    private List<T> list;
    //url表示条件查询的条件(GET方式)
    private String url;

    public int getCurrPage() {
        return currPage;
    }

    public void setCurrPage(int currPage) {
        this.currPage = currPage;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    //设置总页数(计算得出)
    public int getTotalPage() {
        Double totalPage=Math.ceil((double)totalCount/pageSize);
        return totalPage.intValue();
    }

    public List<T> getList() {
        return list;
    }

    public void setList(List<T> list) {
        this.list = list;
    }

    public String getUrl() {
        return url;
    }
    
    public void setUrl(String url) {
        this.url = url;
    }
}

控制层要添加的方法(encoding()方法根据情况添加):

    /**
     * 得到包含查询的条件URL
     */
    private String getURL(HttpServletRequest request){
        String contextPath=request.getContextPath();  //项目名
        String servletPath=request.getServletPath();  //servlet路径,即/*Servlet   建议使用request.getRequestURI()获得一个包含项目名+Servlet的路径
        String queryString=request.getQueryString();  //?后面的参数
        //判断参数中是否带当前页(currPage)
        if(queryString.contains("&currPage=")){
            int index=queryString.lastIndexOf("&currPage=");
            queryString=queryString.substring(0, index);
        }
        return contextPath+servletPath+"?"+queryString;
    }
    
    /**
     *对(这里是4个条件)条件进行编码(GET方式防止中文乱码,POST方式已经在BaseServlet中配置) 
     * @throws UnsupportedEncodingException 
     *
     */
    private Customer encoding(Customer customer) throws UnsupportedEncodingException{
        String cname=customer.getCname();
        String gender=customer.getGender();
        String cellPhone=customer.getCellphone();
        String email=customer.getEmail();
        if(cname!=null&&!cname.isEmpty()){
           // cname=new String(cname.getBytes("ISO-8859-1"),"UTF-8");    //tomcat8之前tomcat默认字符编码使用是ISO-8859-1
            cname=new String(cname.getBytes(),"UTF-8");         
            customer.setCname(cname);
        }
        if(gender!=null&&!gender.isEmpty()){
            //gender=new String(gender.getBytes("ISO-8859-1"),"UTF-8");  
            gender=new String(gender.getBytes("ISO-8859-1"),"UTF-8");
            customer.setCname(gender);
        }
        if(cellPhone!=null&&!cellPhone.isEmpty()){
            //cellPhone=new String(cellPhone.getBytes("ISO-8859-1"),"UTF-8");
            cellPhone=new String(cellPhone.getBytes(),"UTF-8");
            customer.setCname(cellPhone);
        }
        if(email!=null&&!email.isEmpty()){
            //email=new String(email.getBytes("ISO-8859-1"),"UTF-8");
            email=new String(email.getBytes(),"UTF-8");
            customer.setCname(email);
        }
        return customer;
    } 

这个geturl()方法就是获得url的方法,我用来表示当前页的变量使用的是currPage,根据实际情况更换。encoding()方法是处理get方式获得的条件中文乱码问题,如果项目使用tomcat8之后的服务器就不用转码了。注意:现在是所有的查询的方法的需要使用geturl()方法,并将返回值封装到POJO中。使用request.getRequestURI()方法也可以获得Struts2中Action的地址。使用框架开发应该就不用考虑encoding()这个编码方法,只需要处理geturl()方法了

多条件查询的Servlet方法:

    /**
     * 多条件查询
     */
    public String query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获得查询条件
        Customer customer=CommonUtils.toBean(request.getParameterMap(), Customer.class);
        //处理GET请求的编码格式
        //customer = encoding(customer);              因为我的tomcat是8.5,所以就不转码了
        //处理PageBean
        int pageSize=10;         //每页记录数为10
        int currPage=getCurrentPage(request);
        int begin=(currPage-1)*pageSize;         //计算每页的开头
        PageBean<Customer> pageBean=customerService.query(customer,begin,pageSize);
        //封装当前页和每页记录数
        pageBean.setCurrPage(currPage);
        pageBean.setPageSize(pageSize);
        //封装url
        pageBean.setUrl(getURL(request));
        //将pageBean保存request
        request.setAttribute("pb", pageBean);
        return "f:/list.jsp";
    }

注意:因为这种方式的页码算是使用的超连接,所以currPage可能会被恶意输入,需要验证一下,这里粘一个使用正则验证是否是数字的方法(其实不仅仅要验证是否为数字,还应该验证当前页不要大于总页数,也不要小于1):

    /**
     * 使用正则验证输入的是否为数字
     * @param string
     * @return
     */
    public static boolean isNum(String string) {
        Pattern pattern=Pattern.compile("[1-9]{1}\\d*");
        Matcher matcher = pattern.matcher(string);
        return matcher.matches();
    }

Dao中多条件查询:

/**
     * 多条件查询
     * 
     */
    public PageBean<Customer> query(Customer customer, int begin, int pageSize) {
        try {
            PageBean<Customer> pageBean=new PageBean<Customer>();
            
            /**
             * 根据条件查询出总记录数
             * 拼sql语句
             */
            StringBuilder cntSql=new StringBuilder("select count(*) from t_customer");
            StringBuilder whereSql=new StringBuilder(" where 1=1");
            List<Object> params=new ArrayList<Object>();
            
            String cname=customer.getCname();
            if(cname!=null&&!cname.trim().isEmpty()){
                whereSql.append(" and cname like ?");
                params.add("%"+cname+"%");
            }
            
            String gender=customer.getGender();if(gender!=null&&!gender.trim().isEmpty()){
                whereSql.append(" and gender=?");
                params.add(gender);
            }
            
            String phone=customer.getCellphone();
            if(phone!=null&&!phone.trim().isEmpty()){
                whereSql.append(" and cellphone like ?");
                params.add("%"+phone+"%");
            }
            
            String email=customer.getEmail();
            if(email!=null&&!email.trim().isEmpty()){
                whereSql.append(" and email like ?");
                params.add("%"+email+"%");
            }
            //运行sql
            Number count=(Number) qr.query(cntSql.append(whereSql).toString(), new ScalarHandler(),params.toArray());
            pageBean.setTotalCount(count.intValue());

            
            /**
             * 根据条件查询出每页数据
             */
            StringBuilder listSql=new StringBuilder("select * from t_customer");
            //limit子句分页
            StringBuilder limitSql=new StringBuilder(" order by cname limit ?,?");
            //添加这两个参数
            params.add(begin);
            params.add(pageSize);
            //运行sql
            List<Customer> beanList=qr.query(listSql.append(whereSql).append(limitSql).toString(), 
                    new BeanListHandler<Customer>(Customer.class),
                    params.toArray());
            pageBean.setList(beanList);
            return pageBean;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

我还是使用的是拼接sql的方式,与众不同的是分页中使用的count语句limit语句都是带where条件的

 在页面中也需要进行相应的修改(我这里使用的是JSTL):

第${pb.currPage }页/共${pb.totalPage }页
<c:if test="${pb.currPage>1 }">
<a href="${pb.url }&currPage=1">首页</a>
<a href="${pb.url }&currPage=${pb.currPage-1 }">上一页</a>
</c:if>

<c:if test="${pb.currPage<pb.totalPage }"> <a href="${pb.url }&currPage=${pb.currPage+1 }">下一页</a> <a href="${pb.url }&currPage=${pb.totalPage }">尾页</a> </c:if>

pb就是我放在request中的pageBean的名字,注意url需要我们从pageBean的url中取出,页面只关心当前页,不去关心路径到底是什么

一个页码的实现

可能说一个页码又什么好实现的,直接把所有页数写出来,都加上超链接不就好了,但是观察百度等有页码的网站,你会发现页面是会随着页数变换的,比如说:先显示10页,但是当你点击第7页的时候,第1页应该隐藏,第11页应该出现,只有出现11页我们才能点击,这就是要实现的功能

<%-- 页码 --%>
<c:choose>
<%-- 总页数小于等于10页,把所有页显示 --%>
  <c:when test="${pb.totalPage<=10 }">
    <c:set var="begin" value="1"/>
    <c:set var="end" value="${pb.totalPage }"/>
  </c:when>
  <c:otherwise>
    <%--总页数>10,计算begin和end  --%>
    <c:set var="begin" value="${pb.currPage-5 }"/>
    <c:set var="end" value="${pb.currPage+4 }"/>
    <%--处理头溢出  --%>
    <c:if test="${begin<1 }">
      <c:set var="begin" value="1"/>
      <c:set var="end" value="10"/>
    </c:if>
    <%-- 处理尾溢出 --%>
    <c:if test="${end>pb.totalPage }">
      <c:set var="begin" value="${pb.totalPage-9 }"/>
      <c:set var="end" value="${pb.totalPage }"/>
    </c:if>
  </c:otherwise>
</c:choose>
<c:forEach begin="${begin }" end="${end }" var="i">
  <c:choose>
   <c:when test="${i eq pb.currPage }">
     [${i }]
   </c:when>
   <c:otherwise>
     <a href="${pb.url }&currPage=${i }">[${i }]</a>
   </c:otherwise>
  </c:choose>
</c:forEach>

这是一个显示10页,点击第7页后,1页消失11页出现,点击8页后,2页消失12页出现,以此类推

效果图(这是我把页码放在上一页和下一页之间的效果):

posted @ 2017-04-02 23:08  OverZeal  阅读(16006)  评论(0编辑  收藏  举报