MyBatis使用RowBounds實現的分頁是邏輯分頁,也就是先把數據記錄全部查詢出來,然在再根據offset和limit截斷記錄返回
為了在數據庫層面上實現物理分頁,又不改變原來MyBatis的函數邏輯,可以編寫plugin截獲MyBatis Executor的statementhandler,重寫SQL來執行查詢
下面的插件代碼只針對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語句實現物理分頁。 * 老規矩,簽名里要攔截的類型只能是接口。 * */@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"); // 分離代理對象鏈(由于目標類可能被多個攔截器攔截,從而形成多次代理,通過下面的兩次循環可以分離出最原始的的目標類) 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文件內配置 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結尾的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的內存分頁了,所以重置下面的兩個參數 metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET); metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT); } } // 將執行權交給下一個攔截器 return invocation.proceed(); } @Override public Object plugin(Object target) { // 當目標類是StatementHandler類型時,才包裝目標類,否者直接返回目標本身,減少目標被代理的次數 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>
調用示例
@Override public List<User> selectUserByPage(int offset, int limit) { RowBounds rowBounds = new RowBounds(offset, limit); return getSqlSession().selectList("dao.userdao.selectUserByPage", new Object(), rowBounds); }
另外,結合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; // 每頁顯示記錄數 private int currentPage = 1; // 當前頁碼 private int maxPage = Integer.MAX_VALUE; // 最大頁數 // 獲取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; }}
為了計算最大頁碼,需要知道數據表的總記錄數,查詢SQL如下
<!-- 記錄總數 --> <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"; }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。
新聞熱點
疑難解答