Mybatis-PageHelper源码分析
Mybatis-PageHelper源码分析
1. 背景
项目中分页是非常常见的需求,在项目中我们一般集成第三方的分页插件,避免在业务层嵌入过多业务代码。
分页插件,我们项目中是采用Mybatis-PageHelper
,作为一款轻量级的插件,源码应该不难,我们就来分析分析他的源码
2. 分页插件实现原理与基础
2.1 拦截器
Mybatis 提供了拦截器接口Interceptor(org.apache.ibatis.plugin.Interceptor)
, 我们仅需要在实现类中对拦截对象和方法进行处理即可
2.1.1 Object intercept(Invocation invocation)
intercept 是mybatis 运行时要执行的方法。通过该方法的参数invocation
可以得到很多有用的信息,该参数的方法常用如下
public class ZszInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget();
Method method = invocation.getMethod();
Object[] args = invocation.getArgs();
Object result = invocation.proceed();
return result;
}
}
getTarget ():
获取当前拦截的对象
getMethod():
获取当前被拦截的方法
getArgs():
可以返回被拦截方法中的参数
invocation.proceed():
proceed() 方法实际上执行了method.invoke(target,args)方法,上面的代码中没有做任何处理,直接返回了结果
2.1.2 plugin(Object target)
这个方法的参数target 就是拦截器要拦截的对象,该方法会在创建被拦截的接口实现类时被调用。
听起来有点绕,其实现其实我们只需要调用mybatis 提供的 Plugin.wrap(target, this)
静态方法就可以通过java的动态代理拦截目标对象
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
2.1.3 setProperties(Properties properties)
传递插件的参数,可以通过参数来改变插件的行为
如何配置参数:
我们在配置拦截器的时候,就需要配置
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 支持通过Mapper接口参数来传递分页参数 --> <property name="helperDialect" value="mysql"/> <property name="supportMethodsArguments" value="true"/> <property name="rowBoundsWithCount" value="true"/> </plugin> </plugins>
2.2 拦截器签名
@Intercepts
和 注解签名 @Signature
用来配置拦截器要拦截的接口的方法
@Intercepts
注解中的属性是一个 @Signature
签名数组,可以在同一个拦截器中同时拦截不同的接口和方法
@Signature
注解包含以下三个属性
type: 配置拦截器的接口,可选值是
- Executor()
- ParameterHandler()
- ResultSetHandler()
- StatementHandler()
Method: 设置拦截器中的方法名,可选值是上面4个接口中对应的方法,需要和接口匹配
例如Executor 中能选query,update等
args:设置拦截方法的参数类型数组,通过方法名和参数类型可以确定唯一一个方法
3. PageHelper源码实现
3.1 拦截器与拦截器签名
这样我们就能拦截到sql 查询语句
3.2 获取拦截参数
3.3 分页判断
- 判断是否需要分页
- 判断是否需要进行count 查询
- 查询总数
- 分页查询
3.4 计算总数
计算总数实现
获取方言count sql
此时还贴心的去除了order by
获取普通的Count-sql
3.5 分页查询
- boundSql 包含了执行的sql 和对应的参数
调用方言获取分页 sql
String sql = boundSql.getSql()
方言sql- 其中
Page page = this.getLocalPage();
就是获得分页的参数 - 判断是否需要排序,添加orderby 语句
获取分页的 getPageSql
转换为分页语句
3.6 添加order by 语句
在做分页查询的时候校验了是否需要order by 语句
添加order 语句
4. Dialect 方言接口
Dialect 方言 包含了数据库支持的类型
我们可以看到几个关键节点上都调用了Dialect