MyBatis的分页原理

这篇具有很好参考价值的文章主要介绍了MyBatis的分页原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写作目的

最近看到了一篇MyBatis的分页实现原理,文章里描述到使用ThreadLocal,其实想主要想看看ThreadLocal的巧妙使用,并且看一下分页是如何实现的。

源码下载

ChaiRongD/Demooo - Gitee.com

源码跟踪

其实一个简单的分页如下面代码所示,使用PageHelp对象设置分页的参数,然后把查询到的List对象作为参数传入PageInfo对象中,就拿到了分页对象的结果。

    @GetMapping("/page")
    public Object page() {
        //查询第三页,每页三条
        PageHelper.startPage(3 , 3);
        List<Temperature> temperatures = temperatureDao.selectByExample(null);
        //得到分页的结果对象
        PageInfo<Temperature> resPage = new PageInfo<>(temperatures);
        return resPage;
    }

PageHelper.startPage方法

一直跟下去会定位到PageMethod的startPage方法,方法内容为创建一个包含分页参数的page对象,然后放在ThreadLocal中。

/**
     * 开始分页
     *
     * @param pageNum      页码
     * @param pageSize     每页显示数量
     * @param count        是否进行count查询
     * @param reasonable   分页合理化,null时用默认配置
     * @param pageSizeZero true且pageSize=0时返回全部结果,false时分页,null时用默认配置
     */
    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
        //构建一个包含分页参数的page对象
        //构建一个包含分页参数的page对象
        //构建一个包含分页参数的page对象
        Page<E> page = new Page<E>(pageNum, pageSize, count);
        page.setReasonable(reasonable);
        page.setPageSizeZero(pageSizeZero);
        //当已经执行过orderBy的时候
        Page<E> oldPage = getLocalPage();
        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }
        //把page对象放在ThreadLocal中
        //把page对象放在ThreadLocal中
        //把page对象放在ThreadLocal中
        setLocalPage(page);
        return page;
    }

真正的执行逻辑

即执行dao.select方法

List<Temperature> temperatures = temperatureDao.selectByExample(null);

下一步直接跳到PageInterceptor的intercept方法

@Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
        
          
            //省略内容,省略内容,省略内容

            List resultList;
            //步骤1:调用方法判断是否需要进行分页,如果不需要,直接返回结果
            if (!dialect.skip(ms, parameter, rowBounds)) {
                //判断是否需要进行 count 查询
                if (dialect.beforeCount(ms, parameter, rowBounds)) {
                    //步骤2:查询总条数
                    Long count = count(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                    //处理查询总数,返回 true 时继续分页查询,false 时直接返回
                    //步骤3:保存总条数
                    if (!dialect.afterCount(count, parameter, rowBounds)) {
                        //当查询总数为 0 时,直接返回空的结果
                        return dialect.afterPage(new ArrayList(), parameter, rowBounds);
                    }
                }
               //步骤4:执行分页查询
                resultList = ExecutorUtil.pageQuery(dialect, executor,
                        ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
            } else {
                //rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }
            //步骤5:封装结果
            return dialect.afterPage(resultList, parameter, rowBounds);
        } finally {
            if(dialect != null){
                dialect.afterAll();
            }
        }
    }

步骤1:判断是否分页

首先根据PageHelper的skip方法查看是否需要分页,判断条件是ThreadLocal中是否有page对象,因为PageHelper.startPage方法放入到ThreadLocal中放入page对象,因此此处会判断为分页

步骤2:查询总条数

方法会定位到PageInterceptor的count方法的的代码

count = ExecutorUtil.executeAutoCount(dialect, executor, countMs, parameter, boundSql, rowBounds, resultHandler);

方法executeAutoCount方法如下,

1)首先根据查询语句拼接count语句(select * from table where a —> select count(“0”) from table where a)

2)然后执行SQL

3)拿到count结果

MyBatis的分页原理

步骤3:保存总条数

首先从ThreadLocal中获取page对象,然后把总条数count放在page对象中,然后根据总条数和分页条件判断是否有必要查询,比如一共10条记录,你每页10条,你查第2页,那么就没必要去查询,因此11-20条记录不存在。

public boolean afterCount(long count, Object parameterObject, RowBounds rowBounds) {
        //获取ThreadLocal中的page对象
        Page page = getLocalPage();
        //保存count对象
        page.setTotal(count);
        if (rowBounds instanceof PageRowBounds) {
            ((PageRowBounds) rowBounds).setTotal(count);
        }
        //pageSize < 0 的时候,不执行分页查询
        //pageSize = 0 的时候,还需要执行后续查询,但是不会分页
        if (page.getPageSize() < 0) {
            return false;
        }
        //根据总数量和你分页条件去判断是否有必要去做查询
        return count > ((page.getPageNum() - 1) * page.getPageSize());
    }

步骤4:执行分页查询

首先获取分页的SQL(slect * from tablle -> select * from table limit ,),然后执行获取到结果

MyBatis的分页原理

怎么获取分页的SQL呢?简单到没朋友

public String getPageSql(String sql, Page page, CacheKey pageKey) {
        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
        sqlBuilder.append(sql);
        if (page.getStartRow() == 0) {
            sqlBuilder.append(" LIMIT ? ");
        } else {
            sqlBuilder.append(" LIMIT ?, ? ");
        }
        return sqlBuilder.toString();
    }

步骤5:封装结果

还是把查询到的结果放到TheadLocal中的page对象中,然后返回page对象,此时page对象带有查询对象集合、分页条数、第几页

//AbstractHelperDialect###afterPage
public Object afterPage(List pageList, Object parameterObject, RowBounds rowBounds) {
        Page page = getLocalPage();
        if (page == null) {
            return pageList;
        }
        page.addAll(pageList);
        
        //省略

        return page;
    }

构造PageInfo对象

首先要明确的是下面代码中的temperatures对象是Page(Page extends ArrayList )类型的,Page集成了ArrayList对象。

MyBatis的分页原理

下面看PageInfo的构造方法,真是一看吓一跳。首先list参数传入的是Page对象,可以从Page对象中拿到total、pageNum、pageSize和当前页的数据集合,可以进一步算出是否为首页、尾页等其他非必要的分页信息。

public PageInfo(List<T> list, int navigatePages) {
        //把list对象强转为page对象,然后获取total总条数对象
        super(list);
        if (list instanceof Page) {
            Page page = (Page) list;
            //获取当前第几页
            this.pageNum = page.getPageNum();
            //获取每页大小
            this.pageSize = page.getPageSize();

            this.pages = page.getPages();
            this.size = page.size();
            //由于结果是>startRow的,所以实际的需要+1
            if (this.size == 0) {
                this.startRow = 0;
                this.endRow = 0;
            } else {
                this.startRow = page.getStartRow() + 1;
                //计算实际的endRow(最后一页的时候特殊)
                this.endRow = this.startRow - 1 + this.size;
            }
        } else if (list instanceof Collection) {
            this.pageNum = 1;
            this.pageSize = list.size();

            this.pages = this.pageSize > 0 ? 1 : 0;
            this.size = list.size();
            this.startRow = 0;
            this.endRow = list.size() > 0 ? list.size() - 1 : 0;
        }
        if (list instanceof Collection) {
            this.navigatePages = navigatePages;
            //计算导航页
            calcNavigatepageNums();
            //计算前后页,第一页,最后一页
            calcPage();
            //判断页面边界
            judgePageBoudary();
        }
    }

总结

分页过程

首先会把分页参数封装成Page对象放到ThreadLocal中

然后根据SQL进行拼接转换(select * from table where a) ->(select count(“0”)from table where a)和(select * from table where a limit ,)

有了total总条数、pageNum当前第几页、pageSize每页大小和当前页的数据,就可以算出分页的其他非必要信息(是否为首页,是否为尾页,总页数)

ThreadLocal对象的使用

ThreadLocal的巧妙使用(big)文章来源地址https://www.toymoban.com/news/detail-486726.html

到了这里,关于MyBatis的分页原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java springboot整合MyBatis实现分页查询以及带条件的分页查询

    之前的文章 java springboot整合MyBatis做数据库查询操作操作了springboot整合MyBatis,然后简单做了个按id查询的操作 那么 我们按上文搭建起的环境继续 我们直接在staffDao接口中声明一个分页函数 这里 我们直接在 sql语句中写limit 分页逻辑 参数是方法接收的 这个函数接收两个参数

    2024年02月10日
    浏览(44)
  • Mybatis分页方式及实现原理

    一、mybatis的4种分页方式(物理分页、逻辑分页) 1、借助Sql语句Q进行分页(物理分页) 2、拦截器分页(物理分页)通过拦截器给sq语句末尾加Eimt语句来查询 3、借助 数组Q进行分页(逻辑分页) 4、RowBounds分页插件实现分页(逻辑分页) 二、mybatis分页的原理 mybatis分页原理是:在MyBatis内部

    2024年02月16日
    浏览(32)
  • JavaWeb12(实现基础分页&模糊查询的分页)

    目录 一. 效果预览 ​编辑 二. 实现基本分页 2.1 分页sql  --每页3条  取第二页 --由于伪列不能作用与大于符号也不能作用于between....and --因此需要将伪列-----名列  2.2 万能公式  2.3 首页上一页下一页实现 ②前端代码 2.4 末页实现优化 ①底层代码 ②前端优化  三.实现模糊查询

    2024年02月06日
    浏览(46)
  • Elasticsearch - 闲聊ElasticSearch中的分页

    ElasticSearch是一款强大的搜索引擎,它能够帮助我们快速地搜索海量数据。然而,在处理大量数据时,ElasticSearch的性能可能会受到影响。其中一个常见的问题是深度分页,也就是当我们需要获取大量数据时,ElasticSearch需要处理的数据量太大,导致性能下降。 Elasticsearch 深度分

    2024年02月11日
    浏览(35)
  • 【定时任务处理中的分页问题】

    最近要做一个定时任务处理的需求,在分页处理上。发现了大家容易遇到的一些\\\"坑\\\",特此分析记录一下。 场景 现在想象一下这个场景,你有一个定时处理任务,需要查询数据库任务表中的所有待处理任务,然后进行处理。 举个例子:生成用户的月度账单,并且要尽可能确

    2024年02月12日
    浏览(44)
  • MyBatisPlus(SpringBoot版)的分页插件

    目录  一、前置工作:         1.整体项目目录结构         2.创建普通javamaven项目。         3.导入依赖,改造成springboot项目         4.配置启动类         5.创建service接口及其实现类        6.创建接口Mapper                  7.配置数据源         8.创建数

    2024年04月27日
    浏览(42)
  • Android Jetpack Compose 中的分页与缓存展示

    在几乎任何类型的移动项目中,移动开发人员在某个时候都会处理分页数据。如果数据列表太大,无法一次从服务器检索完毕,这就是必需的。因此,我们的后端同事为我们提供了一个端点,返回分页数据列表,并期望我们知道如何在客户端处理它。 在本文中,我们将重点介

    2024年02月13日
    浏览(47)
  • jquery 封装的分页插件(包括上一页,下一页,跳转)

    pagnation.js cs.html

    2024年02月06日
    浏览(37)
  • 打印Winform控件实现简陋版的分页打印(C#)

    本文的代码可以从这里获取:winformDemo.rar · 张祥裕/分享的资源名称 - Gitee.com 作者的水平有限,如有错误,望指正。 为了简单起见,纸张大小,打印机等信息按照默认的来,本文的实现方案是:打印Panel中的控件信息,循环进行打印,打印完一张,把信息重新填充到对应的控

    2024年02月14日
    浏览(33)
  • SpringBoot ElasticSearch 实现订单的分页查询 【SpringBoot系列17】

    SpringCloud 大型系列课程正在制作中,欢迎大家关注与提意见。 程序员每天的CV 与 板砖,也要知其所以然,本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCloud 微服务系列项目开发 elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海

    2024年02月09日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包