新版本的PageHelper与mybatis请移步这里——支持PageHelper5.1.4 Mybatis3.4.6
先说说使用PageHelper踩过的坑:
- 在mapper映射文件中,如果使用了limit关键字,而又使用了PageHelper的功能,比如orderBy,那么就会报错。
- 在使用稍微复杂的查询时,PageHelper是针对最外层(最下方)的数据进行分页的。
出现这样的问题,归根结底还是PageHelper源码的问题。
这套修改的原理即是修改pagehelper “limit”的位置,从末尾改到我们想要的位置。
不多说PageHelper本身,本文使用的PageHelper版本为
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.2.1</version>
</dependency>
这里以MySql为例,使得PageHelper支持复杂查询的分页。
一、添加CustomDialect,自定义方言
先看看原版的方言类的定义
package com.github.pagehelper.dialect;
@Override
public String getPageSql(String sql, Page page, RowBounds rowBounds, CacheKey pageKey) {
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
sqlBuilder.append(sql);
sqlBuilder.append(" limit ?,?");
return sqlBuilder.toString();
}
从这里可以看出,PageHelper在针对MySql数据库进行分页时,是在sql语句的最后加上limit偏移量进行分页查询的。且不说这种查询在数据量大时效率可能不高,这样去操作的话,在稍微复杂的查询中,通常无法正常的进行分页。
/**
* Created by Anur IjuoKaruKas on 2017/9/28.
* Description :
*/
public class CustomDialect extends MySqlDialect {
public CustomDialect(SqlUtil sqlUtil) {
super(sqlUtil);
}
@Override
public Object processPageParameter(MappedStatement ms, Map<String, Object> paramMap, Page page, BoundSql boundSql, CacheKey pageKey) {
return super.processPageParameter(ms, paramMap, page, boundSql, pageKey);
}
@Override
public String getPageSql(String sql, Page page, RowBounds rowBounds, CacheKey pageKey) {
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
if (sql.indexOf(ProjectConstant.SQL_SIGN) != -1) {
StringBuffer stringBuffer = new StringBuffer(sql);
stringBuffer.indexOf(ProjectConstant.SQL_SIGN);
StringBuffer mae = new StringBuffer(stringBuffer.substring(0, stringBuffer.indexOf(ProjectConstant.SQL_SIGN)));
StringBuffer uShiRo = new StringBuffer(stringBuffer.substring(stringBuffer.indexOf(ProjectConstant.SQL_SIGN), sql.length()));
mae.insert(mae.lastIndexOf(")")," limit ?,?");
return mae.append(uShiRo).toString();
}
sqlBuilder.append(sql);
sqlBuilder.append(" limit ?,?");
return sqlBuilder.toString();
}
}
所以,在这里,我对它进行了一点点的改写。
新建一个CustomDialect继承MySqlDialect,并重写了原方法。
在这里,做了一个判断,如果sql语句中包含 * ProjectConstant.SQL_SIGN(limitable) ,那么在这前面加上 “limit ?,?”*
这个SQL_SIGN是什么呢?
public static final String SQL_SIGN = "AS limitable";
实际上就是一个用于标记哪里需要使用偏移量的一个标识。
在mapper映射文件中就可以这样写:
<select id="selectRecordByComplexCondition" resultMap="ListQueryMap" parameterMap="ListQueryPo">
SELECT
*
from
(
SELECT
*
FROM
record r
WHERE
r.record_release_state = 1
<if test="tagIdListToSqlString!=null">
AND r.record_id IN (
SELECT
object_id
FROM
record_tag rtag
WHERE
rtag.tag_id IN ${tagIdListToSqlString}
AND rtag.module = #{moduleEnumIndex}
GROUP BY
rtag.object_id
HAVING
count(*) >= #{tagCount}
)
</if>
AND r.module = #{moduleEnumIndex}
<if test="rcId!=null">
AND r.rc_id = #{rcId}
</if>
<if test="raId!=null">
AND r.ra_id = #{raId}
</if>
<if test="recordTitle!=null">
AND r.record_title LIKE CONCAT(CONCAT('%', #{recordTitle}), '%')
</if>
<if test="recordReleaseTimeFrom!=null">
AND r.record_release_time > #{recordReleaseTimeFrom}
</if>
/* 现在的PageHelper limit 加在这里 */
) AS limitable