MyBatis實現(xiàn)物理分頁的實例
MyBatis使用RowBounds實現(xiàn)的分頁是邏輯分頁,也就是先把數(shù)據(jù)記錄全部查詢出來,然在再根據(jù)offset和limit截斷記錄返回
為了在數(shù)據(jù)庫層面上實現(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語句實現(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"); // 分離代理對象鏈(由于目標類可能被多個攔截器攔截,從而形成多次代理,通過下面的兩次循環(huán)可以分離出最原始的的目標類) while (metaStatementHandler.hasGetter("h")) { Object object = metaStatementHandler.getValue("h"); metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY); } // 分離最后一個代理對象的目標類 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"); // 設置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匹配,默認重寫以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)存分頁了,所以重置下面的兩個參數(shù) metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET); metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT); } } // 將執(zhí)行權交給下一個攔截器 return invocation.proceed(); } @Override public Object plugin(Object target) { // 當目標類是StatementHandler類型時,才包裝目標類,否者直接返回目標本身,減少目標被代理的次數(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; // 當前頁碼 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; } }
為了計算最大頁碼,需要知道數(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)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
關于JAVA_HOME路徑修改之后JDK的版本依然不更改的解決辦法
今天小編就為大家分享一篇關于JAVA_HOME路徑修改之后JDK的版本依然不更改的解決辦法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-04-04SpringBoot使用itext填充pdf表單及導出pdf的流程
由于最近開發(fā)的項目需要用到打印單據(jù),就在網(wǎng)上找了一下方案,反反復復,都沒有找到合適的,借鑒了網(wǎng)上資源,使用itext5、itext7的工具包,所以本文介紹了SpringBoot使用itext填充pdf表單及導出pdf的流程,需要的朋友可以參考下2024-09-09Mybatis-Plus設置全局或者局部ID自增的實現(xiàn)
在使用Mybatis-Plus新增的時候,我們往往想要id隨著數(shù)據(jù)庫自增,本文主要介紹了Mybatis-Plus設置全局或者局部ID自增的實現(xiàn),具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-01-01SpringBoot使用AOP實現(xiàn)日志記錄功能詳解
這篇文章主要為大家介紹了SpringBoot使用AOP實現(xiàn)日志記錄功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07java實現(xiàn)String類型和Date類型相互轉(zhuǎn)換
很多人表示,java將string類型轉(zhuǎn)為date類型不知道應該怎樣做,本文就來介紹一下java實現(xiàn)String類型和Date類型相互轉(zhuǎn)換,具有一定的參考價值,感興趣的可以了解一下2023-10-10