利用java反射解決Mybatis Pagehelper插件聯(lián)表查詢分頁不準確的問題

前言

反射可以獲取任何一個已知名稱的類中定義的屬性蒜危,不論它是公有還是私有潮酒!使用反射你會發(fā)現(xiàn)原來java可以如此靈活,你不用再無窮無盡地寫循環(huán)磨镶、定義變量溃蔫,它會讓你的代碼簡潔大方,耦合性更低琳猫。我本身剛剛接觸到反射伟叛,希望通過一個分頁功能的實現(xiàn)和大家一起去學習應用反射,在編程的不歸路上越走越遠脐嫂。

背景

很多人在mybatis開發(fā)中都喜歡使用pagehelper當做自己的分頁插件统刮,但是這個插件在使用過程中一直存在一個問題——多表關聯(lián)一對多的情況下分頁會出現(xiàn)不準確的情況紊遵,出現(xiàn)這種情況的原因是當你的查詢語句主表和附表之間是一對多的關系時,當sql語句查詢完成后mybatis會以主表為主將附表信息封裝到你定義的主表對應的字段中侥蒙。本人也曾嘗試去仔細閱讀pageheloer的api文檔暗膜,但是找來找去耽誤了很長時間也沒找到具體行之有效的方法。索性自己封裝一個pageutil鞭衩,總體來說效果不錯~

使用框架與結(jié)構(gòu)的定義

在案例中我們使用的前端框架是vue学搜,這里不做過多的介紹,如果大家感興趣我會在后期寫一篇使用vue-cli搭建vue項目的博客论衍。后端是springboot瑞佩。

實例

首先封裝一個pageBO,用來存放返回前端的數(shù)據(jù)

@DatapublicclassPageBO{privateintsize;//list長度privateinttotal;//總查詢數(shù)privateintstartRow;//開始條數(shù)privateintendRow;//結(jié)束條數(shù)privateintfirstPage;//第一頁頁碼privateintlastPage;//最后一頁頁碼privateintpages;//頁數(shù)privateint[]navigatepageNums;//頁碼數(shù)組privateintpageNum;//當前頁碼privateintpageSize;//每頁數(shù)據(jù)量Object list;//數(shù)據(jù)列表}

pageUtil源碼:

importcom.easy.xbo.PageBO;importorg.apache.poi.ss.formula.functions.T;importjava.lang.reflect.Method;importjava.util.List;publicclassPageUtil{publicstaticPageBOgetPageBOData(Object service,Object param)throwsException{PageBO pageBO=newPageBO();ClassPageclass=service.getClass();ClassParamclass=param.getClass();Method PagemethodSelectCount=Pageclass.getDeclaredMethod("selectCount",Paramclass);//可以通過Method類的invoke方法調(diào)用類方法//查詢總數(shù)坯台,此時注意需要傳遞兩個參數(shù)炬丸,第一個是方法所在的類,第二個是方法需要的參數(shù) //invoke方法第一個參數(shù)是固定的蜒蕾,是方法所在類稠炬,第二個是可選的,是方法所需參數(shù)intcount=Integer.parseInt(PagemethodSelectCount.invoke(service,param)+"");Method ParammethodGet=Paramclass.getDeclaredMethod("getWhere");Object where=ParammethodGet.invoke(param);ClassWhereclass=where.getClass();//第幾頁Method WheremethodPage=Whereclass.getDeclaredMethod("getPage");intpage=Integer.parseInt(WheremethodPage.invoke(where)+"");//每頁條數(shù)Method WheremethodRows=Whereclass.getDeclaredMethod("getRows");intpageSize=Integer.parseInt(WheremethodRows.invoke(where)+"");//計算本頁從哪一條數(shù)據(jù)開始intstartRow=(page-1)*pageSize;Method WheremethodStartrow=Whereclass.getDeclaredMethod("setStartrow",int.class);WheremethodStartrow.invoke(where,startRow);//計算頁數(shù)intpages=count/pageSize;if(count%pageSize>0){pages++;}//存放頁碼的數(shù)組int[]NavigatepageNums=newint[pages];for(inti=1;i<=pages;i++){NavigatepageNums[i-1]=i;}pageBO.setTotal(count);pageBO.setStartRow(startRow+1);pageBO.setFirstPage(1);pageBO.setPages(pages);pageBO.setLastPage(pages);pageBO.setNavigatepageNums(NavigatepageNums);pageBO.setPageSize(pageSize);pageBO.setPageNum(page);Method PagemethodSelectPage=Pageclass.getDeclaredMethod("selectPage",Paramclass);Object pagelist=PagemethodSelectPage.invoke(service,param);pageBO.setList(pagelist);pageBO.setSize(((List)pagelist).size());intendRow=startRow+pageBO.getSize();pageBO.setEndRow(endRow);returnpageBO;}}

Object 類型的 service 中必須有兩個方法:一個是 selectCount 用來查詢分頁的total咪啡,也就是所有符合查詢條件的數(shù)據(jù)總數(shù)首启;一個是 selectPage 這個方法查詢具體數(shù)據(jù)。

Object 類型的 param 包含兩個參數(shù):一個是where瑟匆;一個是xdo闽坡。(具體結(jié)構(gòu)參照

然后就是對反射的使用(具體參照

以上是公用部分的代碼

下面舉個栗子~~:

公司與員工的關系栽惶,一個公司有多個員工愁溜,屬于一對多的關系,這里插一句lombok很好用外厂,有興趣的小伙伴可以了解一下

CompanyDO源碼:

```java

package com.easy.xdo;

import lombok.Data;

@Data

public class CompanyDO {

//演示公司類

private String id;

private String name;

private String address;

private List<EmployeDO> employes;

}

```

CompanyWhere 源碼:

```java

package com.easy.xdo;

import lombok.Data;

@Data

public class CompanyWhere extends CompanyDO{

/**

? ? * page -1 默認不分頁

? ? */

? ? private int page;

? ? private int rows;

? ? private int startrow;

? ? private String suffix;

}

```

CompanyParam 源碼:

```java

package com.easy.xdo;

import lombok.Data;

@Data

public class CompanyParam {

private CompanyDO xdo;

private CompanyWhere where;

}

```

EmployeDO源碼:

```java

package com.easy.xdo;

import lombok.Data;

@Data

public class EmployeDO {

? ? //演示員工類

private String id;

private String name;

private String age;

private String companyid;

}

CompanyController源碼:

package com.easy.controller;

import io.swagger.annotations.Api;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import com.easy.service.CompanyService;

import com.easy.util.PageUtil;

import com.easy.xbo.PageBO;

import com.easy.xdo.CompanyParam;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.PostMapping;

import io.swagger.annotations.ApiOperation;

@Api("API-公司接口")

@RestController

@RequestMapping("api/v1/company")

public class CompanyController {

@Autowired

? ? private CompanyService companyService;

@ApiOperation(value = "分頁查詢")

? ? @PostMapping("/page")

? ? public PageBO page() throws Exception{

CompanyParam param=new CompanyParam();

? ? ? ? return PageUtil.getPageBOData(companyService,param);

? ? }

}

CompanyService 源碼:

package com.easy.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.easy.dao.CompanyDAO;

import com.easy.xdo.CompanyDO;

import com.easy.xdo.CompanyParam;

public class CompanyService {

@Autowired

private CompanyDAO companyDAO;

public List<CompanyDO> selectPage(CompanyParam param){

? ? ? ? return companyDAO.selectList(param);

? ? }

? ? public int selectCount(CompanyParam param){

? ? ? ? return companyDAO.selectCount(param);

? ? }

}

dao源碼

package com.easy.dao;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.easy.xdo.CompanyDO;

import com.easy.xdo.CompanyParam;

@Repository

public interface CompanyDAO {

List<CompanyDO> selectList(CompanyParam param);

? ? int? selectCount(CompanyParam param);

}

xml源碼

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.easy.dao.CompanyDAO">

? <resultMap id="BaseResultMap" type="com.easy.xdo.CompanyDO">

? ? <id column="id" property="id" />

? ? <result column="name" property="name" />

? ? <result column="address" property="address" />

? ? <collection property="employes" ofType="com.easy.xdo.EmployeDO">

? ? ? <id column="eid" property="id" />

? ? ? <result column="ename" property="name"/>

? ? ? <result column="age" property="age"/>

? ? </collection>

? </resultMap>

? <sql id="sqlBase">

? ? c.id,c.name,c.address,e.id as eid,e.name as ename,e.age

? </sql>

? <sql id="WhereModelSql">

? ? <if test="where != null">

? ? ? <where>

? ? ? ? <if test="where.id != null and where.id != ''"> AND c.id=#{where.id} </if>

? ? ? ? <if test="where.name != null and where.name != ''"> AND c.name=#{where.name} </if>

? ? ? ? <if test="where.address != null and where.address != ''"> AND c.address=#{where.address} </if>

? ? ? </where>

? ? </if>

? </sql>

? <select id="selectList" resultMap="BaseResultMap" parameterType="com.easy.xdo.CompanyParam">

? ? select

? ? <include refid="sqlBase" />

? ? from company c left join employe e on? c.id=e.companyid?

? ? where c.id in (select page.id from (select id from company

? ? <include refid="WhereModelSql" />? limit #{where.startrow},#{where.rows})

? ? page)

? </select>

? <select id="selectCount" resultType="java.lang.Integer" parameterType="com.easy.xdo.CompanyParam">

? ? select

? ? count(c.id)

? ? from company

? ? <include refid="WhereModelSql" />

? </select>

</mapper>

這里特殊說明一下in語句子語句中不能使用limit冕象,但是孫語句中可以使用,很蛋疼所以多包了一層汁蝶,sql語句這里大家可以根據(jù)自己的情況隨意寫渐扮,只要最后傳參進去出來的數(shù)據(jù)準確就ok了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市掖棉,隨后出現(xiàn)的幾起案子墓律,更是在濱河造成了極大的恐慌,老刑警劉巖幔亥,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耻讽,死亡現(xiàn)場離奇詭異,居然都是意外死亡帕棉,警方通過查閱死者的電腦和手機针肥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門饼记,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人慰枕,你說我怎么就攤上這事具则。” “怎么了具帮?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵博肋,是天一觀的道長。 經(jīng)常有香客問我匕坯,道長束昵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任葛峻,我火速辦了婚禮锹雏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘术奖。我一直安慰自己礁遵,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布采记。 她就那樣靜靜地躺著佣耐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪唧龄。 梳的紋絲不亂的頭發(fā)上兼砖,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天蹈集,我揣著相機與錄音蟹肘,去河邊找鬼。 笑死烦租,一個胖子當著我的面吹牛丸冕,可吹牛的內(nèi)容都是我干的耽梅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼胖烛,長吁一口氣:“原來是場噩夢啊……” “哼眼姐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起佩番,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤众旗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后趟畏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贡歧,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了艘款。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片持际。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖哗咆,靈堂內(nèi)的尸體忽然破棺而出蜘欲,到底是詐尸還是另有隱情,我是刑警寧澤晌柬,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布姥份,位于F島的核電站,受9級特大地震影響年碘,放射性物質(zhì)發(fā)生泄漏澈歉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一屿衅、第九天 我趴在偏房一處隱蔽的房頂上張望埃难。 院中可真熱鬧,春花似錦涤久、人聲如沸涡尘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽考抄。三九已至,卻和暖如春蔗彤,著一層夾襖步出監(jiān)牢的瞬間川梅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工然遏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贫途,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓啦鸣,卻偏偏與公主長得像潮饱,于是被迫代替她去往敵國和親来氧。 傳聞我的和親對象是個殘疾皇子诫给,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355