pageHelper的分页是怎么实现运行的?
下载pageHelper源码,使用pageHelper的官方demo进行Debug试验。
代码语言:javascript复制源码地址: https://gitee.com/free/Mybatis_PageHelper.git 本文使用分支: 4.2
PageHelper.startPage(2, 10, "id desc");
点进第一个断点
PageHelper.startPage
<E>
:Element,常表示List<E>
,使用泛型的方式是得代码得到复用,达到允许放入不同List的功能。
SqlUtil.getLocalPage
:
SqlUtil是BaseSqlUtil的子类,BaseSqlUtil中定义了一个TreadLocal类型的LOCAL_PAGE
。
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
TreadLocal是一个线程内部存储类,可以在指定线程内存储数据副本,数据存储后,只有特定线程可以得到存储数据。 ThreadLocal内部定义了一个静态成员类ThreadLocalMap,其存在的目的就是为了外围类提供数据存储,因此ThreadLocal类似于一个map(),key就是当前的线程,value就是需要存储的对象,可以通过get/set方法得到当前线程存储的数据(value)。 ThreadLoacl.get() 获取当前线程副本中的值,在pageHelper中主要起到存放分页参数的作用。
ThreadLocal.set() 给当前线程副本的value赋值
自此,PageHelper.startPage(2,10)
已走完,下面开始执行MyBatis的查询语句。
List<Country> list = countryMapper.selectAll();
MyBatis拦截器
在MyBatis将Java代码转化为SQL之前,会先去BaseSqlUtil中的LOCAL_PAGE
变量中获取线程中的分页数据。
而后会走到SqlUtil的doIntercept拦截。
SqlUtil的拦截似乎跟MyBatis并无关系,可实际上在进行拦截时,是走的MyBatis的拦截方法。
原因:
PageHelper类继承自BasePageHelper实现了MyBatis的Interceptor接口使用了MyBatis的Intercepts
注解实现拦截,pageHelper实现了MyBatis的Interceptor接口,完成对SQL的动态分页。所以在进行拦截时,是走的是pageHelper的方法。
拦截器的实现在SqlUtil中。
在boundSql这个对象中,是本次根据参数编译出需要执行的SQL语句。
对比调用xml中的SQL:
不能说是一模一样,可以说是完全一致。
拦截器的翻译
编写的xml文件会在本步骤被编译成SQL语句,在数据库中执行。
在执行查询SQL之前,源码中会通过获取ThreadLocal
中是否存在分页参数,如果存在分页参数,则runtimeDialect
调用getCountSql
方法,先对查询语句做一遍count,根据count的值再分情况执行select语句,当然,在查询时,也可以在代码中设置不进行count。如果ThreadLoacl中没有分页参数,则直接返回查询结果。
如果统计数大于0,则带着分页参数执行SQL语句,将结果存放到LOCAL_PAGE中,返回查询结果。
返回结果后,关闭本次sqlSession。
来自官方的使用提示:
只有紧跟在PageHelper.startPage
方法后的第一个Mybatis的查询(Select)方法会被分页。