Java Web 之分页技术

作者: 廖少少 | 来源:发表于2016-12-01 20:18 被阅读1207次

    本文包括:

    1、分页技术概述

    2、实现分页

    3、完善分业——分页工具条

    4、几种常见的分页工具条

    1、分页技术概述

    1. 物理分页

      • 在SQL查询时,从数据库只查询分页需要的数据

      • 通常,对于不同数据库有不同的物理分页语句

        MySQL 使用limit;
        SQLServer 使用top;
        Oracle使用rowNum

      • 对于MySQL,采用limit关键字

      • 例如:查询第11-20条数据,SQL语句:

          select * from user limit 10,10;
        
      • demo:

          @Test
          public void demo2() throws SQLException {
              // 物理分页 ,根据数据库关键字 limit 查询需要数据 查询150-200条
              String sql = "select * from customer order by name limit ?,?";
              int start = 150 - 1; // 开始索引 开始条数-1
              int len = 200 - 150 + 1; // 结束条数-开始条数 +1
              QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
              List<Customer> customers = queryRunner.query(sql,
                      new BeanListHandler<Customer>(Customer.class), start, len);
              System.out.println("size:" + customers.size());
              for (Customer customer : customers) {
                  System.out.println(customer.getName());
              }
          }
        
    2. 逻辑分页

      • 在SQL查询时,先从数据库查询出所有数据的结果集

      • 在Java代码中通过逻辑语句获得分页需要的数据

      • 例如:查询第11-20条数据:

          userList.subList(10,20)
        
      • demo:

          @Test
          public void demo3() throws SQLException {
              // 逻辑分页 150 - 200
              String sql = "select * from customer order by name";
              QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
              List<Customer> customers = queryRunner.query(sql,
                      new BeanListHandler<Customer>(Customer.class));
        
              customers = customers.subList(150 - 1, 200);
              System.out.println("size:" + customers.size());
              for (Customer customer : customers) {
                  System.out.println(customer.getName());
              }
          }
        
    3. 性能上,物理分页明显好于逻辑分页,尽量使用物理分页。

    2、实现分页

    1. 分类查询UML图

      UML绘制软件:Jude(Java and UML Developer's Environment)

      Jude教程:http://blog.csdn.net/shesunshine/article/details/5670862

    2. 在JSP页面新增a链接,其中pageQuery为PageQueryServlet的URL:

       <a href="XX/pageQuery?pNum=1">分页查询</a>
      
    3. PageQueryServlet:

       public class PageQueryServlet extends HttpServlet {
       
           public void doGet(HttpServletRequest request, HttpServletResponse response)
                   throws ServletException, IOException {
               // 获得客户端提交页码
               String pNumStr = request.getParameter("pNum");
               int pNum = Integer.parseInt(pNumStr);// 如果不是数字报错
       
               // 将页码传递 业务层
               CustomerService customerService = new CustomerService();
               List<Customer> customers = customer Service.pageQuery(pNum);
       
               // 传递结果进行显示
               request.setAttribute("customers", customers); 
               request.getRequestDispatcher("/list.jsp").forward(request,
                       response);
           }
       
           public void doPost(HttpServletRequest request, HttpServletResponse response)
                   throws ServletException, IOException {
               doGet(request, response);
           }
       
       }
      
    4. CustomerService中设置常量、新增pageQuery(int pNum)方法:

       public static final int NUMBERPAGE = 10; // 设置每页条数为常量
       public List<Customer> pageQuery(int pNum){
           // 根绝页码和每页条数计算开始索引
           int start = (pNum - 1) * NUMBERPAGE;
       
           // 调用DAO进行分页查询
           CustomerDAO customerDAO = new CustomerDAO();
           return customerDAO.findByPage(start, NUMBERPAGE);
       }
      
    5. CustomerDAO中新增findByPage(int pNum, int numberPage)方法:

       public List<Customer> findByPage(int start, int numberPage){
           String sql = "select * from customer limit ?,?";
           QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource()); // 利用DBUtils开源工具进行JDBC编程
           try{
               return queryRunner.query(sql,new BeanListHandler<Customer>(Customer.class));
           } catch(SQLException e){
               e.printStackTrace();
           }
           return null;
       }
      

    3、完善分页——分页工具条

    1. 实现分页中的虽然能提供分页,但是需要手动在地址栏输入第几页,这显然对用户极不友好,不过别急,上面只是实现了分页的效果。

    2. 很多网站都提供了分页功能,分页页面效果:

      首页 上一页 1 2 3 4 5 6 7 下一页 尾页

    3. 上面的工具条只适用页数很少的业务,google查询的页数有上万页,不可能全部显示在页面上,也不可能提供“尾页”这个选项,所以以当前页为中心,提供前后5页的跳转链接,下面是一种可借鉴的分页工具条(假设当前页数为10):

      上一页 5 6 7 8 9 10 11 12 13 14 15 下一页

      谷歌的分页工具条:


    4. 现在一般的做法,分页查询都会用单独类来封装查询结果

      PageBean ----- 在业务层返回数据返回PageBean对象

       public class PageBean {
           public static final int NUMPERPAGE = 10; // 每页多少条
           private int pNum; // 当前第几页
           private int totalPageNum; // 总页数
           private int totalRecordNum; // 总记录数
           private List<Customer> customers; // 结果数据
       
           public int getpNum() {
               return pNum;
           }
       
           public void setpNum(int pNum) {
               this.pNum = pNum;
           }
       
           public int getTotalPageNum() {
               return totalPageNum;
           }
       
           public void setTotalPageNum(int totalPageNum) {
               this.totalPageNum = totalPageNum;
           }
       
           public int getTotalRecordNum() {
               return totalRecordNum;
           }
       
           public void setTotalRecordNum(int totalRecordNum) {
               this.totalRecordNum = totalRecordNum;
           }
       
           public List<Customer> getCustomers() {
               return customers;
           }
       
           public void setCustomers(List<Customer> customers) {
               this.customers = customers;
           }
       
       }
      
    5. 于是,在CustomerService修改pageQuery(int pNum)方法:

       public static final int NUMBERPAGE = 10; // 设置每页条数为常量
       public PageBean pageQuery(int pNum) {
           // 根据页码 和 每页条数 计算开始索引
           int start = (pNum - 1) * NUMPERPAGE;
      
           PageBean bean = new PageBean();
      
           // 封装当前页码
           bean.setpNum(pNum);
      
           // 调用DAO进行分页查询 --- 结果数据
           CustomerDAO customerDAO = new CustomerDAO();
           List<Customer> customers = customerDAO.findByPage(start,
                   PageBean.NUMPERPAGE);
           bean.setCustomers(customers);
      
           // 封装总记录条数,findTotalRecordNum()方法见下文
           int totalRecordNum = customerDAO.findTotalRecordNum();
           bean.setTotalRecordNum(totalRecordNum);
      
           // 计算总页数,很常用!!!
           int totalPageNum = (totalRecordNum + PageBean.NUMPERPAGE - 1)
                   / PageBean.NUMPERPAGE;
           bean.setTotalPageNum(totalPageNum);
      
           return bean;
       }
      
    6. 在CustomerDAO中新增findTotalRecordNum()方法:

       // 查询总记录条数
       public int findTotalRecordNum() {
           String sql = "select count(*) from customer";
           QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
           // ScalarHandler
           try {
               // 因为结果集只有一行一列,所以这里应该用ScalarHandler
               long totalRecordNum = (Long) queryRunner.query(sql,
                       new ScalarHandler(1));
               return (int) totalRecordNum; // int表示的范围足够了
           } catch (SQLException e) {
               e.printStackTrace();
           }
           return 0;
       }
      
    7. 于是,在PageQueryServlet中修改:

      修改前:

       List<Customer> customers = customer Service.pageQuery(pNum);
      
       // 传递结果进行显示
       request.setAttribute("customers", customers); 
       request.getRequestDispatcher("/list.jsp").forward(request,
               response);
      

      修改后:

       PageBean pageBean = customerService.pageQuery(pNum);
      
       // 传递结果进行显示
       request.setAttribute("pageBean", pageBean); // ${pageBean}
       request.getRequestDispatcher("/page_list.jsp").forward(request,
               response);
      
    8. 接下来就是编写JSP页面:

      • 预期效果:

      • 现在的问题是:根本不知道哪个是当前页,所以还要改进一下foreach中的代码:

              <!-- 当前页不显示链接,即可知道哪个是当前页 -->
              <!-- 利用foreach循环输出 -->
              <c:forEach begin="${begin}" end="${end}" var="i">
                  <c:if test="${pageBean.pNum==i}">
                      ${i }
                  </c:if>
                  <c:if test="${pageBean.pNum!=i}">
                      <a href="/pageQuery?pNum=${i }">${i } </a>
                  </c:if>    
              </c:forEach>
        
      • 现在即可清晰的显示当前页了(可用CSS/JavaScript进一步美化界面,功能实现到此为止)

      • 实现输入页码跳转,在尾页代码的后面加入input:

              <input type="text" id="pNum" size="2"/><input type="button" value="go" onclick="jump();"/>
        

        对应的JavaScript代码:

          <script type="text/javascript">
              function jump(){
                  // 获得用户输入页码
                  var pNum = document.getElementById("pNum").value;
                  location.href="/pageQuery?pNum=" + pNum;
              }
          </script>
        

    4、几种常见的分页工具条

    • 百度


      强迫症看着难受,为什么前面显示5页,后面显示4页???

    • 必应


      必应的分页工具条很简洁。

    • CSDN博客:http://blog.csdn.net/


      这种分页工具栏比较有意思,我们来分析一下:

      • 利用Chrome浏览器的检查功能:
      • 它的分页工具条只显示5页,最左边的页码是m*5+1 (m为非负整数),最右边的页码是(m+1)*5,点击左侧的...,上述的m变为m-1,点击右侧的...,上述的m变为m+1

      • 它的重点在于计算当前页所属的m值,稍微思考一下,可以得出当前页pNum与当前页所属的m值的关系:

          int m = pNum/5;
        
      • 注意分页工具条左侧的...,当前页为6时,它会跳转到第1页,所以无论是左侧还是右侧的...,都将会跳转到对应m值的第1页。

    相关文章

      网友评论

        本文标题:Java Web 之分页技术

        本文链接:https://www.haomeiwen.com/subject/tdcfmttx.html