mybatis --pageHelper分頁(yè)數(shù)據(jù)總條數(shù)不對(duì)和數(shù)據(jù)不完整問(wèn)題分析

問(wèn)題復(fù)現(xiàn):

我有兩張表抚笔,1張是學(xué)生表扶认,另一張是家屬表。學(xué)生與家長(zhǎng)是一對(duì)多關(guān)系∈獬龋現(xiàn)在有個(gè)需求辐宾,查出每個(gè)學(xué)生和他的家屬的姓名狱从,并分頁(yè)展示。關(guān)聯(lián)關(guān)系是叠纹,學(xué)生fa_id作為學(xué)生表的主鍵矫夯,家長(zhǎng)表內(nèi)通過(guò)學(xué)生fa_id與該學(xué)生關(guān)聯(lián)。

查詢方式是:
學(xué)生表fb left join 家長(zhǎng)表 fp on fb.fa_id = fp.fa_id
然后由mybatis根據(jù)嵌套了collection的resultMap來(lái)將數(shù)據(jù)進(jìn)行封裝吊洼。(問(wèn)題就出在嵌套的resultMap上)

傳輸對(duì)象如下:

public class UserBasicInfoTo {
    private String faId; //學(xué)生id
    private String fbName; // 學(xué)生名
    private String fbPhone; //學(xué)生電話號(hào)碼
    private List<String> agedPeople; //學(xué)生家屬名列表

    public UserBasicInfoTo() {
    }
//getters and setters
}

xml配置如下:

    <resultMap id="UserInfoToMap" type="com.cloud.fmmp.base.bean.to.UserBasicInfoTo">
        <result column="FB_Name" property="fbName"></result>
        <result column="FA_ID" property="faId"></result>
        <result column="FB_Phone" property="fbPhone"></result>
        <collection property="agedPeople" ofType="java.lang.String">
            <result column="pb_cnname"></result>
        </collection>
    </resultMap>


    <select id="getUserInfoTos" resultMap="UserInfoToMap">
        SELECT FB_Name, FB_Phone, fb.FA_ID, PB_CNName FROM ins_family_base fb
        LEFT JOIN ins_family_people fp
        ON fb.FA_ID = fp.FA_ID
        WHERE fb.SYS_ID = #{sysId}
        <if test="fbName != null">
            and fb.FB_Name = #{fbName}
        </if>
        <if test="fbPhone != null">
            and fb.FB_Phone = #{fbPhone}
        </if>
    </select>

對(duì)應(yīng)的mapper接口

@Repository
public interface InsFamilyBaseMapper extends BaseMapper<InsFamilyBase> {
    List<UserBasicInfoTo> getUserInfoTos(@Param("sysId") String sysId, @Param("fbName") String fbName, @Param("fbPhone") String fbPhone);
}

service代碼片段

        Integer pageNum = Integer.parseInt(map.get("pageNum"));
        Integer pageSize = Integer.parseInt(map.get("pageSize"));
        PageHelper.startPage(pageNum, pageSize);
        // join兩張表,UserBasicInfoTo嵌套一個(gè)List
        List<UserBasicInfoTo> tos = insFamilyBaseMapper.getUserInfoTos(sysId, fbName, fbPhone);

        PageInfo<UserBasicInfoTo> pageInfo = new PageInfo<>(tos);
        return pageInfo;

上述代碼在返回pageInfo時(shí)制肮,結(jié)果的總條數(shù)不對(duì)冒窍。查出來(lái)的對(duì)象個(gè)數(shù)為13,但是pageInfo卻顯示有19條記錄豺鼻,如下所示:


查詢到的vos一共13條综液,但pageInfo的total卻為19

這里明顯是不對(duì)的,但哪里出問(wèn)題了呢儒飒?
在講resultSetHandler的時(shí)候谬莹,明確提到過(guò),它對(duì)有嵌套的resultMap的處理時(shí)桩了,做了一次ensureNoRowBounds的檢查附帽,即確保他自己沒(méi)有分頁(yè)參數(shù)。

也就是說(shuō)井誉,在mybatis的resultSetHandler處理原始帶有嵌套的resultMap時(shí)蕉扮,是沒(méi)有帶分頁(yè)參數(shù)的。那么這個(gè)時(shí)候pageHelper是怎么工作的呢颗圣?
我們實(shí)現(xiàn)一個(gè)interceptor攔截打印一下執(zhí)行的sql喳钟,就了然了。

1.把pageHelper注釋掉


不用pageHelper

不帶pageHelper時(shí)的執(zhí)行語(yǔ)句

sql查到了19條記錄在岂,此時(shí)并沒(méi)有封裝成UserBasicInfoTo奔则,即數(shù)據(jù)庫(kù)中有19條記錄是滿足查詢條件的。
然后resultSetHandler對(duì)結(jié)果集進(jìn)行處理封裝蔽午,對(duì)相同fa_id的結(jié)果集進(jìn)行合并處理(相同fa_id的結(jié)果集都封裝成1個(gè)infoTO對(duì)象易茬,對(duì)嵌套的resultMap處理并放入到該對(duì)象的List<String> agedPeople中。)所以處理完成之后就剩13個(gè)對(duì)象了.如圖

image.png

現(xiàn)在明白了祠丝,19是原始的記錄數(shù)疾呻,13是resultSetHandler封裝后的結(jié)果數(shù)。

那么為什么pageHelper能得到總的記錄數(shù)呢写半?
原因是他做了這樣一個(gè)操作:攔截了當(dāng)前執(zhí)行的sql語(yǔ)句岸蜗,并通過(guò)select count(0)語(yǔ)句得到了符合查詢條件的記錄數(shù),然后將其作為總頁(yè)數(shù),即19叠蝇。


image.png

然后璃岳,對(duì)sql語(yǔ)句進(jìn)行拼接年缎,加上分頁(yè)參數(shù)。


這是一張細(xì)長(zhǎng)的圖片

但是mybatis在處理statement的時(shí)候铃慷,將分頁(yè)參數(shù)提取出來(lái)了单芜,存放在了rowBounds中,向數(shù)據(jù)庫(kù)發(fā)送的query中并沒(méi)有l(wèi)imit a, b ,而是最后在resultSetHandler處理結(jié)果集的時(shí)候犁柜,跳過(guò)了分頁(yè)參數(shù)影響的那些行洲鸠。
image.png

[圖片上傳中...(image.png-f4c9f3-1616516429638-0)]

所以,pageHelper對(duì)嵌套的resultMap進(jìn)行分頁(yè)處理的時(shí)候馋缅,還有可能會(huì)導(dǎo)致嵌套的數(shù)據(jù)不完整扒腕,如Vo中應(yīng)當(dāng)包含一個(gè)List,該list中如果有數(shù)據(jù)被分頁(yè)參數(shù)跳過(guò)了,那么list內(nèi)的數(shù)據(jù)就不完整了萤悴。

最后舉幾個(gè)例子來(lái)說(shuō)明pageHelper在處理嵌套的resultMap時(shí)瘾腰,出現(xiàn)上面所說(shuō)的數(shù)據(jù)總數(shù)和數(shù)據(jù)不完整的問(wèn)題。

    1. pageNum =3, pageSize = 5, 轉(zhuǎn)換成sql則是limit 10, 5獲取第11-15條記錄覆履,假設(shè)這里面有3個(gè)不同的學(xué)生蹋盆,5個(gè)家長(zhǎng)。如果這三個(gè)學(xué)生還有其他滿足條件的記錄硝全,但這些記錄不在第11-15條之間栖雾,那么家長(zhǎng)信息就不完整了。另外柳沙,pageinfo的total是上面所說(shuō)的select count(0) from xxx where (columns subject to condition),也就是總的記錄數(shù)岩灭,但我現(xiàn)在要的是不同的學(xué)生數(shù),所以page中的count也不對(duì)了赂鲤。
    1. 舉個(gè)極端點(diǎn)的例子來(lái)說(shuō)噪径,pageNum = 2,時(shí),在pageSize = 1和pageSize = 20的兩種條件下数初,查詢出來(lái)的agedPeople截然不同找爱。


      pageSize = 1

      pageSize = 20

      看出問(wèn)題來(lái)了嗎? 現(xiàn)在總記錄數(shù)是19條泡孩,當(dāng)pageSize = 20的時(shí)候车摄,該學(xué)生所有的家長(zhǎng)都包含在了agedPeople中了。但是pageSize= 1的時(shí)候仑鸥,僅僅包含了第2條記錄對(duì)應(yīng)的家長(zhǎng)吮播。

總結(jié)一下問(wèn)題就是:我告訴你我要查第i到j(luò)個(gè)學(xué)生的信息,你卻給了我數(shù)據(jù)庫(kù)第i到j(luò)條記錄封裝成的學(xué)生信息眼俊。我想要知道我一共有多少個(gè)學(xué)生符合查詢條件意狠,你卻告訴我一共有多少條記錄滿足查詢條件。這完全是兩碼事嘛疮胖。

問(wèn)題的解決辦法:避免在使用分頁(yè)插件的時(shí)候用嵌套的resultMap环戈。 如resultMap中去嵌套1個(gè)List闷板。
目前可以解決的辦法有兩種:

  • 1.使用子查詢:
    <resultMap id="UserBasicInfoToMap" type="com.cloud.fmmp.base.bean.to.UserBasicInfoTo">
        <result column="FB_Name" property="fbName"></result>
        <result column="FA_ID" property="faId"></result>
        <result column="FA_NickName" property="faNickname"></result>
        <result column="FB_Phone" property="fbPhone"></result>
        <!--注意看這里,里面包含一個(gè)select子查詢-->
        <collection property="agedPeople" javaType="java.util.List" ofType="java.lang.String" column="fa_id = fa_id" select="selectAgedPeople"></collection>
    </resultMap>

    <select id="getUserBasicInfoTos" resultMap="UserBasicInfoToMap">
    SELECT FB_Name, FB_Phone, fb.FA_ID, fa.FA_NickName FROM ins_family_base fb
    LEFT JOIN ins_family_account fa
    ON fb.FA_ID = fa.FA_ID
    WHERE fb.SYS_ID = #{sysId}
    <if test="fbName != null">
        and fb.FB_Name = #{fbName}
    </if>
    <if test="fbPhone != null">
        and fb.FB_Phone = #{fbPhone}
    </if>
    </select>

    <select id="selectAgedPeople" resultType="java.lang.String">
        select PB_CNName from ins_family_people where fa_id = #{fa_id}
    </select>

mapper接口方法院塞、封裝數(shù)據(jù)使用的javaBean也相同遮晚。唯一不同的就是select和resultMap了(見(jiàn)上)。

    List<UserBasicInfoTo> getUserBasicInfoTos(@Param("sysId") String sysId, @Param("fbName") String fbName, @Param("fbPhone") String fbPhone);

此時(shí)pageNum=2, pageSize =1的結(jié)果:由于對(duì)學(xué)生進(jìn)行了過(guò)濾拦止,共有兩個(gè)符合條件的學(xué)生县遣,當(dāng)前頁(yè)顯示其中一個(gè)人的數(shù)據(jù),學(xué)生的家長(zhǎng)數(shù)據(jù)完整汹族。


關(guān)聯(lián)查詢艺玲,得到正確結(jié)果
  • 2.分步查詢, 先將包含rowKey的主體信息查出來(lái)鞠抑,即先去主表查出能區(qū)分不同實(shí)體的屬性。然后根據(jù)主體屬性與嵌套的屬性關(guān)系忌警,查嵌套屬性并自己在java代碼內(nèi)完成封裝搁拙。這種方式很容易理解,就不羅列代碼了法绵。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末箕速,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子朋譬,更是在濱河造成了極大的恐慌盐茎,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徙赢,死亡現(xiàn)場(chǎng)離奇詭異字柠,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)狡赐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)窑业,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人枕屉,你說(shuō)我怎么就攤上這事常柄。” “怎么了搀擂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵西潘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我哨颂,道長(zhǎng)喷市,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任咆蒿,我火速辦了婚禮东抹,結(jié)果婚禮上蚂子,老公的妹妹穿的比我還像新娘。我一直安慰自己缭黔,他們只是感情好食茎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著馏谨,像睡著了一般别渔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惧互,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天哎媚,我揣著相機(jī)與錄音,去河邊找鬼喊儡。 笑死拨与,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的艾猜。 我是一名探鬼主播买喧,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼匆赃!你這毒婦竟也來(lái)了淤毛?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤算柳,失蹤者是張志新(化名)和其女友劉穎低淡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瞬项,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔗蹋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了囱淋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纸颜。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖绎橘,靈堂內(nèi)的尸體忽然破棺而出胁孙,到底是詐尸還是另有隱情,我是刑警寧澤称鳞,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布涮较,位于F島的核電站,受9級(jí)特大地震影響冈止,放射性物質(zhì)發(fā)生泄漏狂票。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一熙暴、第九天 我趴在偏房一處隱蔽的房頂上張望闺属。 院中可真熱鬧慌盯,春花似錦、人聲如沸掂器。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)国瓮。三九已至灭必,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乃摹,已是汗流浹背禁漓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孵睬,地道東北人播歼。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像掰读,于是被迫代替她去往敵國(guó)和親荚恶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容