1. 程式人生 > >MyBaties分頁外掛PageHelper的簡單使用

MyBaties分頁外掛PageHelper的簡單使用

 

丟擲問題:

如果想要將現有的select語句改為支援分頁功能的查詢語句該怎麼做呢?
最簡單的一種做法就是將所有的select語句都加上limit來實現分頁,這種做法有什麼問題呢?

有沒有一種簡便方法實現呢?
Mybatis提供了plugin機制,允許我們在Mybatis的原有處理流程上加入自己邏輯,所有我們就可以使用這種邏輯加上我們的分頁邏輯,也就是實現攔截器。
Mybatis支援的攔截的介面有4個,Executor、ParameterHandler、ResultSetHandler、StatementHandler。關於分頁攔截器的配置和使用後期我會更新。
 

一、PageHelper的介紹和使用

Mybatis的一個外掛,PageHelper,非常方便mybatis分頁查詢。國內牛人的一個開源專案,有興趣的可以去看原始碼。在github上倉庫地址為:Mybatis-PageHelper。它支援基本主流與常用的資料庫,這可以在它的文件上看到。這裡記錄一下使用的基本方法。PageHelpe開源地址:
github專案地址:https://github.com/pagehelper/Mybatis-PageHelper
碼雲 專案地址:http://git.oschina.net/free/Mybatis_PageHelper

 

二、專案配置

首先引入jar包依賴
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.1.4</version>
</dependency>

 

在mybatis的全域性配置檔案SqlMapConfig.xml中配置該外掛
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 配置分頁外掛 -->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <!-- 設定資料庫型別 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六種資料庫-->        
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>
</configuration>

 

三、基本使用

dao層:

public List<User> queryUserListLikeName(@Param("name") String name);

mapper.xml:

<select id="queryUserListLikeName" parameterType="String" resultType="User">
    SELECT * FROM tb_user WHERE name LIKE '%${name}%'
</select>

service.java
 

public PageInfo<User> testQueryUserListLikeName() {
    //設定分頁條件,Parameters:pageNum 頁碼pageSize 每頁顯示數量count 是否進行count查詢
    PageHelper.startPage(1, 3, true);
    List<User> users = this.userMapper.queryUserListLikeName(null);
    //取分頁後結果
    PageInfo<User> pageInfo = new PageInfo<>(list);
     PageInfo<User> pageInfo = new PageInfo<User>(users);
   
    //列印分頁資訊
    System.out.println("資料總數:" + pageInfo.getTotal());
    System.out.println("資料總頁數:" + pageInfo.getPages());
    System.out.println("最後一頁:" + pageInfo.getLastPage());
    
    return pageInfo;
}

pageInfo.java


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 prePage;
    //下一頁
    private int nextPage;
 
    //是否為第一頁
    private boolean isFirstPage = false;
    //是否為最後一頁
    private boolean isLastPage = false;
    //是否有前一頁
    private boolean hasPreviousPage = false;
    //是否有下一頁
    private boolean hasNextPage = false;
    //導航頁碼數
    private int navigatePages;
    //所有導航頁號
    private int[] navigatepageNums;
    //導航條上的第一頁
    private int navigateFirstPage;
    //導航條上的最後一頁
    private int navigateLastPage;
 
    public PageInfo() {
    }
 
    /**
     * 包裝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.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 = this.pageSize > 0 ? 1 : 0;
            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();
        }
    }
 
 
.......
}

注意:

PageHelper 方法使用了靜態的 ThreadLocal 引數,分頁引數和執行緒是繫結的。只要你可以保證在 PageHelper 方法呼叫後緊跟 MyBatis 查詢方法,這就是安全的。因為 PageHelper 在 finally 程式碼段中自動清除了 ThreadLocal 儲存的物件。如果程式碼在進入 Executor 前發生異常,就會導致執行緒不可用,這屬於人為的 Bug(例如介面方法和 XML 中的不匹配,導致找不到 MappedStatement 時), 這種情況由於執行緒不可用,也不會導致 ThreadLocal 引數被錯誤的使用。
但是如果你寫出下面這樣的程式碼,就是不安全的用法:
PageHelper.startPage(1, 10);
List<Country> list;
if(param1 != null){
    list = countryMapper.selectIf(param1);
} else {
    list = new ArrayList<Country>();
}
這種情況下由於 param1 存在 null 的情況,就會導致 PageHelper 生產了一個分頁引數,但是沒有被消費,這個引數就會一直保留在這個執行緒上。當這個執行緒再次被使用時,就可能導致不該分頁的方法去消費這個分頁引數,這就產生了莫名其妙的分頁。上面這個程式碼,應該寫成下面這個樣子:
List<Country> list;
if(param1 != null){
    PageHelper.startPage(1, 10);
    list = countryMapper.selectIf(param1);
} else {
    list = new ArrayList<Country>();
}
這種寫法就能保證安全。
如果你對此不放心,你可以手動清理 ThreadLocal 儲存的分頁引數,可以像下面這樣使用:

List<Country> list;
if(param1 != null){
    PageHelper.startPage(1, 10);
    try{
        list = countryMapper.selectAll();
    } finally {
        PageHelper.clearPage();
    }
} else {
    list = new ArrayList<Country>();
}

以上就是PageHelper的基本使用,簡單方便的分頁外掛。