第一个做法,就是直接使用我们的sql语句进行分页,也就是在mapper里面加上分页的语句就好了。

  1. <select id="" parameterType="" resultType="" resultMap="">
  2. Select ROWNUM,ID,NAME FROM(Select ROWNUM as ROWNO, ID,NAME from CHANGED_CONTENT
  3. <where>
  4. <![CDATA[ROWNUM <= #{endRow}]]>
  5. </where>
  6. )
  7. <where>
  8. <![CDATA[ROWNO > #{startRow}]]>
  9. </where>
  10. </select>

然后我们在使用这个dao的时候,传入我们的分页的参数,就可以实现我们的分页需求了。其实很简单。


第二个做法,是一个更为通用的做法,那就是利用mybatis的拦截器,拦截每一个sql,在需要分页的sql加上分页的语句,就可以实现我们的分页功能。

我们可以查看 org.mybatis.spring.SqlSessionFactoryBean,其实他有一个plugin属性,我们可以配置实现一个插件来实现我们的需求。

首先说明一下实现的流程

例如我们有一条sql

String sql =select * from tableXXX where a=xxxx

我们的 拦截器拦截这条sql,然后判断是否是需要分页的,然后将这条sql转化为分页的sql

  1. <span style="font-family: 微软雅黑; background-color: rgb(255, 255, 255);">    String countSql = "select count(1) from (" + sql    + ") tmp_count"  </span>
这样我们就可以得到分页的总页数了。

然后, 拼上我们传入的分页数据,哪一页,每一页几条数据:

select * from ( select row_.*, rownum rownum_ from (   sql        ) row_ ) where rownum_ <=  endString   and rownum_ >  offsetPlaceholder

这样我们就可以得到我们本页的返回了。

具体的代码如下:

首先是实现了ibatis intercepter的pageplugin
  1. public class PagePlugin implements Interceptor {
  2. private static Logger log = Logger.getLogger("page plugin");
  3. private static Dialect dialectObject = null; // 数据库方言
  4. private static String pageSqlId = ""; // mybaits的数据库xml映射文件中需要拦截的ID(正则匹配)
  5. public Object intercept(Invocation ivk) throws Throwable {
  6. if (ivk.getTarget() instanceof RoutingStatementHandler) {
  7. log.info("com.dragon.dao.pulgin.mybatis.plugin.PagePlugin.intercept() enter*****************");
  8. RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk
  9. .getTarget();
  10. BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper
  11. .getValueByFieldName(statementHandler, "delegate");
  12. MappedStatement mappedStatement = (MappedStatement) ReflectHelper
  13. .getValueByFieldName(delegate, "mappedStatement");
  14. /**
  15. * 方法1:通过ID来区分是否需要分页..*query.* 方法2:传入的参数是否有page参数,如果有,则分页,
  16. */
  17. if (mappedStatement.getId().matches(pageSqlId)) { // 拦截需要分页的SQL
  18. BoundSql boundSql = delegate.getBoundSql();
  19. Object parameterObject = boundSql.getParameterObject();// 分页SQL<select>中parameterType属性对应的实体参数,即Mapper接口中执行分页方法的参数,该参数不得为空
  20. if (parameterObject == null) {
  21. //throw new NullPointerException("boundSql.getParameterObject() is null!");
  22. return ivk.proceed();
  23. } else {
  24. PageView pageView = null;
  25. if (parameterObject instanceof PageView) { // 参数就是Pages实体
  26. pageView = (PageView) parameterObject;
  27. } else if (parameterObject instanceof Map) {
  28. for (Entry entry : (Set<Entry>) ((Map) parameterObject).entrySet()) {
  29. if (entry.getValue() instanceof PageView) {
  30. pageView = (PageView) entry.getValue();
  31. break;
  32. }
  33. }
  34. } else { // 参数为某个实体,该实体拥有Pages属性
  35. pageView = ReflectHelper.getValueByFieldType(
  36. parameterObject, PageView.class);
  37. if (pageView == null) {
  38. return ivk.proceed();
  39. }
  40. }
  41. String sql = boundSql.getSql();
  42. PreparedStatement countStmt = null;
  43. ResultSet rs = null;
  44. try {
  45. Connection connection = (Connection) ivk.getArgs()[0];
  46. String countSql = "select count(1) from (" + sql
  47. + ") tmp_count"; // 记录统计
  48. countStmt = connection.prepareStatement(countSql);
  49. ReflectHelper.setValueByFieldName(boundSql, "sql",
  50. countSql);
  51. DefaultParameterHandler parameterHandler = new DefaultParameterHandler(
  52. mappedStatement, parameterObject, boundSql);
  53. parameterHandler.setParameters(countStmt);
  54. rs = countStmt.executeQuery();
  55. Long count = 0L;
  56. if (rs.next()) {
  57. count = ((Number) rs.getObject(1)).longValue();
  58. }
  59. pageView.setRowCount(count);
  60. } finally {
  61. try {
  62. rs.close();
  63. } catch (Exception e) {
  64. }
  65. try {
  66. countStmt.close();
  67. } catch (Exception e) {
  68. }
  69. }
  70. String pageSql = generatePagesSql(sql, pageView);
  71. ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql); // 将分页sql语句反射回BoundSql.
  72. }
  73. }
  74. }
  75. return ivk.proceed();
  76. }
  77. /**
  78. * 根据数据库方言,生成特定的分页sql
  79. *
  80. * @param sql
  81. * @param page
  82. * @return
  83. */
  84. private String generatePagesSql(String sql, PageView page) {
  85. if (page != null && dialectObject != null) {
  86. //pageNow默认是从1,而已数据库是从0开始计算的.所以(page.getPageNow()-1)
  87. int pageNow = page.getPageNow();
  88. return dialectObject.getLimitString(sql, (pageNow <= 0 ? 0 : pageNow - 1)
  89. * page.getPageSize(), page.getPageSize());
  90. }
  91. return sql;
  92. }
  93. public Object plugin(Object target) {
  94. return Plugin.wrap(target, this);
  95. }
  96. public void setProperties(Properties p) {
  97. String dialect = ""; // 数据库方言
  98. dialect = p.getProperty("dialect");
  99. if (StringUtils.isBlank(dialect)) {
  100. try {
  101. throw new PropertyException("dialect property is not found!");
  102. } catch (PropertyException e) {
  103. log.error(e);
  104. }
  105. } else {
  106. try {
  107. dialectObject = (Dialect) Class.forName(dialect)
  108. .getDeclaredConstructor().newInstance();
  109. } catch (Exception e) {
  110. throw new RuntimeException(dialect + ", init fail!\n" + e);
  111. }
  112. }
  113. pageSqlId = p.getProperty("pageSqlId");//根据id来区分是否需要分页
  114. if (StringUtils.isBlank(pageSqlId)) {
  115. try {
  116. throw new PropertyException("pageSqlId property is not found!");
  117. } catch (PropertyException e) {
  118. log.error(e);
  119. }
  120. }
  121. }
  122. }

然后是实现了dialect的oracledialect 数据库方言让我们区分不同的数据库不同的sql语句

  1. public class OracleDialect extends Dialect {
  2. public boolean supportsLimit() {
  3. return true;
  4. }
  5. public boolean supportsLimitOffset() {
  6. return true;
  7. }
  8. public String getLimitString(String sql, int offset, String offsetPlaceholder, int limit, String limitPlaceholder) {
  9. sql = sql.trim();
  10. boolean isForUpdate = false;
  11. if (sql.toLowerCase().endsWith(" for update")) {
  12. sql = sql.substring(0, sql.length() - 11);
  13. isForUpdate = true;
  14. }
  15. StringBuffer pagingSelect = new StringBuffer(sql.length() + 100);
  16. if (offset > 0) {
  17. pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
  18. } else {
  19. pagingSelect.append("select * from ( ");
  20. }
  21. pagingSelect.append(sql);
  22. if (offset > 0) {
  23. // int end = offset+limit;
  24. String endString = offsetPlaceholder + "+" + limitPlaceholder;
  25. pagingSelect.append(" ) row_ ) where rownum_ <= " + endString + " and rownum_ > " + offsetPlaceholder);
  26. } else {
  27. pagingSelect.append(" ) where rownum <= " + limitPlaceholder);
  28. }
  29. if (isForUpdate) {
  30. pagingSelect.append(" for update");
  31. }
  32. return pagingSelect.toString();
  33. }
  34. }

配置mybatis.xml

我们需要在sqlSessionFactory中注册我们的插件,发挥效果
  1. <bean id="pagePlugin" class="com.xxxxx.pulgin.mybatis.plugin.PagePlugin">
  2. <property name="properties">
  3. <props>
  4. <prop key="dialect">com.dragon.dao.pulgin.jdbc.dialet.OracleDialect</prop>
  5. <prop key="pageSqlId">.*query.*</prop>
  6. </props>
  7. </property>
  8. </bean>

  1. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  2. <property name="dataSource" ref="dataSource" />
  3. <property name="configLocation" value="classpath:spring/mybatis.xml" />
  4. <property name="plugins">
  5. <array>
  6. <ref bean="pagePlugin" />
  7. </array>
  8. </property>
  9. <property name="mapperLocations">
  10. <list>
  11. <!-- 自动匹配Mapper映射文件 -->
  12. <value>classpath:com/xxxxx/mapper/*-mapper.xml</value>
  13. </list>
  14. </property>
  15. </bean>

至此,我们的分页效果就做出来了。

charles at P.P 2016-7-14

如何做系列(1)- mybatis 如何实现分页?的更多相关文章

  1. Mybatis Generator实现分页功能

    Mybatis Generator实现分页功能 分类: IBATIS2013-07-17 17:03 882人阅读 评论(1) 收藏 举报 mybatisibatisgeneratorpage分页 众 ...

  2. Mybatis第三方PageHelper分页插件原理

    ​ 欢迎关注公号:BiggerBoy,看更多文章 原文链接:https://mp.weixin.qq.com/s?__biz=MzUxNTQyOTIxNA==&mid=2247485158&a ...

  3. Mybatis中的分页

    Mybatis中有哪些分页方式? 数组分页:查询出全部数据,然后再list中截取需要的部分.(逻辑分页) 优点:效率高     缺点:占用内存比较高 sql分页:只从数据库中查询当前页的数据.(物理分 ...

  4. SpringBoot+Mybatis配置Pagehelper分页插件实现自动分页

    SpringBoot+Mybatis配置Pagehelper分页插件实现自动分页 **SpringBoot+Mybatis使用Pagehelper分页插件自动分页,非常好用,不用在自己去计算和组装了. ...

  5. SpringBoot+Mybatis+PageHelper实现分页

    SpringBoot+Mybatis+PageHelper实现分页 mybatis自己没有分页功能,我们可以通过PageHelper工具来实现分页,非常简单方便 第一步:添加依赖 <depend ...

  6. SpringBoot集成Mybatis并具有分页功能PageHelper

    SpringBoot集成Mybatis并具有分页功能PageHelper   环境:IDEA编译工具   第一步:生成测试的数据库表和数据   SET FOREIGN_KEY_CHECKS=0;   ...

  7. SpringBoot系列-整合Mybatis(注解方式)

    目录 一.常用注解说明 二.实战 三.测试 四.注意事项 上一篇文章<SpringBoot系列-整合Mybatis(XML配置方式)>介绍了XML配置方式整合的过程,本文介绍下Spring ...

  8. Mybatis的PageHelper分页插件的PageInfo的属性参数,成员变量的解释,以及页面模板

    作者:个人微信公众号:程序猿的月光宝盒 //当前页 private int pageNum; //每页的数量 private int pageSize; //当前页的数量 private int si ...

  9. 小白的springboot之路(十五)、mybatis的PageHelper分页插件使用

    0.前言 用mybatis,那么分页必不可少,基本都是用PageHelper这个分页插件,好用方便: 1.实现 1.1.添加依赖: <!-- 3.集成 mybatis pagehelper--& ...

随机推荐

  1. CSS W3C统一验证工具

    CssStats 是一个在线的 CSS 代码分析工具  网址是: http://www.cssstats.com/ 如果你想要更全面的,这个神奇,你值得拥有: W3C 统一验证工具: http://v ...

  2. TopCoder代码格式模板

    $BEGINCUT$ $PROBLEMDESC$ $ENDCUT$ #include<bits/stdc++.h> using namespace std; class $CLASSNAM ...

  3. springboot下自定义配置文件,并在项目里读取的方法

    首先 pom文件引入springboot文件处理器 <dependency> <groupId>org.springframework.boot</groupId> ...

  4. linux学习(五)-----组管理和权限管理

    Linux 组基本介绍 在 linux 中的每个用户必须属于一个组,不能独立于组外.在 linux 中每个文件有所有者.所在组.其它组的概念. 1)所有者 2)所在组 3)其它组 4)改变用户所在的组 ...

  5. MyBatis的核心API

    MyBatis核心Api 上次简单的写了一个MyBatis的简介以及编写了一个MyBatis的入门程序,但是在入门程序中出现多很多比较陌生的词,比如SqlSessionFactoryBuilder.S ...

  6. <day006>bootstrap的简单学习 + 轮播图

    任务1:bootstrap的简单学习 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta c ...

  7. 牛客网NOIP赛前集训营-普及组(第七场)

    链接:C 来源:牛客网 牛牛的同学给牛牛表演了一个读心术:牛牛先任意选定一个非负整数,然后进行N次操作:每次操作前,假设牛牛当前的数是a,那么这个操作可能是a = a + x, 或者a = a * x ...

  8. Luogu P4158 [SCOI2009]粉刷匠(dp+背包)

    P4158 [SCOI2009]粉刷匠 题意 题目描述 \(windy\)有\(N\)条木板需要被粉刷.每条木板被分为\(M\)个格子. 每个格子要被刷成红色或蓝色. \(windy\)每次粉刷,只能 ...

  9. java内存模型JMM理解整理

    什么是JMM JMM即为JAVA 内存模型(java memory model).因为在不同的硬件生产商和不同的操作系统下,内存的访问逻辑有一定的差异,结果就是当你的代码在某个系统环境下运行良好,并且 ...

  10. 莫烦PyTorch学习笔记(四)——回归

    下面的代码说明个整个神经网络模拟回归的过程,代码含有详细注释,直接贴下来了 import torch from torch.autograd import Variable import torch. ...