• 网页分页功能的实现


    最近在学习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页出现,以此类推

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

  • 相关阅读:
    android入门
    jquery实现市,县级联
    java基础学习笔记五(抽象类)
    java基础学习笔记四(异常)
    java基础学习笔记三(多态)
    java基础学习笔记二(接口、super、this)
    java基础学习笔记一
    实习第二十二天
    武汉第二十一天
    武汉第二十天
  • 原文地址:https://www.cnblogs.com/lz2017/p/6660109.html
Copyright © 2020-2023  润新知