Mybatis 分頁插件實現(xiàn)

Mybatis 分頁插件實現(xiàn)

有數(shù)據(jù)的地方就有分頁,分頁的sql基本大加都會寫,MySQL可以用limit,Oracle可以用ronum,sql server可以用top。這些基本大家都會用,但是一個程序中需要用到分頁的地方無疑是很多的,每個地方都作這樣的處理則顯示的很繁瑣,存在大量的重復(fù)工作,如果能統(tǒng)一無疑會方便很多。

對mybatis插件開發(fā)感興趣的同學(xué),可以參考mybatis官方文檔

現(xiàn)在話不多說,直接上干貨。

分頁需要哪些屬性呢

當(dāng)前頁pageNo

每頁條數(shù)pageSize

總條數(shù)totalCount

第一個和第二個屬性基本所有分頁都需要的,總條數(shù)呢?可能大多地方也是需要的,因為大多場景需要計算所有數(shù)據(jù)一共可以分多少頁;但也有一些地方不需要的,比如說我在首頁只需要展示我最近五條動態(tài)。

所以我在這里加了個屬性needTotalCount,可能有些同學(xué)會認(rèn)為直接都查出來,不需要不用就ok了,但是考慮到查詢總條數(shù)需要執(zhí)行額外的sql,帶來性能的損耗,所以這個屬性了加上去了。

針對分頁查詢,這里我建立了一個分頁的基類 ,get與set方法太占版面,所以沒貼

```packagecom.lsf.twnb.dao.base;

importjava.io.Serializable;

importjava.util.Collections;

importjava.util.List;

/**

* Base class for query

*/

publicclassPageQueryimplementsSerializable {

publicstaticfinalintDEFAULT_PAGE_SIZE=10;

publicstaticfinalintDEFAULT_PAGE_NO=1;

/**

* 是否分頁,默認(rèn)不分頁

*/

booleanisPage=false;

/**

* 每頁條數(shù)

*/

intpageSize=DEFAULT_PAGE_SIZE;

/**

* 當(dāng)前頁數(shù)數(shù)

*/

intpageNo=DEFAULT_PAGE_NO;

/**

* 數(shù)據(jù)集

*/

Listitems;

/**

* 是否需要總條數(shù),查詢總條數(shù)會額外增加一次查詢

*/

booleanisNeedTotalCount=false;

/**

* 總條數(shù)

*/

inttotalCount=0;

publicbooleanisPage() {

returnisPage;

}

publicvoidsetPage(booleanpage) {

isPage=page;

}

publicintgetPageSize() {

returnpageSize;

}

publicvoidsetPageSize(intpageSize) {

this.pageSize=pageSize;

}

publicintgetPageNo() {

returnpageNo;

}

publicvoidsetPageNo(intpageNo) {

this.pageNo=pageNo;

}

publicListgetItems() {

returnitems;

}

publicvoidsetItems(Listitems) {

this.items=items;

}

publicbooleanisNeedTotalCount() {

returnisNeedTotalCount;

}

publicvoidsetNeedTotalCount(booleanneedTotalCount) {

isNeedTotalCount=needTotalCount;

}

publicintgetTotalCount() {

returntotalCount;

}

publicvoidsetTotalCount(inttotalCount) {

this.totalCount=totalCount;

}

publicintgetOffset() {

return(pageNo-1)*pageSize;

}

}```

大家可以注意到這里多了一個items屬性和getOffest方法,items就是你需要的數(shù)據(jù)列表,offset是根據(jù)當(dāng)前頁數(shù)計算當(dāng)前偏移量。

這是查詢分頁的基類,也是查詢分頁需要的條件,所有分頁查詢條件繼承此基類。

這里我們以查詢用戶為例(get和set方法同樣省略):

/*** Condition for UserPageQuery Query*/publicclassUserPageQueryextendsPageQuery{privateStringusername;}

我們這里增加了一個條件用戶名username,那我們只需要關(guān)注這個用戶名即可,

Dao層方法:

ListqueryUser(UserPageQueryuserQuery);

Mapper配置:

selectfrom User u where username=#{username}

接下來就是考慮怎樣寫插件了,mybatis的Plugin需要繼承Interceptor接口,接口如下

publicinterfaceInterceptor {Objectintercept(Invocationinvocation)throwsThrowable;Objectplugin(Objecttarget);voidsetProperties(Propertiesproperties);}

接口共有三個方法,我們先看第二個方法plugin, plugin方法就是把你實現(xiàn)這個接口的類加入插件,寫法基本固定

@OverridepublicObjectplugin(Objecttarget) {returnPlugin.wrap(target,this);}

我們看wrap方法的源碼基本可以發(fā)現(xiàn),就是就是將攔截的方法通過動態(tài)代理的方式由第一個方法plugin執(zhí)行。

再看第三個方法setPrpperties(),顧明思義,即設(shè)置屬性,我們要使插件起作用,需要在mybatis配置文件中配置,配置如下


這里增加的property即可以在setProperties的參數(shù)中取到。

最后我們來說核心的intercept方法,前面我們說過plugin方法是將指定的方法動態(tài)代理,那具體要攔截什么方法了,我們增加分頁,無非是對sql作一下調(diào)整,所以有很多地方可以攔截,這里我們對Executor的query方法進(jìn)行攔截,配置注解如下

@Intercepts({@Signature(type=Executor.class,method="query",args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})})

intercept方法如下

@OverridepublicObjectintercept(Invocationinvocation)throwsThrowable{//若查詢參數(shù)不為分頁參數(shù),不作處理,按原計劃執(zhí)行if(!(invocation.getArgs()[1]instanceofPageQuery)) {returninvocation.proceed();}MappedStatementms=(MappedStatement)invocation.getArgs()[0];ListtextSqlNodes=(List)SystemMetaObject.forObject(ms).getValue("sqlSource.rootSqlNode.contents");if(textSqlNodes.lastIndexOf(mysqlPageNode)!=textSqlNodes.size()-1) {textSqlNodes.add(mysqlPageNode);}PageQuerypageQuery=(PageQuery)invocation.getArgs()[1];BoundSqlboundSql=ms.getBoundSql(pageQuery);//條件未繼承分頁查詢類PageQuery的sql按原有邏輯處理if(!(boundSql.getParameterObject()instanceofPageQuery)) {returninvocation.proceed();}? ? Executorexecutor=(Executor)invocation.getTarget();Connectionconnection=executor.getTransaction().getConnection();//查詢總條數(shù)會額外執(zhí)行一條sql,影響性能if(pageQuery.isNeedTotalCount()) {//查詢總條數(shù)inttotalCount=getPageQueryCount(connection,ms,boundSql,pageQuery);pageQuery.setTotalCount(totalCount);if(totalCount==0){returnCollections.EMPTY_LIST;}? ? }returninvocation.proceed();}

我們攔截的是

Listquery(MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler)

第二個參數(shù)即查詢的參數(shù),分頁時即繼承了PageQuery的類,若未繼承,則按原有邏輯處理;

mybatis會將一條sql劃分為許多塊,而textSqlNodes為這些塊的集合,

比如

selectfrom User u where username=#{username}

就被劃分成了

select

id,username

from User

where username=#{username}

四塊,而我們要做分頁,只需要在這個集合里面加入分頁數(shù)據(jù)即可

textSqlNodes.add(mysqlPageNode);

聲明如下

privatefinalTextSqlNodemysqlPageNode=newTextSqlNode(" limit #{offset},#{pageSize} ");

,如果是oracle,則在前后加上rownum的塊。

這里的pageSize和offset我們的rownum里面都有了的。

注意這個判斷

if(textSqlNodes.lastIndexOf(mysqlPageNode)!=textSqlNodes.size()-1)

只有第一次查詢分頁的方法時需要加,因為這個已經(jīng)寫進(jìn)內(nèi)存了,所以第二次調(diào)用時就不用加了,最后,如果你不需要總條數(shù),可以直接

returninvocation.proceed();

執(zhí)行被代理的方法,game over。

如果需要總條數(shù),則繼續(xù)往下看,調(diào)用方法getPageQueryCount查詢總條數(shù)

privateintgetPageQueryCount(Connectionconnection,MappedStatementms,BoundSqlboundSql,PageQuerypageQuery)throwsThrowable{//獲取綁定sqlStringsql=boundSql.getSql();//數(shù)據(jù)庫連接//查詢數(shù)據(jù)總條數(shù)PreparedStatementcountQuery=connection.prepareStatement(dialect.generateCountSql(sql));//記錄當(dāng)前頁數(shù)和每頁條數(shù)(查詢總條數(shù)時offset和limit要去掉)intpageSize=pageQuery.getPageSize();intpageNo=pageQuery.getPageNo();pageQuery.setPageNo(PageQuery.DEFAULT_PAGE_NO);pageQuery.setPageSize(RowBounds.NO_ROW_LIMIT);ParameterHandlerparameterHandler=newDefaultParameterHandler(ms,pageQuery,boundSql);parameterHandler.setParameters(countQuery);ResultSetrs=countQuery.executeQuery();inttotalCount=0;try{if(rs.next()) {? ? ? ? ? ? totalCount=rs.getInt(1);}? ? }finally{rs.close();countQuery.close();pageQuery.setPageNo(pageNo);pageQuery.setPageSize(pageSize);}returntotalCount;}

這里用PreparedStatemetn執(zhí)行預(yù)處理查詢總條數(shù),這里需要注意的是因為前面我們已經(jīng)加入了分頁條件,所以這里把分頁條件的值設(shè)置為最大。如果數(shù)據(jù)集為空,則直接返回空集合。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,908評論 2 372

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,713評論 18 399
  • 1. 簡介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,561評論 0 4
  • 分頁是項目中經(jīng)常會使用到的功能,我們可以自己手動在sql語句中進(jìn)行分頁,但是這樣比較麻煩,Mybatis的Page...
    就沒一個昵稱能用閱讀 11,875評論 0 3
  • 半盞酒《半盞酒》 作者:晨之旅 瓷杯闊,紅塵斟千盅, 戀得太滿,溢得心驚。 詩酒烈,浮世一場醉, 愛得太深,倒在雪...
    晨之旅閱讀 524評論 2 7
  • 環(huán)境配置 :OpenCV在xCode中的安裝與環(huán)境配置 處理效果 函數(shù)參數(shù)介紹 主要函數(shù)filter2Dvoid ...
    FLNuo閱讀 732評論 0 1