1. 程式人生 > >(轉)mybatis數據庫物理分頁插件PageHelper

(轉)mybatis數據庫物理分頁插件PageHelper

tps postgresq 如果 param ble 邊界 適合 static 方法

  以前使用ibatis/mybatis,都是自己手寫sql語句進行物理分頁,雖然稍微有點麻煩,但是都習慣了。最近試用了下mybatis的分頁插件 PageHelper,感覺還不錯吧。記錄下其使用方法。

一、引入依賴jar包

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

二、配置分頁攔截器

  PageHelper的原理是基於攔截器實現的。攔截器的配置有兩種方法,一種是在mybatis的配置文件中配置,一種是直接在spring的配置文件中進行:

1、在mybatis-config.xml文件中配置

<plugins>
    <!-- com.github.pagehelper為PageHelper類所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <property name="dialect" value="mysql"/>
        <!-- 該參數默認為false -->
        <!-- 設置為true時,會將RowBounds第一個參數offset當成pageNum頁碼使用 -->
        <!-- 和startPage中的pageNum效果一樣-->
        <property name="offsetAsPageNum" value="true"/>
        <!-- 該參數默認為false -->
        <!-- 設置為true時,使用RowBounds分頁會進行count查詢 -->
        <property name="rowBoundsWithCount" value="true"/>
        
        <!-- 設置為true時,如果pageSize=0或者RowBounds.limit = 0就會查詢出全部的結果 -->
        <!-- (相當於沒有執行分頁查詢,但是返回結果仍然是Page類型)
        
<property name="pageSizeZero" value="true"/>--> <!-- 3.3.0版本可用 - 分頁參數合理化,默認false禁用 --> <!-- 啟用合理化時,如果pageNum<1會查詢第一頁,如果pageNum>pages會查詢最後一頁 --> <!-- 禁用合理化時,如果pageNum<1或pageNum>pages會返回空數據 --> <property name="reasonable" value="true"/> <!-- 3.5.0版本可用 - 為了支持startPage(Object params)方法 --> <!-- 增加了一個`params`參數來配置參數映射,用於從Map或ServletRequest中取值 --> <!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默認值 --> <!-- 不理解該含義的前提下,不要隨便復制該配置
<property name="params" value="pageNum=start;pageSize=limit;"/> --> </plugin> </plugins>

簡寫版:

 <!-- 配置分頁插件 -->
<plugins> <!-- com.github.pagehelper為PageHelper類所在包名 --> <plugin interceptor="com.github.pagehelper.PageHelper"> <!-- 設置數據庫類型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六種數據庫 在4.0.0版本以後,dialect參數可以不配置,系統能自動識別這裏提到的所有數據庫。--> <!-- <property name="dialect" value="mysql"/> --> </plugin>
</plugins>

  這裏要註意 <plugins> 在mybatis-config.xml文件中的位置,必須要符合 http://mybatis.org/dtd/mybatis-3-config.dtd 中指定的順序:

<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, 
    objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>

  不然會報錯

  參考:(稍後會整理的)

  當然mybatis-config.xml的位置,我們需要在spring的配置文件中進行指定:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <!-- 第一步:註入數據源 -->
    <property name="dataSource" ref="dataSource" /> <!-- 第二步:註入mybatis配置文件 -->
    <property name="configLocation" value="classpath:config/mybatis-config.xml" /> <!-- 配置mapper映射文件 -->
<property name="mapperLocations" value="classpath*:config/mappers/**/*.xml" /> </bean>

2、如果mybatis沒有mybatis-config.xml文件,那麽就只能直接在spring的配置文件中配置了:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   
   <!-- 第一步:註入數據源 -->
   <property name="dataSource" ref="dataSource"/>

   <!-- 第二步:配置mapper映射文件 -->   <property name="mapperLocations">     <array>       <value>classpath:config/mapper/*.xml</value>     </array>   </property>
  <!-- 第三步:配置別名 -->
   <property name="typeAliasesPackage" value="com.test.pojo"/>

   <!-- 第四步:配置插件 -->    <property name="plugins">     <array>
          <!-- 分頁插件 -->       <bean class="com.github.pagehelper.PageHelper">         <property name="properties">
                <!-- 設置數據庫類型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六種數據庫,在4.0.0版本以後,dialect參數可以不配置,系統能自動識別這裏提到的所有數據庫。-->           <value>             dialect=mysql            </value>         </property>        </bean>     </array>   </property>
</bean>

  到這裏PageHelper所需要的配置已經完成,下面還需要在serviceImpl類中加入分頁參數的代碼:

三、向攔截器傳遞分頁參數

  我們首先看下不分頁的serviceImpl代碼:

    @Override
    public List<User> getUserByNoAndEmail(String no, String email) {
        Map<String, Object> map = new HashMap<>();
        map.put("no", no);
        map.put("email", email);
        return this.userMapper.getUserByNoAndEmail(map);
    }

  然後我們將它改造成使用PageHelper分頁:

  1、首先我們根據自己項目的情況,定義一個PageBean,來保存分頁之後的結果,需要哪些屬性,就加入哪些屬性,具體可以參考源代碼中的PageInfo類的定義,其實PageInfo是插件作者給我們自己定義自己的PageBean,提供的一個參考例子

  PageInfo代碼如下:

@SuppressWarnings({"rawtypes", "unchecked"})
public class PageInfo<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    //當前頁
    private int pageNum;
    //每頁的數量
    private int pageSize;
    //當前頁的數量
    private int size;
    //由於startRow和endRow不常用,這裏說個具體的用法
    //可以在頁面中"顯示startRow到endRow 共size條數據"

    //當前頁面第一個元素在數據庫中的行號
    private int startRow;
    //當前頁面最後一個元素在數據庫中的行號
    private int endRow;
    //總記錄數
    private long total;
    //總頁數
    private int pages;
    //結果集
    private List<T> list;

    //第一頁
    private int firstPage;
    //前一頁
    private int prePage;
    //下一頁
    private int nextPage;
    //最後一頁
    private int lastPage;

    //是否為第一頁
    private boolean isFirstPage = false;
    //是否為最後一頁
    private boolean isLastPage = false;
    //是否有前一頁
    private boolean hasPreviousPage = false;
    //是否有下一頁
    private boolean hasNextPage = false;
    //導航頁碼數
    private int navigatePages;
    //所有導航頁號
    private int[] navigatepageNums;

    /**
     * 包裝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.total = page.getTotal();
            this.pages = page.getPages();
            this.list = page;
            this.size = page.size();
            //由於結果是>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;
            }
            this.navigatePages = navigatePages;
            //計算導航頁
            calcNavigatepageNums();
            //計算前後頁,第一頁,最後一頁
            calcPage();
            //判斷頁面邊界
            judgePageBoudary();
        }
    }

    /**
     * 計算導航頁
     */
    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++;
                }
            }
        }
    }

    /**
     * 計算前後頁,第一頁,最後一頁
     */
    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;
            }
        }
    }

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

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public int getPageNum() {
        return pageNum;
    }

    public int getPageSize() {
        return pageSize;
    }

    public int getSize() {
        return size;
    }

    public int getStartRow() {
        return startRow;
    }

    public int getEndRow() {
        return endRow;
    }

    public long getTotal() {
        return total;
    }

    public int getPages() {
        return pages;
    }

    public List<T> getList() {
        return list;
    }

    public int getFirstPage() {
        return firstPage;
    }

    public int getPrePage() {
        return prePage;
    }

    public int getNextPage() {
        return nextPage;
    }

    public int getLastPage() {
        return lastPage;
    }

    public boolean isIsFirstPage() {
        return isFirstPage;
    }

    public boolean isIsLastPage() {
        return isLastPage;
    }

    public boolean isHasPreviousPage() {
        return hasPreviousPage;
    }

    public boolean isHasNextPage() {
        return hasNextPage;
    }

    public int getNavigatePages() {
        return navigatePages;
    }

    public int[] getNavigatepageNums() {
        return navigatepageNums;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("PageInfo{");
        sb.append("pageNum=").append(pageNum);
        sb.append(", pageSize=").append(pageSize);
        sb.append(", size=").append(size);
        sb.append(", startRow=").append(startRow);
        sb.append(", endRow=").append(endRow);
        sb.append(", total=").append(total);
        sb.append(", pages=").append(pages);
        sb.append(", list=").append(list);
        sb.append(", firstPage=").append(firstPage);
        sb.append(", prePage=").append(prePage);
        sb.append(", nextPage=").append(nextPage);
        sb.append(", lastPage=").append(lastPage);
        sb.append(", isFirstPage=").append(isFirstPage);
        sb.append(", isLastPage=").append(isLastPage);
        sb.append(", hasPreviousPage=").append(hasPreviousPage);
        sb.append(", hasNextPage=").append(hasNextPage);
        sb.append(", navigatePages=").append(navigatePages);
        sb.append(", navigatepageNums=");
        if (navigatepageNums == null) sb.append("null");
        else {
            sb.append(‘[‘);
            for (int i = 0; i < navigatepageNums.length; ++i)
                sb.append(i == 0 ? "" : ", ").append(navigatepageNums[i]);
            sb.append(‘]‘);
        }
        sb.append(‘}‘);
        return sb.toString();
    }
}

  因為PageInfo.java只是一個示例,所以他定義得有點重量級,屬性有點多,我們可以參考它,定義適合我們自己的PageBean, 比如如下定義:

public class PageBean<T> implements Serializable {
    private static final long serialVersionUID = 8656597559014685635L;
    private long total;        //總記錄數
    private List<T> list;    //結果集
    private int pageNum;    // 第幾頁
    private int pageSize;    // 每頁記錄數
    private int pages;        // 總頁數
    private int size;        // 當前頁的數量 <= pageSize,該屬性來自ArrayList的size屬性
    
    /**
     * 包裝Page對象,因為直接返回Page對象,在JSON處理以及其他情況下會被當成List來處理,
     * 而出現一些問題。
     * @param list          page結果
     * @param navigatePages 頁碼數量
     */
    public PageBean(List<T> list) {
        if (list instanceof Page) {
            Page<T> page = (Page<T>) list;
            this.pageNum = page.getPageNum();
            this.pageSize = page.getPageSize();
            this.total = page.getTotal();
            this.pages = page.getPages();
            this.list = page;
            this.size = page.size();
        }
    }

    public long getTotal() {
        return total;
    }

    public void setTotal(long total) {
        this.total = total;
    }

    public List<T> getList() {
        return list;
    }

    public void setList(List<T> list) {
        this.list = list;
    }

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
    
}

  因為分頁查詢結果返回的是一個 Page 對象,而 Page 對象繼承自ArrayList,但是如果我們直接返回ArrayList的話,在一些場景下回遇到問題,比如在JSON處理Page類型的結果時,會被當成List來JSON格式化,會丟棄 Page 對象的所有擴展屬性,所以這裏我們要將分頁的結果 Page 類型轉換成我們自己定義的 PageBean. 我們自己定義的PageBean沒有繼承ArrayList,而是包含一個List屬性來保存分頁結果。所以避免前面的問題。因為分頁查詢結果返回的是一個 Page 對象,而 Page 對象繼承自ArrayList,但是如果我們直接返回ArrayList的話,在一些場景下回遇到問題,比如在JSON處理Page類型的結果時,會被當成List來JSON格式化,會丟棄 Page 對象的所有擴展屬性,所以這裏我們要將分頁的結果 Page 類型轉換成我們自己定義的 PageBean. 我們自己定義的PageBean沒有繼承ArrayList,而是包含一個List屬性來保存分頁結果所以避免前面的問題。

  2、修改 serviceImpl中的代碼:

    @Override
    public PageBean<User> getUserByNoAndEmail(String no, String email) {
        Map<String, Object> map = new HashMap<>();
        map.put("no", no);
        map.put("email", email);
        
        PageHelper.startPage(PaginationContext.getPageNum(), PaginationContext.getPageSize());
        List<User> list = this.userMapper.getUserByNoAndEmail(map);
        return new PageBean<User>(list);
    }

  我們只需要使用 PageHelper.startPage(pageNum, pageSize); 函數來指定 pageNum(第幾頁) 和 pageSize(每頁顯示幾條記錄) 兩個參數。然後調用原來的查詢,就進行了分頁。最後將返回的List,轉換成 PageBean類型的結果即可。前臺頁面就可以根據PageBean中包括的屬性來進行分頁顯示了。

  上面的 PaginationContext 是基於 ThreadLocal 來傳遞分頁參數的一個工具類,其實現如下:

public class PaginationContext {
    // 定義兩個threadLocal變量:pageNum和pageSize
    private static ThreadLocal<Integer> pageNum = new ThreadLocal<Integer>();    // 保存第幾頁
    private static ThreadLocal<Integer> pageSize = new ThreadLocal<Integer>();    // 保存每頁記錄條數

    /*
     * pageNum :get、set、remove
     */
    public static int getPageNum() {
        Integer pn = pageNum.get();
        if (pn == null) {
            return 0;
        }
        return pn;
    }

    public static void setPageNum(int pageNumValue) {
        pageNum.set(pageNumValue);
    }

    public static void removePageNum() {
        pageNum.remove();
    }

    /*
     * pageSize :get、set、remove
     */
    public static int getPageSize() {
        Integer ps = pageSize.get();
        if (ps == null) {
            return 0;
        }
        return ps;
    }

    public static void setPageSize(int pageSizeValue) {
        pageSize.set(pageSizeValue);
    }

    public static void removePageSize() {
        pageSize.remove();
    }
}

  實現了前臺頁面向ServiceImpl中傳遞分頁參數: pageNum 和 pageSize.

  OK,到此,PageHelper的使用方法,基本結束。

  PageHelper 項目地址:http://git.oschina.net/free/Mybatis_PageHelper

本文轉自:https://www.cnblogs.com/zhujiabin/p/5104841.html

(轉)mybatis數據庫物理分頁插件PageHelper