MyBatis的一個分頁問題

目前獨立負責一個單獨的產(chǎn)品服務(wù),技術(shù)棧是用的SpringBoot2 + MyBatis,分頁用的MyBatis-PageHelper插件,版本是5.1.8

在項目中有一個非常復雜的查詢語句,是用作模糊查詢的,攏共LEFT JOIN了5張表,因為參數(shù)很多,又涉及到aggregation聚合操作,所以在MyBatis的xml里寫了很多分支和邏輯操作。分頁還是采用原始的PageHelper.start(from, to)這種方式。

先說下這個分頁原理,其實就是MyBatis在做真正的Query之前有一個interceptor,會先把要執(zhí)行的查詢語句做一個包裝變成:

SELECT COUNT(0) FROM 
(要執(zhí)行的查詢語句) table_count

先取到總數(shù),然后再根據(jù)limit和offset做分頁。

開始以為很簡單,但奇怪的是分頁居然出了問題,具體的表現(xiàn)就是,比如說一頁取40個對象,有時候就會出現(xiàn)一頁只有20多個,但總數(shù)和分頁的個數(shù)是對的。穩(wěn)定復現(xiàn)的問題可能很好定位,但時有時無的問題就很難解決。首先看下有沒有人遇到過類似的問題,所以先試查閱MyBatis-Pagehelper官方文檔,發(fā)現(xiàn)有類似的分頁問題issue提到需要升級版本,從5.1.8再升級到5.1.10發(fā)現(xiàn)并沒有任何變化。
這時候沒發(fā)現(xiàn)有人遇到這種相似問題后的做法一般都是梳理下思路,從數(shù)據(jù)庫的層面先排查下問題,把MyBatis執(zhí)行的SQL打印出來,然后在數(shù)據(jù)庫里執(zhí)行,但發(fā)現(xiàn)有個有意思的現(xiàn)象就是比如從數(shù)據(jù)庫里查詢到有40條結(jié)果,這是分頁后的結(jié)果,但頁面上確只有38條,仔細比對后發(fā)現(xiàn)最后幾條數(shù)據(jù)的id是有重復的,這是因為做了多次join,并且在resultmap中存在collection這種nested的嵌套型數(shù)據(jù)結(jié)構(gòu),不考慮分頁的話,MyBatis自己會按照定義好的resultMap和結(jié)果集進行組合返回我們期望的數(shù)據(jù)結(jié)構(gòu)。

所以結(jié)論結(jié)論就是MyBatis-Pagehelper這個插件如果用數(shù)據(jù)庫的分頁去實現(xiàn)分頁的話,對于collection這種一對多或多對多的關(guān)系映射其實是沒有辦法實現(xiàn)分頁的,最簡單的分頁辦法就是全部取出來,然后放到List中取subList,最后再自己封裝一個PageInfo對象即可,我也是這么做的。

封裝的代碼我先貼到這里: 其中users這個List就是一個subList,fromIndex是起始index,toIndex是截取到的結(jié)束index,total是所有user的總數(shù),pageNumber是用戶看到第幾頁,pageSize是一頁有多少User

public class PageInfoBuilder {

    public static PageInfo<User> build(List<User> users, int fromIndex, int toIndex, int total, int pageNumber, int pageSize) {
        if (fromIndex < 0 || fromIndex >= users.size() || toIndex <= 0 || toIndex < fromIndex) {
            return PageInfo.of(Collections.emptyList());
        }

        PageInfo<User> pageInfo = PageInfo.of(users.subList(fromIndex, toIndex));
        pageInfo.setPageNum(pageNumber);
        pageInfo.setPageSize(pageSize);
        pageInfo.setTotal(total);
        int totalPages = (int) Math.ceil((double) total / pageSize);
        int [] navigatepageNums = new int[totalPages];
        for (int i = 0; i < totalPages; i++) {
            navigatepageNums[i] = i + 1;
        }
        pageInfo.setStartRow(fromIndex);
        pageInfo.setEndRow(toIndex);
        pageInfo.setNavigatePages(totalPages);
        pageInfo.setNavigatepageNums(navigatepageNums);
        pageInfo.setPages(totalPages);

        calcNavigatepageNums(pageInfo, navigatepageNums);
        calcPage(pageInfo, pageNumber, totalPages);
        judgePageBoudary(pageInfo, pageNumber, totalPages);

        return pageInfo;
    }

    private static void calcNavigatepageNums(PageInfo<Template> templatePageInfo, int[] navigatepageNums) {
        if (navigatepageNums.length > 0) {
            templatePageInfo.setNavigateFirstPage(navigatepageNums[0]);
            templatePageInfo.setNavigateLastPage(navigatepageNums[navigatepageNums.length - 1]);
        }
    }

    private static void calcPage(PageInfo<Template> templatePageInfo, int pageNumber, int totalPages) {
        if (pageNumber > 1) {
            templatePageInfo.setPrePage(pageNumber - 1);
        }

        if (pageNumber < totalPages) {
            templatePageInfo.setNextPage(pageNumber + 1);
        }
    }

    private static void judgePageBoudary(PageInfo<Template> templatePageInfo, int pageNumber, int totalPages) {
        templatePageInfo.setIsFirstPage(pageNumber == 1);
        templatePageInfo.setIsLastPage(pageNumber == totalPages || totalPages == 0);
        templatePageInfo.setHasPreviousPage(pageNumber > 1);
        templatePageInfo.setHasNextPage(pageNumber < totalPages);
    }

}

這樣處理完后分頁就正常了,考慮到目前的搜索數(shù)據(jù)集并不是很大,所以可以考慮這種全部load到內(nèi)存然后取subList的做法,但如果很大的話,比如幾百萬的用戶的話這種做法可能就不是很合適,需要另尋解決方案。

最后在處理完問題后,在官方的wiki里有這么句話,嵌套的數(shù)據(jù)映射是不支持分頁的,捂臉。
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/Important.md

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容