原文地址: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. Machine Learning Algorithms Study Notes(3)--Learning Theory

    Machine Learning Algorithms Study Notes 高雪松 @雪松Cedro Microsoft MVP 本系列文章是Andrew Ng 在斯坦福的机器学习课程 CS 22 ...

  2. 基于网格的波动方程模拟(Wave equation on mesh)附源码

    波动方程是偏微分方程 (PDE) 里的经典方程,它在物理学中有大量应用并经常用来解释空间中的能量传播.波动方程是一个依赖时间的方程,它解释了系统状态是如何随着时间的推移而发生变化.在下面模拟波动方程时 ...

  3. Collider Collision 区别

    Collision 中带有碰撞的信息,例如:速度和撞击到的点 示例 void OnCollisionEnter2D(Collision2D coll) { foreach(ContactPoint c ...

  4. JIRA学习一:Windows下安装破解JIRA6.3.6

    安装环境: WindowsXP MySQL-5.5.28 JDK1.6.0_21 JIRA功能全面,界面友好,安装简单,配置灵活,权限管理以及可扩展性方面都十分出色. 一.MySQL建库和建账号 1. ...

  5. Maven学习(八)继承和聚合

    *聚合(多模块) 在一个项目中 往往有多个模块组成,例如有项目demo下面有a, b两个模块 为了能使用一条命令就能构建demo-a, demo-b两个模块, 需要创建一个额外的聚合模块, 然后通过该 ...

  6. Eclipse启动时布局不合理调整

    1. 关掉 启动页 2. 关掉InstSearch页 3.修正InSearch布局 3.1 默认InstSearch不合理,影响使用. 3.2 Inst 搜索一次,然后最小化InstSearch框,再 ...

  7. 10 Things Every Java Programmer Should Know about String

    String in Java is very special class and most frequently used class as well. There are lot many thin ...

  8. Meet Python: little notes

    Source: http://www.liaoxuefeng.com/ ❤ Escape character: '\' - '\n': newline; - '\t': tab; - '\\': \; ...

  9. MVC HTTP 错误 403.14 - Forbidden

    HTTP 错误 403.14 - Forbidden Web 服务器被配置为不列出此目录的内容. 最可能的原因: 没有为请求的 URL 配置默认文档,并且没有在服务器上启用目录浏览. 可尝试的操作: ...

  10. squid代理服务器根据代理IP路由

    import os ips = os.popen("""ifconfig |grep 'inet addr:'|awk '{print $2}'| sed '$d'| s ...