原文地址: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. 第14章 位图和位块传输_14.4 GDI位图对象(2)

    14.4.7 在位图上绘图 (1)在内存设备环境中绘图(与真实DC不同的是,内存DC的显示表面是个位图) (2)GetTextExtentPoint32函数:用于确定文本字符串的像素大小.(此大小就是 ...

  2. 05章 OGNL

    一.OGNL全称是Object Graph Navigation Language,即对象导航图语言 OGNL在框架中主要做两件事情:表达式语言和类型转换器 OGNL在框架中的作用以及数据的流入流出: ...

  3. 银狐云服务架构V0.1

    首先这套程序是由JAVA开发的,为什么使用JAVA? 因为在本人的几年开发经历中,JDK在各种服务器上安装都很顺利, JAVA程序运行稳定,接口名不经常变动等,各种开源类库提升开发效率,给本人以信心, ...

  4. ios客户端快速滚动和回弹效果的实现

    现在很多for Mobile的HTML5网页内都有快速滚动和回弹的效果,看上去和原生app的效率都有得一拼. 要实现这个效果很简单,只需要给容器加一行css代码即可 -webkit-overflow- ...

  5. swift基本用法-数组array

    数组简单用法 //------------------------------------------------------------------------------ // 1. 数组定义 / ...

  6. Castle IOC容器与Spring.NET配置之比较

    我本人对于Spring.NET并不了解,本文只是通过一个简单的例子来比较一下两者配置之间的区别.在Castle IOC容器中,提出了自动装配(Auto-Wiring)的概念,即由容器自动管理组件之间的 ...

  7. Java操作符

    几乎所有运算符都只能操作"主类型"(Primitives).例外是"="."= ="和"! =",它们能操作所有对象.除 ...

  8. 简易安装python统计包

    PythonCharm简易安装python统计包及 本文介绍使用pythonCharm IDE 来安装Python统计包或一些packages的简单过程,基本无任何技术难度,顺便提一提笔者在安装过程中 ...

  9. android:ToolBar详解(手把手教程)(转)

    来源 http://blog.mosil.biz/2014/10/android-toolbar/ 编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅 ...

  10. NOI2018准备 Day11

    今天7点半到9点我都不知道自己在干啥, 一共A了3道题,2道钻石,1道大师. 下午调一道线段树3个小时没调出来,一个单调栈2小时没搞出来...... 学了个算法:求极大子矩阵. 昨天定的目标是学指针, ...