原文地址: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。

[转]Mybatis极其(最)简(好)单(用)的一个分页插件的更多相关文章

  1. SpringBoot入门篇--整合mybatis+generator自动生成代码+druid连接池+PageHelper分页插件

    原文链接 我们这一篇博客讲的是如何整合Springboot和Mybatis框架,然后使用generator自动生成mapper,pojo等文件.然后再使用阿里巴巴提供的开源连接池druid,这个连接池 ...

  2. 【spring boot】14.spring boot集成mybatis,注解方式OR映射文件方式AND pagehelper分页插件【Mybatis】pagehelper分页插件分页查询无效解决方法

    spring boot集成mybatis,集成使用mybatis拖沓了好久,今天终于可以补起来了. 本篇源码中,同时使用了Spring data JPA 和 Mybatis两种方式. 在使用的过程中一 ...

  3. SpringBoot+MyBatis多数据源使用分页插件PageHelper

    之前只用过单数据源下的分页插件,而且几乎不用配置.一个静态方法就能搞定. PageHelper.startPage(pageNum, pageSize); 后来使用了多数据源(不同的数据库),Page ...

  4. (转)淘淘商城系列——MyBatis分页插件(PageHelper)的使用以及商品列表展示

    http://blog.csdn.net/yerenyuan_pku/article/details/72774381 上文我们实现了展示后台页面的功能,而本文我们实现的主要功能是展示商品列表,大家要 ...

  5. Mybatis插件原理和PageHelper结合实战分页插件(七)

    今天和大家分享下mybatis的一个分页插件PageHelper,在讲解PageHelper之前我们需要先了解下mybatis的插件原理.PageHelper 的官方网站:https://github ...

  6. Mybatis插件原理分析(三)分页插件

    在Mybatis中插件最经常使用的是作为分页插件,接下来我们通过实现Interceptor来完成一个分页插件. 虽然Mybatis也提供了分页操作,通过在sqlSession的接口函数中设置RowBo ...

  7. Springboot 系列(十二)使用 Mybatis 集成 pagehelper 分页插件和 mapper 插件

    前言 在 Springboot 系列文章第十一篇里(使用 Mybatis(自动生成插件) 访问数据库),实验了 Springboot 结合 Mybatis 以及 Mybatis-generator 生 ...

  8. 【MyBatis】MyBatis分页插件PageHelper的使用

    好多天没写博客了,因为最近在实习,大部分时间在熟悉实习相关的东西,也没有怎么学习新的东西,这周末学习了MyBatis的一个分页插件PageHelper,虽然没有那么的强大(我在最后会说明它的缺点),但 ...

  9. MyBatis Generator实现MySQL分页插件

    MyBatis Generator是一个非常方便的代码生成工具,它能够根据表结构生成CRUD代码,可以满足大部分需求.但是唯一让人不爽的是,生成的代码中的数据库查询没有分页功能.本文介绍如何让MyBa ...

随机推荐

  1. 最小生成树POJ3522 Slim Span[kruskal]

    Slim Span Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 7594   Accepted: 4029 Descrip ...

  2. using关键字的用法

    1.using 关键字可以用来导命名空间,与java中的import类似. 2.using关键字可以用来释放资源,并且被释放的资源代码是非托管代码. 既然可以释放非托管代码,那么什么是非托管代码? 我 ...

  3. 在公司内网上创建自己的 OSM.Planet 街道级别地图服务器及其客户端程序

    转自我的BLOG http://blog.csdn.net/goldenhawking/article/details/6402775  最近经过陛下点拨,涉猎了“OpenStreetMap”,做了不 ...

  4. iOS远程推送1

    一.APNS 远程推送 1.所有的苹果设备,在联网状态下,都会与苹果服务器建立长连接. 2.长连接:就是只要联网了,就一直建立连接. 3.长连接的作用:时间校准,系统升级,查找我的iPhone. 4. ...

  5. Mysql优化系列(0)--总结性梳理

    对于一个网站来说,在运行很长一段时间后,数据库瓶颈问题会越来越暴露出来.作为运维人员,对数据库做必要的优化十分重要!下面总结以往查阅到的以及自己工作中的一些优化操作经验,并根据OSI七层模型从下往上进 ...

  6. 用Docker封装一个web应用(Django)

    一.复用以前一个封装了SSH的镜像,如果没有封装SSH,可以使用自己的镜像或参考我以前博文:叫板OpenStack:用Docker实现私有云 的前五步 接下来便是正题. 二.部署过程 1.查看镜像 R ...

  7. html代码中显示系统时间

    可以显示系统的静态时间和动态时间 1,静态时间 <script type="text/javascript"> var myDate = new Date(); doc ...

  8. Javascript Math.ceil()与Math.round()与Math.floor()区别

    Math.ceil()向上舍入 1 2 3 alert(Math.ceil(20.1)) //输出 21 alert(Math.ceil(20.5)) //输出 21 alert(Math.ceil( ...

  9. ZooKeeper 笔记(2) 监听数据变化

    ZK中的每个节点都可以存储一些轻量级的数据,这些数据的变化会同步到集群中的其它机器.在应用中程序员可以添加watcher来监听这些数据的变化,watcher只会触发一次,所以触发过后想要继续监听,必须 ...

  10. 读书摘要,一种新的黑客文化:programming is forgetting

    http://opentranscripts.org/transcript/programming-forgetting-new-hacker-ethic/ 这篇文章非常有意思,作者是一个计算机教师, ...