PageHelper分页插件是基于Mybatis的Interceptor实现的,实际上是通过拦截Executor的query方法,通过修改其中的sql和请求参数来完成分页的,今天我们来看一下为何PageHelper插件进行分页为何只对第一条查询有效?
下图是PageHelper拦截的方法,PageHelper通过Mybatis提供的@Intercepts进行拦截,具体拦截内容是实现了Executor接口,并且方法名是query,并且参数是顺序是MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class的。
实际上,我们的查询大部分走的都是这一个方法:
也就是说如果单凭我们上面说的几个条件,所有的查询语句都会走分页,但实际情况我们都知道:PageHelper插件只会对第一条查询进行分页,所以奥秘依然在PageHelper中。
关于PageHelper的分页原理,我在《pageHepler的使用和原理》一文中已经写了,所以我们这次看要进行分页的条件是什么?
我们通过debug发现,只要我们在配置中配置了PageHelper,即便是我们没有使用PageHelper.startPage,我们的查询依然进入了PageHelper的拦截器。
因此我们需要继续往下看,当执行到_processPage方法后,我们发现,因为有:SqlUtil.getLocalPage() == null的判断,因此代码并没有走到分页逻辑处理中,所以这个SqlUtil.getLocalPage()是什么呢?
我们可以看到只有这个LOCAL_PAGE中有值,才会进行分页,而这个LOCAL_PAGE其实就是一个ThreadLocal。因此我们在这里可以知道,只有我们在LOCAL_PAGE中设置了参数,PageHelper才会继续进行分页。
那么我们应该如何设置LOCAL_PAGE呢,答案就在我们PageHelper.startPage中,我们发现当我们调用PageHelper.startPage后,会在LOCAL_PAGE中设置上Page参数,后面才会进行分页
但是为何分页只对第一条查询有效?我们可以看执行分页的processPage方法,当方法之后完后,会将LOCAL_PAGE进行清除,因此当下一次执行查询语句时,由于LOCAL_PAGE已经没有了,虽然会进入到PageHelper的拦截方法中,但是并没有调用分页逻辑。
最后总结一下,当我们为Mybatis设置上分页插件后,大部分的查询语句都会进入到PageHelper的拦截方法中,但是PageHelper会判断LOCAL_PAGE中是否有值,有值才会进行分页,而这个LOCAL_PAGE是我们通过PageHelper.startPage进行设置的;当PageHelper执行完后(不管有没有执行分页逻辑),会清除LOCAL_PAGE,因此下一次查询语句就不会再进行分页了。