MyBatis分頁插件PageHelper實現原理分析

項目地址:https://github.com/pagehelper/Mybatis-PageHelper

 使用方法

public PageInfo<User> findAllByLike(String searchText, PageInfo pageInfo) {
    PageHelper.startPage(pageInfo.getPageNum(),pageInfo.getPageSize());
    List<User> userList = userDao.findAllByLikeNickName(searchText);
    PageInfo<User> page = new PageInfo<>(userList);
    return page;
}

我心想着爲什麼這一句話就可以實現分頁呢,況且sql語句也沒有按照分頁的執行呀。官方文檔裏是這樣說的:“在你需要進行分頁的 MyBatis 查詢方法前調用 PageHelper.startPage 靜態方法即可,緊跟在這個方法後的第一個MyBatis 查詢方法會被進行分頁”。哦,原來如此,如果只是爲了使用這個插件,可能看官方文檔的說明就夠了。但是知其然還要知其所以然。我們沿着PageHelper.startPage這個靜態方法一探究竟,一步一步的深入,來到了com.github.pagehelper.page.PageMethod類裏的下面代碼:

public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
    Page<E> page = new Page(pageNum, pageSize, count);
    page.setReasonable(reasonable);
    page.setPageSizeZero(pageSizeZero);
    Page<E> oldPage = getLocalPage();
    if (oldPage != null && oldPage.isOrderByOnly()) {
        page.setOrderBy(oldPage.getOrderBy());
    }
        setLocalPage(page);
        return page;
}

裏面最重要的就是這個setLocalPage(page);

protected static void setLocalPage(Page page) {
    LOCAL_PAGE.set(page);
}

終於明白了,是基於ThreadLocal,但是還沒完,我們只看到了set的地方,卻沒有看到remove的地方,com.github.pagehelper.page.PageMethod類裏有個clearPage方法

清除本地線程變量的就是這個clearPage方法,我們再看看這個clearPage會在什麼地方調用,看到下面的截圖,恍然大悟了。PageInterceptor這個類的名字是不是特別熟悉?如果你自己實現過mybatis分頁插件的話,我想你會取相同的名字。我們看看這個類com.github.pagehelper.PageInterceptor的定義:

這個類實現了org.apache.ibatis.plugin.Interceptor接口。在com.github.pagehelper.PageInterceptor.intercept(Invocation)方法的最後finally塊中調用了afterAll

來看看com.github.pagehelper.PageHelper中afterAll的實現,最後調用了clearPage方法清除ThreadLocal變量:

總結起來就是,在你要使用分頁查詢的時候,先使用PageHelper.startPage這樣的語句在當前線程上下文中設置一個ThreadLocal變量,再利用mybatis提供的攔截器(插件)實現一個com.github.pagehelper.PageInterceptor接口,這個分頁攔截器攔截到後會從ThreadLocal中拿到分頁的信息,如果有分頁信息,這進行分頁查詢,最後再把ThreadLocal中的東西清除掉。所ThreadLocal在使用過程中一定要明白如何使用,什麼時候該清除,尤其是在線程池盛行的年代。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章