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对象。

猜你喜欢

转载自www.cnblogs.com/Edword-ty/p/10621612.html