分页插件PageHelper工作原理

  1. 1. 分页插件PageHelper工作原理
  2. 2. 1. 原理概述
  3. 3. 2. 使用注意事项

分页插件PageHelper工作原理

1. 原理概述

​ PageHelper是MyBatis的一个插件,内部实现了一个PageInterceptor拦截器。Mybatis会加载这个拦截器到拦截器链中。在我们使用过程中先使用PageHelper.startPage这样的语句在当前线程上下文中设置一个ThreadLocal变量,再利用PageInterceptor这个分页拦截器拦截,从ThreadLocal中拿到分页的信息,如果有分页信息拼装分页SQL(limit语句等)进行分页查询,最后再把ThreadLocal中的东西清除掉。

部分源码可知设置了一个ThreadLocal变量:

2. 使用注意事项

​ PageHelper使用了ThreadLocal保存分页参数,分页参数和线程是绑定的。因此我们需要保证PageHelper 的startPage调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。

​ 如果代码在进入Executor前发生异常,就会导致线程不可用,这属于人为Bug(例如接口方法和XML中的不匹配,导致找不到MapppedStatement时)。这种情况由于线程不可用,也不会导致 ThreadLocal参数被错误使用。

但是如果写出以下代码,就是不安全的用法:

1
2
3
4
5
6
7
PageHelper.startPage(1, 10);
List<Country> list;
if(param1 != null){
list = countryMapper.selectIf(param1);
} else {
list = new ArrayList<Country>();
}

​ 这种情况下由于param1存在null的情况,就会导致PageHelper产生了一个分页参数,但是没有被消费,也没有被清理。这个参数就会一直保存在ThreadLocal中。可能导致其它不该分页的方法去消费了这个参数,莫名其妙的做了分页。

​ 此外如果考虑到发生异常,可以加一个finally块,手动清理参数。