打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
mybatis分页的简单实现

最近的项目中使用到了mybatis,发现mybatis不支持物理分页,只支持内存分页。因此为了解决这个问题,在网上搜索了一番,不过都比较繁琐。最后使用正则表达过滤查询语句的方式简单解决了该问题.

mybatis物理分页的核心是使用mybatis的拦截器 org.apache.ibatis.plugin.Interceptor ,在mybatis准备好SQL的时候,对SQL字符串进行拦截,生成适合Oracle数据库的分页语句即可。废话不多讲了,直接上代码.

Notes: 该部分依赖commons-lang3.jar包进行反射写入,也可使用 mybatis 自带的反射类实现这部分功能

拦截器代码

  1. package org.mybatis.test.interceptor;
  2. import java.sql.Connection;
  3. import java.util.*;
  4. import org.apache.commons.lang3.StringUtils;
  5. import org.apache.commons.lang3.reflect.FieldUtils;
  6. import org.apache.ibatis.executor.statement.StatementHandler;
  7. import org.apache.ibatis.mapping.BoundSql;
  8. import org.apache.ibatis.plugin.*;
  9. import org.apache.ibatis.session.RowBounds;
  10. @Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }))
  11. public class PaginationInterceptor implements Interceptor {
  12. private final static String SQL_SELECT_REGEX = "(?is)^\\s*SELECT.*$";
  13. private final static String SQL_COUNT_REGEX = "(?is)^\\s*SELECT\\s+COUNT\\s*\\(\\s*(?:\\*|\\w+)\\s*\\).*$";
  14. //@Override
  15. public Object intercept(Invocation inv) throws Throwable {
  16. StatementHandler target = (StatementHandler) inv.getTarget();
  17. BoundSql boundSql = target.getBoundSql();
  18. String sql = boundSql.getSql();
  19. if (StringUtils.isBlank(sql)) {
  20. return inv.proceed();
  21. }
  22. System.out.println("origin sql>>>>>" + sql.replaceAll("\n", ""));
  23. // 只有为select查询语句时才进行下一步
  24. if (sql.matches(SQL_SELECT_REGEX)
  25. && !Pattern.matches(SQL_COUNT_REGEX, sql)) {
  26. Object obj = FieldUtils.readField(target, "delegate", true);
  27. // 反射获取 RowBounds 对象。
  28. RowBounds rowBounds = (RowBounds) FieldUtils.readField(obj,
  29. "rowBounds", true);
  30. // 分页参数存在且不为默认值时进行分页SQL构造
  31. if (rowBounds != null && rowBounds != RowBounds.DEFAULT) {
  32. FieldUtils.writeField(boundSql, "sql", newSql(sql, rowBounds),
  33. true);
  34. System.out.println("new sql>>>>>"
  35. + boundSql.getSql().replaceAll("\n", ""));
  36. // 一定要还原否则将无法得到下一组数据(第一次的数据被缓存了)
  37. FieldUtils.writeField(rowBounds, "offset",
  38. RowBounds.NO_ROW_OFFSET, true);
  39. FieldUtils.writeField(rowBounds, "limit",
  40. RowBounds.NO_ROW_LIMIT, true);
  41. }
  42. }
  43. return inv.proceed();
  44. }
  45. public String newSql(String oldSql, RowBounds rowBounds) {
  46. String start = " SELECT * FROM (SELECT row_.*, ROWNUM rownum_ FROM ( ";
  47. String end = " ) row_ WHERE ROWNUM <= " + rowBounds.getLimit()
  48. + ") WHERE rownum_ > " + rowBounds.getOffset();
  49. return start + oldSql + end;
  50. }
  51. //@Override
  52. public Object plugin(Object target) {
  53. return Plugin.wrap(target, this);
  54. }
  55. //@Override
  56. public void setProperties(Properties arg0) {
  57. System.out.println(arg0);
  58. }
  59. //测试正则表达式是否能正常工作
  60. public static void main(String[] args) {
  61. String SQL_SELECT_REGEX = "^\\s*SELECT.*$";
  62. String SQL_COUNT_REGEX = "^\\s*SELECT\\s+COUNT\\s*\\(\\s*(?:\\*|\\w+)\\s*\\).*$";
  63. List<String> tests = new ArrayList<String>();
  64. tests.add("select count(*) from abc \n\t\t where\n abc");
  65. tests.add("SELECT COUNT(*) from abc");
  66. tests.add(" select count (*) from abc");
  67. tests.add(" select count( *) from abc");
  68. tests.add("select count( * ),id from abc");
  69. tests.add("select * from abc");
  70. tests.add("select abc,test,fdas from abc");
  71. tests.add("select count(adb) from abc");
  72. tests.add("select count(0) from abc");
  73. tests.add("select min(count(*)) from abc");
  74. tests.add("update min(count(*)) from abc");
  75. tests.add("delete min(count(*)) from abc");
  76. Pattern p1 = Pattern.compile(SQL_SELECT_REGEX, Pattern.DOTALL
  77. | Pattern.CASE_INSENSITIVE);
  78. Pattern p2 = Pattern.compile(SQL_COUNT_REGEX, Pattern.DOTALL
  79. | Pattern.CASE_INSENSITIVE);
  80. for (String str : tests) {
  81. Matcher m1 = p1.matcher(str);
  82. Matcher m2 = p2.matcher(str);
  83. System.out.println("匹配字符串: " + str);
  84. System.out.println(" 是select语句? " + m1.matches());
  85. System.out.println(" 是count语句? " + m2.matches());
  86. System.out.println();
  87. }
  88. }
  89. }

在spring中配置拦截器

  1. <bean name="paginationInterceptor" class="org.mybatis.test.interceptor.PaginationInterceptor"></bean>
  2. <!-- define the SqlSessionFactory -->
  3. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  4. <property name="dataSource" ref="dataSource" />
  5. <property name="typeAliasesPackage" value="org.mybatis.test.domain" />
  6. <property name="plugins">
  7. <list>
  8. <ref bean="paginationInterceptor" />
  9. </list>
  10. </property>
  11. </bean>

使用

  1. public class Test(){
  2. private String name;
  3. private int age;
  4. // set/get省
  5. }
  1. 在TestMapper.java接口类中声明分页方法

    1. //统计总条数
    2. int countByVo(Test test);
    3. //分页查询
    4. List<T> queryByVo(RowBounds rowBound, Test test);
  2. TestMapper.xml中

    1. <select id="countByVo" parameterType="Test" resultType="int">
    2. select count(*) as count from table_test
    3. <where>
    4. <if test="name != null">name like '' || #{name} || '%'</if>
    5. </where>
    6. </select>
    7. <select id="queryByVo" parameterType="Test" resultType="Test">
    8. select * from table_test
    9. <where>
    10. <if test="name != null">name like '' || #{name} || '%'</if>
    11. </where>
    12. </select>
  3. 业务层代码

    1. @Autowired
    2. private TestMapper testMapper;
    3. /**
    4. * @param offset 起始位置
    5. * @param limit 结束位置
    6. */
    7. public void queryByVo(int offset, int limit) {
    8. Test test = new Test();
    9. List<User> list = this.testMapper.queryByVo(new RowBounds(offset, limit), test);
    10. }

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
mybatis下的分页,支持所有的数据库
MyBatis3.2 使用Interceptor进行分页
深入浅出Mybatis
Mybatis源码解析之六剑客
阿里面试:Mybatis中方法和SQL是怎么关联起来的呢?
Mybatis分页插件-pagehelper
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服