在前几天的文章中,我们介绍了PageHelper的分页方法,研读代码定位到了ExecutorUtil.pageQuery(...)
方法,并阅读到了其中的部分代码。
今天我们将看到重要的SQL修改代码。
getPageSql
我们接着看代码:
if (!dialect.beforePage(ms, parameter, rowBounds)) {
return executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
} else {
parameter = dialect.processParameterObject(ms, parameter, boundSql, cacheKey);
String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, cacheKey);
// 其他代码 ...
}
今天应该阅读dialect.getPageSql
方法了。通过前面的文章,我们已经知道dialect对应的实现是MysqlDialect,那么getPageSql在MySql中的具体实现是什么样的呢?
public class MySqlDialect extends AbstractHelperDialect {
public String getPageSql(String sql, Page page, CacheKey pageKey) {
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
sqlBuilder.append(sql);
if (page.getStartRow() == 0L) {
sqlBuilder.append("\n LIMIT ? ");
} else {
sqlBuilder.append("\n LIMIT ?, ? ");
}
return sqlBuilder.toString();
}
}
经过不懈的查找,我们终于看到了对SQL语句的直接修改了。(^▽^)。
这里的写法也很值得学习,首先是使用StringBuilder加速字符串拼接速度;其次,是在拼接前首先开辟充足的空间new StringBulder(sql.length() + 14)
以免在StringBuilder拼接过程中再次分配空间浪费时间。
然后我们就看到了SQL拼接啦!!!
首先判断是否是第一页,如果是第一页,则拼接\n LIMIT ?
,不是第一页则拼接\n LIMIT ?, ?
。
至此我们终于看到了SQL修改的真容。
为表庆祝,已经确定我们查看的就是代码执行位置,今天我们换种形式,借助IDEA来逐步查看Java代码的执行情况。
以若依系统中的中某个页面,如“系统监控->定时任务”存在分页,我们以此为例。
页面加载完成后是下面的样子:
我们首先在PageInterceptor拦截器入口处打一个断点。:
并成功拦截。
然后我们在MysqlDialect修改方法出打一个断点,并再次成功拦截!开心!
题外话
我公司目前使用的另一套开源架构jfinal,其中的分页方式也蛮方便的,但是和PageHelper没什么关系,这里多说几句吧。
jfinal中的分页方式使用为:
Page<Record> page = Db.paginate(pageNumber, pageSize, selectSql, sqlExceptSelect);
里面的参数名称都很规范,相信大家都看的明白,我们大致看一下它其中的分页实现代码是怎样的:
/**MySqlDialect.java*/
public String forPaginate(int pageNumber, int pageSize, StringBuilder findSql) {
int offset = pageSize * (pageNumber - 1);
findSql.append(" limit ").append(offset).append(", ").append(pageSize); // limit can use one or two '?' to pass paras
return findSql.toString();
}
可以看到他也是通过拼接LIMIT offset, pageSize
来实现的。其中的offset=pageSize * (pageNumber - 1)。