zoukankan      html  css  js  c++  java
  • myBatis分页插件PageHelper的使用及源码详解

      在做某购物商城的系统时,遇到了需要分页的情况,采用myBatis的一个十分便捷的插件PageHelper进行后台分页。具体使用方法就是通过maven添加依赖:

     <dependency>
          <groupId>com.github.pagehelper</groupId>
          <artifactId>pagehelper</artifactId>
          <version>4.1.0</version>
    </dependency>

    使用的过程如下:

     Controller层:
        @RequestMapping("list.do") @ResponseBody /** * 获取产品详情 */ public ServerResponse getList(HttpSession session, @RequestParam(value = "pageNum",defaultValue = "1") int pageNum, @RequestParam(value = "pageNum",defaultValue = "10")int pageSize){ //非关键代码省略if(iUserService.checkAdminRole(user).isSuccess()){ return iProductService.getProductList(pageNum, pageSize); } }
    Service实现:

          public ServerResponse<PageInfo> getProductList(int pageNum,int pageSize){
          //startPage
          PageHelper.startPage(pageNum, pageSize);
          //填充自己的sql,此过程是将拿到的数据库对象转换为表示层的VO对象,可忽略
          List<ProductListVo> productListVoList = new ArrayList<ProductListVo>();
          List<Product> productList = productMapper.selectList();
          for(Product productItem : productList){
            ProductListVo productListVo = assembleProductListVo(productItem);
            productListVoList.add(productListVo);
          }
          //pageHelper
          PageInfo pageResult = new PageInfo(productList);
          pageResult.setList(productListVoList);


          return ServerResponse.createBySuccess(pageResult);
           }

    接着我们来了解PageHelper的内部实现过程。

    首先来看startPage的设计,进入该静态方法中,代码中方法重载的startPage代码如下:

    /**
         * 开始分页
         *
         * @param pageNum      页码
         * @param pageSize     每页显示数量
         * @param count        是否进行count查询
         * @param reasonable   分页合理化,null时用默认配置
         * @param pageSizeZero true且pageSize=0时返回全部结果不执行分页,false时分页,null时用默认配置
         */
        public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
            Page<E> page = new Page<E>(pageNum, pageSize, count);
            page.setReasonable(reasonable);
            page.setPageSizeZero(pageSizeZero);
            //当已经执行过orderBy的时候
            Page<E> oldPage = SqlUtil.getLocalPage();
            if (oldPage != null && oldPage.isOrderByOnly()) {
                page.setOrderBy(oldPage.getOrderBy());
            }
            SqlUtil.setLocalPage(page);
            return page;
        }

    第一行代码对应page的构造方法为

    public Page(int pageNum, int pageSize, boolean count) {
            this(pageNum, pageSize, count, null);
    }

    其中调用的构造方法为:

    private Page(int pageNum, int pageSize, boolean count, Boolean reasonable) {
            super(0);
            if (pageNum == 1 && pageSize == Integer.MAX_VALUE) {
                pageSizeZero = true;
                pageSize = 0;
            }
            this.pageNum = pageNum;
            this.pageSize = pageSize;
            this.count = count;
            calculateStartAndEndRow();
            setReasonable(reasonable);
        }

    第一行super(0)是调用父类ArrayList的构造函数,接着几行是对参数的填充,我们先看看 calculateStartAndEndRow() 方法,顾名思义就是计算起始和终止行号,看代码:

      /**
         * 计算起止行号
         */
        private void calculateStartAndEndRow() {
            this.startRow = this.pageNum > 0 ? (this.pageNum - 1) * this.pageSize : 0;
            this.endRow = this.startRow + this.pageSize * (this.pageNum > 0 ? 1 : 0);
        }

    再看setReasonable(reasonable)的作用,先放代码:

    public Page<E> setReasonable(Boolean reasonable) {
            if (reasonable == null) {
                return this;
            }
            this.reasonable = reasonable;
            //分页合理化,针对不合理的页码自动处理
            if (this.reasonable && this.pageNum <= 0) {
                this.pageNum = 1;
                calculateStartAndEndRow();
            }
            return this;
        }

    显而易见,这是对表示层传来的页数做逻辑处理,页数如果为0或负数,则置1。其实这些逻辑在前端就需要进行控制,当页数为1时,无法点击进行上一页。

    接下来我们再回头看Service实现中的pageHelper的代码:

    首先是创建一个pageInfo对象,将从数据库拿到的list数据作为参数,看构造函数代码:

        /**
         * 包装Page对象
         *
         * @param list
         */
        public PageInfo(List<T> list) {
            this(list, 8);
        }

    点进去看:

    /**
         * 包装Page对象
         *
         * @param list          page结果
         * @param navigatePages 页码数量
         */
        public PageInfo(List<T> list, int navigatePages) {
            if (list instanceof Page) {
                Page page = (Page) list;
                this.pageNum = page.getPageNum();
                this.pageSize = page.getPageSize();
                this.orderBy = page.getOrderBy();
    
                this.pages = page.getPages();
                this.list = page;
                this.size = page.size();
                this.total = page.getTotal();
                //由于结果是>startRow的,所以实际的需要+1
                if (this.size == 0) {
                    this.startRow = 0;
                    this.endRow = 0;
                } else {
                    this.startRow = page.getStartRow() + 1;
                    //计算实际的endRow(最后一页的时候特殊)
                    this.endRow = this.startRow - 1 + this.size;
                }
            } else if (list instanceof Collection) {
                this.pageNum = 1;
                this.pageSize = list.size();
    
                this.pages = 1;
                this.list = list;
                this.size = list.size();
                this.total = list.size();
                this.startRow = 0;
                this.endRow = list.size() > 0 ? list.size() - 1 : 0;
            }
            if (list instanceof Collection) {
                this.navigatePages = navigatePages;
                //计算导航页
                calcNavigatepageNums();
                //计算前后页,第一页,最后一页
                calcPage();
                //判断页面边界
                judgePageBoudary();
            }
        }

    在不传导航页码数量的参数时,默认导航页码数量为8。首先判断list类型,如果是page类型,填参即可,注意startRow和endRow的计算方法。如果是Collection类型,也是进行填参,然后执行三个方法分别是calcNavigatepageNums();  calcPage();  judgePageBoudary();

    首先看calcNavigatepageNums()方法:

    /**
         * 计算导航页
         */
        private void calcNavigatepageNums() {
            //当总页数小于或等于导航页码数时
            if (pages <= navigatePages) {
                navigatepageNums = new int[pages];
                for (int i = 0; i < pages; i++) {
                    navigatepageNums[i] = i + 1;
                }
            } else { //当总页数大于导航页码数时
                navigatepageNums = new int[navigatePages];
                int startNum = pageNum - navigatePages / 2;
                int endNum = pageNum + navigatePages / 2;
    
                if (startNum < 1) {
                    startNum = 1;
                    //(最前navigatePages页
                    for (int i = 0; i < navigatePages; i++) {
                        navigatepageNums[i] = startNum++;
                    }
                } else if (endNum > pages) {
                    endNum = pages;
                    //最后navigatePages页
                    for (int i = navigatePages - 1; i >= 0; i--) {
                        navigatepageNums[i] = endNum--;
                    }
                } else {
                    //所有中间页
                    for (int i = 0; i < navigatePages; i++) {
                        navigatepageNums[i] = startNum++;
                    }
                }
            }
        }

    再看 calcPage()方法:

      /**
         * 计算前后页,第一页,最后一页
         */
        private void calcPage() {
            if (navigatepageNums != null && navigatepageNums.length > 0) {
                firstPage = navigatepageNums[0];
                lastPage = navigatepageNums[navigatepageNums.length - 1];
                if (pageNum > 1) {
                    prePage = pageNum - 1;
                }
                if (pageNum < pages) {
                    nextPage = pageNum + 1;
                }
            }
        }

    judgePageBoudary()方法:

       /**
         * 判定页面边界
         */
        private void judgePageBoudary() {
            isFirstPage = pageNum == 1;
            isLastPage = pageNum == pages;
            hasPreviousPage = pageNum > 1;
            hasNextPage = pageNum < pages;
        }

    到此,pageHelper的源码解读就结束了,实际上就是通过表示层传来的页数和每页显示的行数,以此为参数计算出分页对象page中的相关参数,并将list结果集保存至page中,而page继承自ArrayList,然后将page对象进行打包存放如pageInfo对象中,最后返回pageInfo对象。

  • 相关阅读:
    检测登录按钮 ,回车即登录
    [转]程序变量命名推荐规范
    [NET].NET Framework 3.5 SP1 真正的离线安装(转)
    Arthas使用教程[转载]
    Python中Hamcrest断言的使用
    C#中查询不支持中文的解决方法
    如何防止C#的小黑窗关闭
    AWS的EC2 micro instance真是慢啊
    一键分享文字图片到新浪微博,facebook,twitter 还有保存打印等 (使用 iOS6 自带的 social.framework)
    自己写的可以布置多个button在同一行的ActionSheet (iPhone)
  • 原文地址:https://www.cnblogs.com/Edword-ty/p/10621612.html
Copyright © 2011-2022 走看看