原文地址: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. php5.3 fastcgi方式 安装以及和nginx整合

    注意:不要随便改变基础库的版本,否则会对别的软件产生编译错误 1.基础依赖包安装: cd /software/zlib- ./configure make make install cd /softw ...

  2. Network Instructions in Linux

    I will gradually learn some instructions about Network in Linux. First, here are somethings about IP ...

  3. AC日记——接龙游戏 codevs 1051

    1051 接龙游戏  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond       题目描述 Description 给出了N个单词,已经按长度排好了序.如果 ...

  4. MipMap

    MipMap 首先从MIPMAP的原理说起,它是把一张贴图按照2的倍数进行缩小.直到1X1.把缩小的图都存储起来.在渲染时,根据一个像素离眼睛为之的距离,来判断从一个合适的图层中取出texel颜色赋值 ...

  5. HTML插入地图的方法

    方法/步骤 1.打开"百度地图生成器"的网址:http://api.map.baidu.com/lbsapi/creatmap/index.html 如下图: 2.在"1 ...

  6. linux下安装安装pcre-8.32 configure: error: You need a C++ compiler for C++ support

    linux下安装安装pcre-8.32./configure --prefix=/usr/local/pcre 出现以下错误configure: error: You need a C++ compi ...

  7. 使用 JavaScript File API 实现文件上传

    概述 以往对于基于浏览器的应用而言,访问本地文件都是一件头疼的事情.虽然伴随着 Web 2.0 应用技术的不断发展,JavaScript 正在扮演越来越重要的角色,但是出于安全性的考虑,JavaScr ...

  8. 启动Eclipse后卡在 android sdk content loader 的解决办法

    Make sure that eclipse is not active. If it is active kill eclipse from the processes tab of the tas ...

  9. Cordova - 常用的插件汇总(附插件的安装、查询、更新、删除等命令)

    Hybrid应用比web应用强大之处在于可以使运行在容器中的web内容访问 native APIs.Cordova 提供了许多插件用于调用移动设备上的API. 一,插件相关常用命令   1,查看所有已 ...

  10. office 2010 2013卸载工具

    http://www.ithome.com/html/soft/32777.htm Office 2003 || Office 2007 || Office 2010.