原文地址:http://blog.csdn.net/isea533/article/details/23831273

分页插件示例:http://blog.csdn.net/isea533/article/details/24700339

最新版分页插件:http://blog.csdn.net/isea533/article/details/25505413

项目地址:http://git.oschina.net/free/Mybatis_PageHelper

以前为Mybatis分页查询发愁过,而且在网上搜过很多相关的文章,最后一个都没采用。在分页的地方完全都是手写分页SQL和count的sql,总之很麻烦。

后来有一段时间想从Mybatis内部写一个分页的实现,我对LanguageDriver写过一个实现,自动分页是没问题了,但是查询总数(count)仍然没法一次性解决,最后不了了之。

最近又要用到分页,为了方便必须地写个通用的分页类,因此又再次参考网上大多数的Mybatis分页代码,本插件主要参考自:

http://blog.csdn.net/hupanfeng/article/details/9265341

实际上在很早之前,有人在github上开源过一个实现,支持mysql,oracle,sqlserver的,和上面这个参考的比较类似,考虑的
更全面。但是我觉得太多类太麻烦了,所以自己实现了一个只有一个拦截器的类,实际上可以分为两个类,其中一个类被我写成静态类放在了拦截器中,你也可以将
Page类提取出来,方便使用Page。

先说实现方法,该插件只有一个类:PageHelper.java

拦截器签名为:

  1. @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}),
  2. @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})

这里的签名对整个实现和思想至关重要,首先我拦截prepare方法来改分页SQL,来做count查询。然后我拦截handleResultSets方法来获取最后的处理结果,将结果放到Page对象中。

下面是修改分页的代码,是针对Oracle数据进行的修改,如果有用其他数据库的,自己修改这里的代码就可以。

  1. /**
  2. * 修改原SQL为分页SQL
  3. * @param sql
  4. * @param page
  5. * @return
  6. */
  7. private String buildPageSql(String sql, Page page) {
  8. StringBuilder pageSql = new StringBuilder(200);
  9. pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
  10. pageSql.append(sql);
  11. pageSql.append(" ) temp where rownum <= ").append(page.getEndRow());
  12. pageSql.append(") where row_id > ").append(page.getStartRow());
  13. return pageSql.toString();
  14. }

之后在下面的setPageParameter方法中一个selelct count语句,这里也需要根据数据库类型进行修改:

  1. // 记录总记录数
  2. String countSql = "select count(0) from (" + sql + ")";

为什么我不提供对各种数据库的支持呢,我觉得没必要,还有些数据库不支持分页,而且这个插件越简单对使用的开发人员来说越容易理解,越容易修改。修改成自己需要的分页查询肯定不是问题。

最后上完整代码(继续看下去,下面还有使用方法):(点击下载

  1. package com.mybatis.util;
  2. import org.apache.ibatis.executor.parameter.ParameterHandler;
  3. import org.apache.ibatis.executor.resultset.ResultSetHandler;
  4. import org.apache.ibatis.executor.statement.StatementHandler;
  5. import org.apache.ibatis.mapping.BoundSql;
  6. import org.apache.ibatis.mapping.MappedStatement;
  7. import org.apache.ibatis.plugin.*;
  8. import org.apache.ibatis.reflection.MetaObject;
  9. import org.apache.ibatis.reflection.SystemMetaObject;
  10. import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
  11. import org.apache.log4j.Logger;
  12. import java.sql.*;
  13. import java.util.List;
  14. import java.util.Properties;
  15. /**
  16. * Mybatis - 通用分页拦截器
  17. * @author liuzh/abel533/isea
  18. * Created by liuzh on 14-4-15.
  19. */
  20. @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}),
  21. @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
  22. public class PageHelper implements Interceptor {
  23. private static final Logger logger = Logger.getLogger(PageHelper.class);
  24. public static final ThreadLocal<Page> localPage = new ThreadLocal<Page>();
  25. /**
  26. * 开始分页
  27. * @param pageNum
  28. * @param pageSize
  29. */
  30. public static void startPage(int pageNum, int pageSize) {
  31. localPage.set(new Page(pageNum, pageSize));
  32. }
  33. /**
  34. * 结束分页并返回结果,该方法必须被调用,否则localPage会一直保存下去,直到下一次startPage
  35. * @return
  36. */
  37. public static Page endPage() {
  38. Page page = localPage.get();
  39. localPage.remove();
  40. return page;
  41. }
  42. @Override
  43. public Object intercept(Invocation invocation) throws Throwable {
  44. if (localPage.get() == null) {
  45. return invocation.proceed();
  46. }
  47. if (invocation.getTarget() instanceof StatementHandler) {
  48. StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
  49. MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
  50. // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环
  51. // 可以分离出最原始的的目标类)
  52. while (metaStatementHandler.hasGetter("h")) {
  53. Object object = metaStatementHandler.getValue("h");
  54. metaStatementHandler = SystemMetaObject.forObject(object);
  55. }
  56. // 分离最后一个代理对象的目标类
  57. while (metaStatementHandler.hasGetter("target")) {
  58. Object object = metaStatementHandler.getValue("target");
  59. metaStatementHandler = SystemMetaObject.forObject(object);
  60. }
  61. MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
  62. //分页信息if (localPage.get() != null) {
  63. Page page = localPage.get();
  64. BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
  65. // 分页参数作为参数对象parameterObject的一个属性
  66. String sql = boundSql.getSql();
  67. // 重写sql
  68. String pageSql = buildPageSql(sql, page);
  69. //重写分页sql
  70. metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
  71. Connection connection = (Connection) invocation.getArgs()[0];
  72. // 重设分页参数里的总页数等
  73. setPageParameter(sql, connection, mappedStatement, boundSql, page);
  74. // 将执行权交给下一个拦截器
  75. return invocation.proceed();
  76. } else if (invocation.getTarget() instanceof ResultSetHandler) {
  77. Object result = invocation.proceed();
  78. Page page = localPage.get();
  79. page.setResult((List) result);
  80. return result;
  81. }
  82. return null;
  83. }
  84. /**
  85. * 只拦截这两种类型的
  86. * <br>StatementHandler
  87. * <br>ResultSetHandler
  88. * @param target
  89. * @return
  90. */
  91. @Override
  92. public Object plugin(Object target) {
  93. if (target instanceof StatementHandler || target instanceof ResultSetHandler) {
  94. return Plugin.wrap(target, this);
  95. } else {
  96. return target;
  97. }
  98. }
  99. @Override
  100. public void setProperties(Properties properties) {
  101. }
  102. /**
  103. * 修改原SQL为分页SQL
  104. * @param sql
  105. * @param page
  106. * @return
  107. */
  108. private String buildPageSql(String sql, Page page) {
  109. StringBuilder pageSql = new StringBuilder(200);
  110. pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
  111. pageSql.append(sql);
  112. pageSql.append(" ) temp where rownum <= ").append(page.getEndRow());
  113. pageSql.append(") where row_id > ").append(page.getStartRow());
  114. return pageSql.toString();
  115. }
  116. /**
  117. * 获取总记录数
  118. * @param sql
  119. * @param connection
  120. * @param mappedStatement
  121. * @param boundSql
  122. * @param page
  123. */
  124. private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement,
  125. BoundSql boundSql, Page page) {
  126. // 记录总记录数
  127. String countSql = "select count(0) from (" + sql + ")";
  128. PreparedStatement countStmt = null;
  129. ResultSet rs = null;
  130. try {
  131. countStmt = connection.prepareStatement(countSql);
  132. BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
  133. boundSql.getParameterMappings(), boundSql.getParameterObject());
  134. setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject());
  135. rs = countStmt.executeQuery();
  136. int totalCount = 0;
  137. if (rs.next()) {
  138. totalCount = rs.getInt(1);
  139. }
  140. page.setTotal(totalCount);
  141. int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 : 1);
  142. page.setPages(totalPage);
  143. } catch (SQLException e) {
  144. logger.error("Ignore this exception", e);
  145. } finally {
  146. try {
  147. rs.close();
  148. } catch (SQLException e) {
  149. logger.error("Ignore this exception", e);
  150. }
  151. try {
  152. countStmt.close();
  153. } catch (SQLException e) {
  154. logger.error("Ignore this exception", e);
  155. }
  156. }
  157. }
  158. /**
  159. * 代入参数值
  160. * @param ps
  161. * @param mappedStatement
  162. * @param boundSql
  163. * @param parameterObject
  164. * @throws SQLException
  165. */
  166. private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
  167. Object parameterObject) throws SQLException {
  168. ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
  169. parameterHandler.setParameters(ps);
  170. }
  171. /**
  172. * Description: 分页
  173. * Author: liuzh
  174. * Update: liuzh(2014-04-16 10:56)
  175. */
  176. public static class Page<E> {
  177. private int pageNum;
  178. private int pageSize;
  179. private int startRow;
  180. private int endRow;
  181. private long total;
  182. private int pages;
  183. private List<E> result;
  184. public Page(int pageNum, int pageSize) {
  185. this.pageNum = pageNum;
  186. this.pageSize = pageSize;
  187. this.startRow = pageNum > 0 ? (pageNum - 1) * pageSize : 0;
  188. this.endRow = pageNum * pageSize;
  189. }
  190. public List<E> getResult() {
  191. return result;
  192. }
  193. public void setResult(List<E> result) {
  194. this.result = result;
  195. }
  196. public int getPages() {
  197. return pages;
  198. }
  199. public void setPages(int pages) {
  200. this.pages = pages;
  201. }
  202. public int getEndRow() {
  203. return endRow;
  204. }
  205. public void setEndRow(int endRow) {
  206. this.endRow = endRow;
  207. }
  208. public int getPageNum() {
  209. return pageNum;
  210. }
  211. public void setPageNum(int pageNum) {
  212. this.pageNum = pageNum;
  213. }
  214. public int getPageSize() {
  215. return pageSize;
  216. }
  217. public void setPageSize(int pageSize) {
  218. this.pageSize = pageSize;
  219. }
  220. public int getStartRow() {
  221. return startRow;
  222. }
  223. public void setStartRow(int startRow) {
  224. this.startRow = startRow;
  225. }
  226. public long getTotal() {
  227. return total;
  228. }
  229. public void setTotal(long total) {
  230. this.total = total;
  231. }
  232. @Override
  233. public String toString() {
  234. return "Page{" +
  235. "pageNum=" + pageNum +
  236. ", pageSize=" + pageSize +
  237. ", startRow=" + startRow +
  238. ", endRow=" + endRow +
  239. ", total=" + total +
  240. ", pages=" + pages +
  241. '}';
  242. }
  243. }
  244. }

使用该拦截器首先需要在Mybatis配置中配置该拦截器:

  1. <plugins>
  2. <plugin interceptor="com.mybatis.util.PageHelper"></plugin>
  3. </plugins>

配置拦截器的时候需要注意plugins的位置,plugins位置顺序如下:

  1. properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?

最后是调用该方法的例子代码(Service层):

  1. @Override
  2. public PageHelper.Page<SysLoginLog> findSysLoginLog(String loginIp,
  3. String username,
  4. String loginDate,
  5. String exitDate,
  6. String logerr,
  7. int pageNumber,
  8. int pageSize) throws BusinessException {
  9. PageHelper.startPage(pageNumber,pageSize);
  10. sysLoginLogMapper.findSysLoginLog(loginIp, username, loginDate, exitDate, logerr);
  11. return PageHelper.endPage();
  12. }

从上面可以看到使用该插件使用起来是很简单的,只需要在查询前后使用PageHelper的startPage和endPage方法即可,中间代码
的调用结果已经存在于Page的result中,如果你在一个返回一个结果的地方调用PageHelper,返回的结果仍然是一个List,取第一个值即
可(我想没人会在这种地方这么用,当然这样也不出错)。

另外在startPage和endPage中间的所有mybatis代码都会被分页,而且PageHelper只会保留最后一次的结果,因而使用时需要保证每次只在其中执行一个mybatis查询,如果有多个分页,请多次使用startPage和endPage。

最新文章

  1. .NET Core性能测试组件BenchmarkDotNet 支持.NET Framework Mono
  2. DBCC SHRINKDATABASE xxxx was skipped because the file does not have enough free
  3. OpenGL ES2.0基础入门
  4. Razor 在JS中嵌入后台变量
  5. Python读取Yaml文件
  6. 通过demo搞懂encode_utf8和decode_utf8
  7. Spring Data Jpa+SpringMVC+Jquery.pagination.js实现分页
  8. echarts词云引用
  9. Flask 扩展 国际化 本地化
  10. 【Unity Shaders】Diffuse Shading——创建一个自定义的diffuse lighting model(漫反射光照模型)
  11. Java之动手动脑(三)
  12. 【Checkio Exercise】Probably Dice
  13. Aria2+百度网盘 无限制的下载神器
  14. 鸟哥的Linux私房菜:基础学习篇 —— 第六章笔记
  15. 最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一)
  16. 编程算法 - 左旋转字符串 代码(C)
  17. apt-spy来获得适合自己的源,适用于UBUNTU/Debian
  18. 关于python接口基础到进阶随笔
  19. React-Navigation与Redux整合详解
  20. wampserver 安装后 启动失败的解决方法

热门文章

  1. java使用IO读写文件总结
  2. AC日记——ISBN号码 openjudge 1.7 29
  3. Github 下载单个文件
  4. CSS垂直居中精华总结
  5. QuickFix/J 源代码
  6. maven-安装配置
  7. 那些强悍的PHP一句话后门
  8. Linux—C内存管理
  9. 在VS2015中用C++创建DLL并用C#调用且同时实现对DLL的调试
  10. php配置rewrite模块