MyBatis實(shí)現(xiàn)物理分頁的實(shí)例
MyBatis使用RowBounds實(shí)現(xiàn)的分頁是邏輯分頁,也就是先把數(shù)據(jù)記錄全部查詢出來,然在再根據(jù)offset和limit截?cái)嘤涗浄祷?/p>
為了在數(shù)據(jù)庫層面上實(shí)現(xiàn)物理分頁,又不改變原來MyBatis的函數(shù)邏輯,可以編寫plugin截獲MyBatis Executor的statementhandler,重寫SQL來執(zhí)行查詢
下面的插件代碼只針對MySQL
plugin代碼
package plugin; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.factory.DefaultObjectFactory; import org.apache.ibatis.reflection.factory.ObjectFactory; import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory; import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.RowBounds; /** * 通過攔截<code>StatementHandler</code>的<code>prepare</code>方法,重寫sql語句實(shí)現(xiàn)物理分頁。 * 老規(guī)矩,簽名里要攔截的類型只能是接口。 * */ @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})}) public class PaginationInterceptor implements Interceptor { private static final Log logger = LogFactory.getLog(PaginationInterceptor.class); private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory(); private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory(); private static String DEFAULT_PAGE_SQL_ID = ".*Page$"; // 需要攔截的ID(正則匹配) @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY); RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds"); // 分離代理對象鏈(由于目標(biāo)類可能被多個(gè)攔截器攔截,從而形成多次代理,通過下面的兩次循環(huán)可以分離出最原始的的目標(biāo)類) while (metaStatementHandler.hasGetter("h")) { Object object = metaStatementHandler.getValue("h"); metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY); } // 分離最后一個(gè)代理對象的目標(biāo)類 while (metaStatementHandler.hasGetter("target")) { Object object = metaStatementHandler.getValue("target"); metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY); } // property在mybatis settings文件內(nèi)配置 Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration"); // 設(shè)置pageSqlId String pageSqlId = configuration.getVariables().getProperty("pageSqlId"); if (null == pageSqlId || "".equals(pageSqlId)) { logger.warn("Property pageSqlId is not setted,use default '.*Page$' "); pageSqlId = DEFAULT_PAGE_SQL_ID; } MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement"); // 只重寫需要分頁的sql語句。通過MappedStatement的ID匹配,默認(rèn)重寫以Page結(jié)尾的MappedStatement的sql if (mappedStatement.getId().matches(pageSqlId)) { BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql"); Object parameterObject = boundSql.getParameterObject(); if (parameterObject == null) { throw new NullPointerException("parameterObject is null!"); } else { String sql = boundSql.getSql(); // 重寫sql String pageSql = sql + " LIMIT " + rowBounds.getOffset() + "," + rowBounds.getLimit(); metaStatementHandler.setValue("delegate.boundSql.sql", pageSql); // 采用物理分頁后,就不需要mybatis的內(nèi)存分頁了,所以重置下面的兩個(gè)參數(shù) metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET); metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT); } } // 將執(zhí)行權(quán)交給下一個(gè)攔截器 return invocation.proceed(); } @Override public Object plugin(Object target) { // 當(dāng)目標(biāo)類是StatementHandler類型時(shí),才包裝目標(biāo)類,否者直接返回目標(biāo)本身,減少目標(biāo)被代理的次數(shù) if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { //To change body of implemented methods use File | Settings | File Templates. } }
配置plugin
<plugins> <plugin interceptor="plugin.PaginationInterceptor" /> </plugins>
查詢SQL
<!-- 測試分頁查詢 --> <select id="selectUserByPage" resultMap="dao.base.userResultMap"> <![CDATA[ SELECT * FROM user ]]> </select>
調(diào)用示例
@Override public List<User> selectUserByPage(int offset, int limit) { RowBounds rowBounds = new RowBounds(offset, limit); return getSqlSession().selectList("dao.userdao.selectUserByPage", new Object(), rowBounds); }
另外,結(jié)合Spring MVC,編寫翻頁和生成頁碼代碼
頁碼類
package util; /** * Created with IntelliJ IDEA. * User: zhenwei.liu * Date: 13-8-7 * Time: 上午10:29 * To change this template use File | Settings | File Templates. */ public class Pagination { private String url; // 頁碼url private int pageSize = 10; // 每頁顯示記錄數(shù) private int currentPage = 1; // 當(dāng)前頁碼 private int maxPage = Integer.MAX_VALUE; // 最大頁數(shù) // 獲取offset public int getOffset() { return (currentPage - 1) * pageSize; } // 獲取limit public int getLimit() { return getPageSize(); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getCurrentPage() { return currentPage; } public void setCurrentPage(int currentPage) { if (currentPage < 1) currentPage = 1; if (currentPage > maxPage) currentPage = maxPage; this.currentPage = currentPage; } public int getMaxPage() { return maxPage; } public void setMaxPage(int maxPage) { this.maxPage = maxPage; } }
為了計(jì)算最大頁碼,需要知道數(shù)據(jù)表的總記錄數(shù),查詢SQL如下
<!-- 記錄總數(shù) --> <select id="countUser" resultType="Integer"> <![CDATA[ SELECT COUNT(*) FROM user ]]> </select>
@Override public Integer countTable() { return getSqlSession().selectOne("dao.userdao.countUser"); }
Controller中的使用
@RequestMapping("/getUserByPage") public String getUserByPage(@RequestParam int page, Model model) { pagination.setCurrentPage(page); pagination.setUrl(getCurrentUrl()); pagination.setMaxPage(userDao.countTable() / pagination.getPageSize() + 1); List<User> userList = userDao.selectUserByPage( pagination.getOffset(), pagination.getLimit()); model.addAttribute(pagination); model.addAttribute(userList); return "index"; }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java簡單實(shí)現(xiàn)SpringMVC+MyBatis分頁插件
- Java的MyBatis框架中實(shí)現(xiàn)多表連接查詢和查詢結(jié)果分頁
- Mybatis實(shí)現(xiàn)增刪改查及分頁查詢的方法
- Mybatis分頁插件PageHelper的使用詳解
- Mybatis常用分頁插件實(shí)現(xiàn)快速分頁處理技巧
- 自己動(dòng)手寫的mybatis分頁插件(極其簡單好用)
- SpringMVC+MyBatis分頁(最新)
- 詳解Mybatis分頁插件 - 示例代碼
- SpringMvc+Mybatis+Pagehelper分頁詳解
- 詳解Mybatis極其(最)簡(好)單(用)的一個(gè)分頁插件
相關(guān)文章
關(guān)于JAVA_HOME路徑修改之后JDK的版本依然不更改的解決辦法
今天小編就為大家分享一篇關(guān)于JAVA_HOME路徑修改之后JDK的版本依然不更改的解決辦法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-04-04SpringBoot使用itext填充pdf表單及導(dǎo)出pdf的流程
由于最近開發(fā)的項(xiàng)目需要用到打印單據(jù),就在網(wǎng)上找了一下方案,反反復(fù)復(fù),都沒有找到合適的,借鑒了網(wǎng)上資源,使用itext5、itext7的工具包,所以本文介紹了SpringBoot使用itext填充pdf表單及導(dǎo)出pdf的流程,需要的朋友可以參考下2024-09-09詳解springboot項(xiàng)目啟動(dòng)時(shí)如何排除用不到的bean
使用springboot開發(fā)項(xiàng)目,我們有時(shí)候會(huì)排除一些項(xiàng)目里面用不到的bean,不然的話項(xiàng)目啟動(dòng)會(huì)報(bào)錯(cuò),這種情況通常是發(fā)生在什么場景里呢,以及如何解決呢,今天咱們就聊一聊2024-01-01java lambda 表達(dá)式中的雙冒號的用法說明 ::
這篇文章主要介紹了java lambda 表達(dá)式中的雙冒號的用法說明 ::具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09Mybatis-Plus設(shè)置全局或者局部ID自增的實(shí)現(xiàn)
在使用Mybatis-Plus新增的時(shí)候,我們往往想要id隨著數(shù)據(jù)庫自增,本文主要介紹了Mybatis-Plus設(shè)置全局或者局部ID自增的實(shí)現(xiàn),具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01SpringBoot使用AOP實(shí)現(xiàn)日志記錄功能詳解
這篇文章主要為大家介紹了SpringBoot使用AOP實(shí)現(xiàn)日志記錄功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07java實(shí)現(xiàn)String類型和Date類型相互轉(zhuǎn)換
很多人表示,java將string類型轉(zhuǎn)為date類型不知道應(yīng)該怎樣做,本文就來介紹一下java實(shí)現(xiàn)String類型和Date類型相互轉(zhuǎn)換,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10http中g(shù)et請求與post請求區(qū)別及如何選擇
這篇文章主要介紹了http中g(shù)et請求與post請求在應(yīng)用中應(yīng)該如何選擇,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2021-09-09