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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM