Mybatis 分页组件PageHelper 使用

超、凢脫俗 2024-04-17 06:02 169阅读 0赞

PageHelper 官网:

  1. https://pagehelper.github.io
  2. 关于PageHelper 的开发和原理官网上也已经讲的很明确了,这里不过多解析官网的意思

快速构建:

  1. 首先需要一个 SSM 项目【也可以单体Mybatis】
  2. 新增Maven 配置:


    com.github.pagehelper
    pagehelper
    5.1.10
  3. mybatis-config.xml 加入以下配置【也可以写在Spring的配置中,写法略有不同】:














  4. Dao 以及 Xml 写法。

    // Dao
    List getListByPage();

    <!— XML —>

  5. 重点Spring 的实现类:

    /**

    1. * 分页查询数据
    2. * @return
    3. */
    4. public PageInfo getListByPage(PageInfo pageInfo){
    5. //设置分页信息(第几页,每页数量),并将Page 对象放入到 ThreadLocal 中,
    6. PageHelper.startPage(pageInfo.getPageNum(), pageInfo.getPageSize());
    7. //查询结果,我们的拦截器会在这里起作用
    8. List<ClassInfo> result = classDao.getListByPage();
    9. // 生成返回结果
    10. PageInfo<ClassInfo> pageInfoResult = new PageInfo<ClassInfo>(result);
    11. return pageInfoResult;
    12. }

这里需要注意的是,getListByPage 的参数PageInfo 仅仅由于接受 前端分页参数,不可用于返回。

  1. 通过Controller执行我们的代码,看一下Mybatis sql打印结果:
    在这里插入图片描述
    哦吼,为什么会打印出这两条SQL ,一条Count,一条Select,我原来的Sql 为什么没有执行那,不着急我们看下一部分

源码浅析:

  1. 要想真正理解这个功能,需要对Mybatis源码 和 Mybatis拦截器有一定的了解,那么PageInterceptor中究竟做了什么那,直接上源码,因为它是中国人开发的,代码注释也一目了然,完全Copy

    package com.github.pagehelper;
    public class PageInterceptor implements Interceptor {

    1. private volatile Dialect dialect;
    2. private String countSuffix = "_COUNT";
    3. protected Cache<String, MappedStatement> msCountMap = null;
    4. private String default_dialect_class = "com.github.pagehelper.PageHelper";
    5. @Override
    6. public Object intercept(Invocation invocation) throws Throwable {
    7. try {
    8. Object[] args = invocation.getArgs();
    9. MappedStatement ms = (MappedStatement) args[0];
    10. Object parameter = args[1];
    11. RowBounds rowBounds = (RowBounds) args[2];
    12. ResultHandler resultHandler = (ResultHandler) args[3];
    13. Executor executor = (Executor) invocation.getTarget();
    14. CacheKey cacheKey;
    15. BoundSql boundSql;
    16. //由于逻辑关系,只会进入一次
    17. if (args.length == 4) {
    18. //4 个参数时
    19. boundSql = ms.getBoundSql(parameter);
    20. cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
    21. } else {
    22. //6 个参数时
    23. cacheKey = (CacheKey) args[4];
    24. boundSql = (BoundSql) args[5];
    25. }
    26. checkDialectExists();
    27. List resultList;
    28. //调用方法判断是否需要进行分页,如果不需要,直接返回结果
    29. if (!dialect.skip(ms, parameter, rowBounds)) {
    30. //判断是否需要进行 count 查询
    31. if (dialect.beforeCount(ms, parameter, rowBounds)) {
    32. //查询总数
    33. Long count = count(executor, ms, parameter, rowBounds, resultHandler, boundSql);
    34. //处理查询总数,返回 true 时继续分页查询,false 时直接返回
    35. if (!dialect.afterCount(count, parameter, rowBounds)) {
    36. //当查询总数为 0 时,直接返回空的结果
    37. return dialect.afterPage(new ArrayList(), parameter, rowBounds);
    38. }
    39. }
    40. resultList = ExecutorUtil.pageQuery(dialect, executor,
    41. ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
    42. } else {
    43. //rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
    44. resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
    45. }
    46. return dialect.afterPage(resultList, parameter, rowBounds);
    47. } finally {
    48. if(dialect != null){
    49. dialect.afterAll();
    50. }
    51. }
    52. }
    53. /**
    54. * Spring bean 方式配置时,如果没有配置属性就不会执行下面的 setProperties 方法,就不会初始化
    55. * <p>
    56. * 因此这里会出现 null 的情况 fixed #26
    57. */
    58. private void checkDialectExists() {
    59. if (dialect == null) {
    60. synchronized (default_dialect_class) {
    61. if (dialect == null) {
    62. setProperties(new Properties());
    63. }
    64. }
    65. }
    66. }
    67. private Long count(Executor executor, MappedStatement ms, Object parameter,
    68. RowBounds rowBounds, ResultHandler resultHandler,
    69. BoundSql boundSql) throws SQLException {
    70. String countMsId = ms.getId() + countSuffix;
    71. Long count;
    72. //先判断是否存在手写的 count 查询
    73. MappedStatement countMs = ExecutorUtil.getExistedMappedStatement(ms.getConfiguration(), countMsId);
    74. if (countMs != null) {
    75. count = ExecutorUtil.executeManualCount(executor, countMs, parameter, boundSql, resultHandler);
    76. } else {
    77. countMs = msCountMap.get(countMsId);
    78. //自动创建
    79. if (countMs == null) {
    80. //根据当前的 ms 创建一个返回值为 Long 类型的 ms
    81. countMs = MSUtils.newCountMappedStatement(ms, countMsId);
    82. msCountMap.put(countMsId, countMs);
    83. }
    84. count = ExecutorUtil.executeAutoCount(dialect, executor, countMs, parameter, boundSql, rowBounds, resultHandler);
    85. }
    86. return count;
    87. }
    88. @Override
    89. public Object plugin(Object target) {
    90. return Plugin.wrap(target, this);
    91. }
    92. @Override
    93. public void setProperties(Properties properties) {
    94. //缓存 count ms
    95. msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);
    96. String dialectClass = properties.getProperty("dialect");
    97. if (StringUtil.isEmpty(dialectClass)) {
    98. dialectClass = default_dialect_class;
    99. }
    100. try {
    101. Class<?> aClass = Class.forName(dialectClass);
    102. dialect = (Dialect) aClass.newInstance();
    103. } catch (Exception e) {
    104. throw new PageException(e);
    105. }
    106. dialect.setProperties(properties);
    107. String countSuffix = properties.getProperty("countSuffix");
    108. if (StringUtil.isNotEmpty(countSuffix)) {
    109. this.countSuffix = countSuffix;
    110. }
    111. }

    }

  2. Mybatis 执行 Dao层方法时执行时,会被拦截器拦截执行Interceptor.intercept()方法,我们可以再源码中看到 第37行时查询了总条数,在第44行执行了 分页查询,也就是说PageInterceptor会拦截请求,找到我们本来要执行的Sql并将其拆解,生成 count 和 Select Limit Sql 执行,并最后将结果返回。

  3. 那么为什么我们原来的XML 配置的sql 并没有执行那,是因为Mybatis执行XML中的sql 也是一个拦截器,Mybatis执行拦截器是责任链的形式,而Mybatis自己的拦截器优先级最低,先执行了 PageInterceptor,并且PageInterceptor 中没有调用 Mybatis本身的拦截器,所以就没有执行。需要在PageInterceptor.intercept() 中加入代码: Object result = invocation.proceed(); 程序才会回去执行下一级拦截器,显然这里根本不需要执行下一个拦截器

以上仅为博主的粗浅认识,如有疑问或建议,可以留言多多探讨,博主会第一时间更正。

发表评论

表情:
评论列表 (有 0 条评论,169人围观)

还没有评论,来说两句吧...

相关阅读