1、背景
大家都知道,Mybatis提供了接口绑定,实现了DAO与xml文件的绑定,无需编写实现类,更方便使用。
但如果我们需要对某些语句进行额外的统一处理,且是在运行时动态修改,怎么办?
比如分页,不希望每次都在sql中手动填充分页参数,能不能动态传递分页参数来实现?
2、PageHelper
以PageHelper为例,来看下他是如果使用拦截器对sql进行处理的。
//其中,type:上面提到的四种拦截对象的类型。method:拦截哪些方法,因为是分页查询,所以只需要query即可。args:对应方法的参数。
@Intercepts(@Signature(type = Executor.class, method = "query", args = {
MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
public class PageHelper implements Interceptor {
//方法增强
public Object intercept(Invocation invocation) throws Throwable {
if (autoRuntimeDialect) {
SqlUtil sqlUtil = getSqlUtil(invocation);
return sqlUtil.processPage(invocation);
} else {
if (autoDialect) {
initSqlUtil(invocation);
}
return sqlUtil.processPage(invocation);
}
}
/**
* 只拦截Executor
*
* @param target
* @return
*/
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
}
其中intercept()主要处理逻辑在SqlUtil类中的doProcessPage(),主要逻辑是拦截boundSql,然后添加上分页的参数,最后将补充完整的sql语句进行执行。
private Page doProcessPage(Invocation invocation, Page page, Object[] args) throws Throwable {
//保存RowBounds状态
RowBounds rowBounds = (RowBounds) args[2];
//获取原始的ms
MappedStatement ms = (MappedStatement) args[0];
//判断并处理为PageSqlSource
if (!isPageSqlSource(ms)) {
processMappedStatement(ms);
}
//设置当前的parser,后面每次使用前都会set,ThreadLocal的值不会产生不良影响
((PageSqlSource)ms.getSqlSource()).setParser(parser);
try {
//忽略RowBounds-否则会进行Mybatis自带的内存分页
args[2] = RowBounds.DEFAULT;
//如果只进行排序 或 pageSizeZero的判断
if (isQueryOnly(page)) {
return doQueryOnly(page, invocation);
}
//简单的通过total的值来判断是否进行count查询
if (page.isCount()) {
page.setCountSignal(