詳解Mybatis極其(最)簡(jiǎn)(好)單(用)的一個(gè)分頁(yè)插件
注意:這篇博客已經(jīng)和當(dāng)前的分頁(yè)插件完全不一樣了,所以建議大家通過(guò)上面項(xiàng)目地址查看最新的源碼和文檔來(lái)了解。
以前為Mybatis分頁(yè)查詢(xún)發(fā)愁過(guò),而且在網(wǎng)上搜過(guò)很多相關(guān)的文章,最后一個(gè)都沒(méi)采用。在分頁(yè)的地方完全都是手寫(xiě)分頁(yè)SQL和count的sql,總之很麻煩。
后來(lái)有一段時(shí)間想從Mybatis內(nèi)部寫(xiě)一個(gè)分頁(yè)的實(shí)現(xiàn),我對(duì)LanguageDriver寫(xiě)過(guò)一個(gè)實(shí)現(xiàn),自動(dòng)分頁(yè)是沒(méi)問(wèn)題了,但是查詢(xún)總數(shù)(count)仍然沒(méi)法一次性解決,最后不了了之。
最近又要用到分頁(yè),為了方便必須地寫(xiě)個(gè)通用的分頁(yè)類(lèi),因此又再次參考網(wǎng)上大多數(shù)的Mybatis分頁(yè)代碼。
實(shí)際上在很早之前,有人在github上開(kāi)源過(guò)一個(gè)實(shí)現(xiàn),支持MySQL,Oracle,sqlserver的,和上面這個(gè)參考的比較類(lèi)似,考慮的更全面。但是我覺(jué)得太多類(lèi)太麻煩了,所以自己實(shí)現(xiàn)了一個(gè)只有一個(gè)攔截器的類(lèi),實(shí)際上可以分為兩個(gè)類(lèi),其中一個(gè)類(lèi)被我寫(xiě)成靜態(tài)類(lèi)放在了攔截器中,你也可以將Page類(lèi)提取出來(lái),方便使用Page。
先說(shuō)實(shí)現(xiàn)方法,該插件只有一個(gè)類(lèi):PageHelper.Java
攔截器簽名為:
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}), @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
這里的簽名對(duì)整個(gè)實(shí)現(xiàn)和思想至關(guān)重要,首先我攔截prepare方法來(lái)改分頁(yè)SQL,來(lái)做count查詢(xún)。然后我攔截handleResultSets方法來(lái)獲取最后的處理結(jié)果,將結(jié)果放到Page對(duì)象中。
下面是修改分頁(yè)的代碼,是針對(duì)Oracle數(shù)據(jù)進(jìn)行的修改,如果有用其他數(shù)據(jù)庫(kù)的,自己修改這里的代碼就可以。
/** * 修改原SQL為分頁(yè)SQL * @param sql * @param page * @return */ private String buildPageSql(String sql, Page page) { StringBuilder pageSql = new StringBuilder(200); pageSql.append("select * from ( select temp.*, rownum row_id from ( "); pageSql.append(sql); pageSql.append(" ) temp where rownum <= ").append(page.getEndRow()); pageSql.append(") where row_id > ").append(page.getStartRow()); return pageSql.toString(); }
之后在下面的setPageParameter方法中一個(gè)selelct count語(yǔ)句,這里也需要根據(jù)數(shù)據(jù)庫(kù)類(lèi)型進(jìn)行修改:
// 記錄總記錄數(shù) String countSql = "select count(0) from (" + sql + ")";
為什么我不提供對(duì)各種數(shù)據(jù)庫(kù)的支持呢,我覺(jué)得沒(méi)必要,還有些數(shù)據(jù)庫(kù)不支持分頁(yè),而且這個(gè)插件越簡(jiǎn)單對(duì)使用的開(kāi)發(fā)人員來(lái)說(shuō)越容易理解,越容易修改。修改成自己需要的分頁(yè)查詢(xún)肯定不是問(wèn)題。
最后上完整代碼(繼續(xù)看下去,下面還有使用方法):(點(diǎn)擊下載)
package com.mybatis.util; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import org.apache.log4j.Logger; import java.sql.*; import java.util.List; import java.util.Properties; /** * Mybatis - 通用分頁(yè)攔截器 * @author liuzh/abel533/isea * Created by liuzh on 14-4-15. */ @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}), @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})}) public class PageHelper implements Interceptor { private static final Logger logger = Logger.getLogger(PageHelper.class); public static final ThreadLocal<Page> localPage = new ThreadLocal<Page>(); /** * 開(kāi)始分頁(yè) * @param pageNum * @param pageSize */ public static void startPage(int pageNum, int pageSize) { localPage.set(new Page(pageNum, pageSize)); } /** * 結(jié)束分頁(yè)并返回結(jié)果,該方法必須被調(diào)用,否則localPage會(huì)一直保存下去,直到下一次startPage * @return */ public static Page endPage() { Page page = localPage.get(); localPage.remove(); return page; } @Override public Object intercept(Invocation invocation) throws Throwable { if (localPage.get() == null) { return invocation.proceed(); } if (invocation.getTarget() instanceof StatementHandler) { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler); // 分離代理對(duì)象鏈(由于目標(biāo)類(lèi)可能被多個(gè)攔截器攔截,從而形成多次代理,通過(guò)下面的兩次循環(huán) // 可以分離出最原始的的目標(biāo)類(lèi)) while (metaStatementHandler.hasGetter("h")) { Object object = metaStatementHandler.getValue("h"); metaStatementHandler = SystemMetaObject.forObject(object); } // 分離最后一個(gè)代理對(duì)象的目標(biāo)類(lèi) while (metaStatementHandler.hasGetter("target")) { Object object = metaStatementHandler.getValue("target"); metaStatementHandler = SystemMetaObject.forObject(object); } MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement"); //分頁(yè)信息if (localPage.get() != null) { Page page = localPage.get(); BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql"); // 分頁(yè)參數(shù)作為參數(shù)對(duì)象parameterObject的一個(gè)屬性 String sql = boundSql.getSql(); // 重寫(xiě)sql String pageSql = buildPageSql(sql, page); //重寫(xiě)分頁(yè)sql metaStatementHandler.setValue("delegate.boundSql.sql", pageSql); Connection connection = (Connection) invocation.getArgs()[0]; // 重設(shè)分頁(yè)參數(shù)里的總頁(yè)數(shù)等 setPageParameter(sql, connection, mappedStatement, boundSql, page); // 將執(zhí)行權(quán)交給下一個(gè)攔截器 return invocation.proceed(); } else if (invocation.getTarget() instanceof ResultSetHandler) { Object result = invocation.proceed(); Page page = localPage.get(); page.setResult((List) result); return result; } return null; } /** * 只攔截這兩種類(lèi)型的 * StatementHandler * ResultSetHandler * @param target * @return */ @Override public Object plugin(Object target) { if (target instanceof StatementHandler || target instanceof ResultSetHandler) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { } /** * 修改原SQL為分頁(yè)SQL * @param sql * @param page * @return */ private String buildPageSql(String sql, Page page) { StringBuilder pageSql = new StringBuilder(200); pageSql.append("select * from ( select temp.*, rownum row_id from ( "); pageSql.append(sql); pageSql.append(" ) temp where rownum <= ").append(page.getEndRow()); pageSql.append(") where row_id > ").append(page.getStartRow()); return pageSql.toString(); } /** * 獲取總記錄數(shù) * @param sql * @param connection * @param mappedStatement * @param boundSql * @param page */ private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement, BoundSql boundSql, Page page) { // 記錄總記錄數(shù) String countSql = "select count(0) from (" + sql + ")"; PreparedStatement countStmt = null; ResultSet rs = null; try { countStmt = connection.prepareStatement(countSql); BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings(), boundSql.getParameterObject()); setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject()); rs = countStmt.executeQuery(); int totalCount = 0; if (rs.next()) { totalCount = rs.getInt(1); } page.setTotal(totalCount); int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 : 1); page.setPages(totalPage); } catch (SQLException e) { logger.error("Ignore this exception", e); } finally { try { rs.close(); } catch (SQLException e) { logger.error("Ignore this exception", e); } try { countStmt.close(); } catch (SQLException e) { logger.error("Ignore this exception", e); } } } /** * 代入?yún)?shù)值 * @param ps * @param mappedStatement * @param boundSql * @param parameterObject * @throws SQLException */ private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject) throws SQLException { ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql); parameterHandler.setParameters(ps); } /** * Description: 分頁(yè) * Author: liuzh * Update: liuzh(2014-04-16 10:56) */ public static class Page<E> { private int pageNum; private int pageSize; private int startRow; private int endRow; private long total; private int pages; private List<E> result; public Page(int pageNum, int pageSize) { this.pageNum = pageNum; this.pageSize = pageSize; this.startRow = pageNum > 0 ? (pageNum - 1) * pageSize : 0; this.endRow = pageNum * pageSize; } public List<E> getResult() { return result; } public void setResult(List<E> result) { this.result = result; } public int getPages() { return pages; } public void setPages(int pages) { this.pages = pages; } public int getEndRow() { return endRow; } public void setEndRow(int endRow) { this.endRow = endRow; } public int getPageNum() { return pageNum; } public void setPageNum(int pageNum) { this.pageNum = pageNum; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getStartRow() { return startRow; } public void setStartRow(int startRow) { this.startRow = startRow; } public long getTotal() { return total; } public void setTotal(long total) { this.total = total; } @Override public String toString() { return "Page{" + "pageNum=" + pageNum + ", pageSize=" + pageSize + ", startRow=" + startRow + ", endRow=" + endRow + ", total=" + total + ", pages=" + pages + '}'; } } }
使用該攔截器首先需要在Mybatis配置中配置該攔截器:
<plugins> <plugin interceptor="com.mybatis.util.PageHelper"></plugin> </plugins>
配置攔截器的時(shí)候需要注意plugins的位置,plugins位置順序如下:
properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?
最后是調(diào)用該方法的例子代碼(Service層):
@Override public PageHelper.Page<SysLoginLog> findSysLoginLog(String loginIp, String username, String loginDate, String exitDate, String logerr, int pageNumber, int pageSize) throws BusinessException { PageHelper.startPage(pageNumber,pageSize); sysLoginLogMapper.findSysLoginLog(loginIp, username, loginDate, exitDate, logerr); return PageHelper.endPage(); }
從上面可以看到使用該插件使用起來(lái)是很簡(jiǎn)單的,只需要在查詢(xún)前后使用PageHelper的startPage和endPage方法即可,中間代碼的調(diào)用結(jié)果已經(jīng)存在于Page的result中,如果你在一個(gè)返回一個(gè)結(jié)果的地方調(diào)用PageHelper,返回的結(jié)果仍然是一個(gè)List,取第一個(gè)值即可(我想沒(méi)人會(huì)在這種地方這么用,當(dāng)然這樣也不出錯(cuò))。
另外在startPage和endPage中間的所有mybatis代碼都會(huì)被分頁(yè),而且PageHelper只會(huì)保留最后一次的結(jié)果,因而使用時(shí)需要保證每次只在其中執(zhí)行一個(gè)mybatis查詢(xún),如果有多個(gè)分頁(yè),請(qǐng)多次使用startPage和endPage。
由于這里只提供了Oracle的實(shí)現(xiàn),所以我希望參考該分頁(yè)插件實(shí)現(xiàn)的其他數(shù)據(jù)庫(kù)的讀者也能將相應(yīng)的代碼開(kāi)源。
項(xiàng)目地址:http://xiazai.jb51.net/201612/yuanma/Mybatis_PageHelper_jb51.zip
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Mybatis分頁(yè)插件PageHelper的使用詳解
- Java簡(jiǎn)單實(shí)現(xiàn)SpringMVC+MyBatis分頁(yè)插件
- mybatis分頁(yè)插件pageHelper詳解及簡(jiǎn)單實(shí)例
- 自己動(dòng)手寫(xiě)的mybatis分頁(yè)插件(極其簡(jiǎn)單好用)
- Mybatis常用分頁(yè)插件實(shí)現(xiàn)快速分頁(yè)處理技巧
- spring boot和mybatis集成分頁(yè)插件
- SSM使用mybatis分頁(yè)插件pagehepler實(shí)現(xiàn)分頁(yè)示例
- SpringBoot集成MyBatis的分頁(yè)插件PageHelper實(shí)例代碼
- Mybatis分頁(yè)插件PageHelper的配置和簡(jiǎn)單使用方法(推薦)
- Mybatis分頁(yè)插件使用方法詳解
相關(guān)文章
mybatis中使用not?in與?in的寫(xiě)法說(shuō)明
這篇文章主要介紹了mybatis中使用not?in與?in的寫(xiě)法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01詳解Java的readBytes是怎么實(shí)現(xiàn)的
眾所周知,Java是一門(mén)跨平臺(tái)語(yǔ)言,針對(duì)不同的操作系統(tǒng)有不同的實(shí)現(xiàn),下面小編就來(lái)從一個(gè)非常簡(jiǎn)單的api調(diào)用帶大家來(lái)看看Java具體是怎么做的吧2023-07-07Java反射機(jī)制詳解_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
Java 反射機(jī)制。通俗來(lái)講呢,就是在運(yùn)行狀態(tài)中,我們可以根據(jù)“類(lèi)的部分已經(jīng)的信息”來(lái)還原“類(lèi)的全部的信息”。這篇文章給大家詳細(xì)介紹了java反射機(jī)制的知識(shí),感興趣的朋友一起看看吧2017-06-06javaweb圖書(shū)商城設(shè)計(jì)之用戶(hù)模塊(1)
這篇文章主要介紹了javaweb圖書(shū)商城設(shè)計(jì)之用戶(hù)模塊的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11java事件處理模型知識(shí)點(diǎn)總結(jié)
在本篇文章里小辮給大家分享的是一篇關(guān)于java事件處理模型知識(shí)點(diǎn)總結(jié)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-01-01三分鐘讀懂mybatis中resultMap和resultType區(qū)別
這篇文章主要給大家介紹了mybatis中resultMap和resultType區(qū)別的相關(guān)資料,resultType和resultMap都是mybatis進(jìn)行數(shù)據(jù)庫(kù)連接操作處理返回結(jié)果的,需要的朋友可以參考下2023-07-07java使用正則表達(dá)式判斷手機(jī)號(hào)的方法示例
這篇文章主要介紹了java使用正則表達(dá)式判斷手機(jī)號(hào)的方法,分析了手機(jī)號(hào)碼段的原理及java使用正則表達(dá)式針對(duì)手機(jī)號(hào)的匹配操作實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-06-06