(springboot2.0.1、pagehelper5.1.4)
pagehelper联表分页,会默认在sql语句的后面添加 limit ?,进行分页。如果是复杂的sql联表查询结果远远不能满足我们,添加 limit在我们想要的位置进行分页,下面的代码中“AS limitable”,为分页标识的sql. 末尾带有 AS limitable 的子查询会拼接上limit进行先分页.然后再外联查询
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import com.github.pagehelper.Dialect;
import com.github.pagehelper.PageException;
import com.github.pagehelper.cache.Cache;
import com.github.pagehelper.cache.CacheFactory;
import com.github.pagehelper.util.MSUtils;
import com.github.pagehelper.util.StringUtil;
@SuppressWarnings({ "rawtypes", "unchecked" })
@Intercepts({
@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class }),
@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }), })
public class MumuPageInterceptor implements Interceptor {
//private static final Logger logger = LoggerFactory.getLogger(MumuPageInterceptor.class);
// 缓存count查询的ms
protected Cache<String, MappedStatement> msCountMap = null;
private Dialect dialect;
private String default_dialect_class = "com.github.pagehelper.PageHelper";
private Field additionalParametersField;
private String countSuffix = "_COUNT";
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
// 由于逻辑关系,只会进入一次
if (args.length == 4) {
// 4 个参数时
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
// 6 个参数时
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
List resultList;
// 调用方法判断是否需要进行分页,如果不需要,直接返回结果
if (!dialect.skip(ms, parameter, rowBounds)) {
// 反射获取动态参数
String msId = ms.getId();
Configuration configuration = ms.getConfiguration();
Map<String, Object> additionalParameters = (Map<String, Object>) additionalParametersField
.get(boundSql);
// 判断是否需要进行 count 查询
if (dialect.beforeCount(ms, parameter, rowBounds)) {
String countMsId = msId + countSuffix;
Long count;
// 先判断是否存在手写的 count 查询
MappedStatement countMs = getExistedMappedStatement(configuration, countMsId);
if (countMs != null) {
count = executeManualCount(executor, countMs, parameter, boundSql, resultHandler);
} else {
countMs = msCountMap.get(countMsId);
// 自动创建
if (countMs == null) {
// 根据当前的 ms 创建一个返回值为 Long 类型的 ms
countMs = MSUtils.newCountMappedStatement(ms, countMsId);
msCountMap.put(countMsId, countMs);
}
count = executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler);
}
// 处理查询总数
// 返回 true 时继续分页查询,false 时直接返回
if (!dialect.afterCount(count, parameter, rowBounds)) {
// 当查询总数为 0 时,直接返回空的结果
return dialect.afterPage(new ArrayList(), parameter, rowBounds);
}
}
// 判断是否需要进行分页查询
if (dialect.beforePage(ms, parameter, rowBounds)) {
// 生成分页的缓存 key
CacheKey pageKey = cacheKey;
// 处理参数对象
parameter = dialect.processParameterObject(ms, parameter, boundSql, pageKey);
// 调用方言获取分页 sql
String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
// pageSql.toString());
/**
* 对sql进行改造
*/
String changer = "";
int limitCount = 0;
if (pageSql.contains(PageSqlResolver.LIMIT_SIGN_EX)) {
changer = PageSqlResolver.LIMIT_SIGN_EX;
limitCount = 2;
} else if (pageSql.contains(PageSqlResolver.LIMIT_SIGN)) {
changer = PageSqlResolver.LIMIT_SIGN;
limitCount = 1;
}
pageSql = PageSqlResolver.resolveLimit(pageSql, changer);
List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();
// 参数调度
/** 参数偏移,需要调整limit 参数 */
if (pageSql.contains(PageSqlResolver.SQL_SIGN)) {
String sqlInCount = pageSql.substring(pageSql.indexOf(PageSqlResolver.SQL_SIGN));
// 统计limitable之后还有多少个?//
Integer count = 0;
for (int i = 0; i < sqlInCount.length(); i++) {
if (sqlInCount.indexOf("?", i) != -1) {
i = sqlInCount.indexOf("?", i);
count++;
}
}
// 获取临时的list
List<ParameterMapping> tempParameterMappingList = boundSql.getParameterMappings();
// count>0则 limit后面有参数,需要将limit的参数往前提
int size = tempParameterMappingList.size();
if (count > 0) {
// 只需要移动一个
if (limitCount == 1) {
// 获取最后一个参数
ParameterMapping p1 = tempParameterMappingList.remove(size - 1);
// 将参数插入到指定位置
tempParameterMappingList.add((size - 1 - count), p1);
}
// 需要移动两个
else if (limitCount == 2) {
// 获取最后两个参数,p1为倒数第一个 p2为倒数第二个
ParameterMapping p1 = tempParameterMappingList.remove(size - 1);
ParameterMapping p2 = tempParameterMappingList.remove(size - 2);
// 将参数插入到指定位置
tempParameterMappingList.add((size - 2 - count), p1);
tempParameterMappingList.add((size - 2 - count), p2);
}
}
parameterMappingList = tempParameterMappingList;
}
// parameterMappingList.toString());
BoundSql pageBoundSql = new BoundSql(configuration, pageSql, parameterMappingList, parameter);
// 设置动态参数
for (String key : additionalParameters.keySet()) {
pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
// 执行分页查询
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
} else {
// 不执行分页的情况下,也不执行内存分页
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
}
} else {
// rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
return dialect.afterPage(resultList, parameter, rowBounds);
} finally
{
dialect.afterAll();
}
}
/**
* 执行手动设置的 count 查询,该查询支持的参数必须和被分页的方法相同
*
* @param executor
* @param countMs
* @param parameter
* @param boundSql
* @param resultHandler
* @return
* @throws IllegalAccessException
* @throws SQLException
*/
private Long executeManualCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql,
ResultHandler resultHandler) throws IllegalAccessException, SQLException {
CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
BoundSql countBoundSql = countMs.getBoundSql(parameter);
Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey,
countBoundSql);
Long count = ((Number) ((List) countResultList).get(0)).longValue();
return count;
}
/**
* 执行自动生成的 count 查询
*
* @param executor
* @param countMs
* @param parameter
* @param boundSql
* @param rowBounds
* @param resultHandler
* @return
* @throws IllegalAccessException
* @throws SQLException
*/
private Long executeAutoCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql,
RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException, SQLException {
Map<String, Object> additionalParameters = (Map<String, Object>) additionalParametersField.get(boundSql);
// 创建 count 查询的缓存 key
CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
// 调用方言获取 count sql
String countSql = dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
// countKey.update(countSql);
BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(),
parameter);
// 当使用动态 SQL 时,可能会产生临时的参数,这些参数需要手动设置到新的 BoundSql 中
for (String key : additionalParameters.keySet()) {
countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
// 执行 count 查询
Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey,
countBoundSql);
Long count = (Long) ((List) countResultList).get(0);
return count;
}
/**
* 尝试获取已经存在的在 MS,提供对手写count和page的支持
*
* @param configuration
* @param msId
* @return
*/
private MappedStatement getExistedMappedStatement(Configuration configuration, String msId) {
MappedStatement mappedStatement = null;
try {
mappedStatement = configuration.getMappedStatement(msId, false);
} catch (Throwable t) {
// ignore
}
return mappedStatement;
}
@Override
public Object plugin(Object target) {
// TODO Spring bean 方式配置时,如果没有配置属性就不会执行下面的 setProperties
// 方法,就不会初始化,因此考虑在这个方法中做一次判断和初始化
// TODO https://github.com/pagehelper/Mybatis-PageHelper/issues/26
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 缓存 count ms
msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);
String dialectClass = properties.getProperty("dialect");
if (StringUtil.isEmpty(dialectClass)) {
dialectClass = default_dialect_class;
}
try {
Class<?> aClass = Class.forName(dialectClass);
dialect = (Dialect) aClass.newInstance();
} catch (Exception e) {
throw new PageException(e);
}
dialect.setProperties(properties);
String countSuffix = properties.getProperty("countSuffix");
if (StringUtil.isNotEmpty(countSuffix)) {
this.countSuffix = countSuffix;
}
try {
// 反射获取 BoundSql 中的 additionalParameters 属性
additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters");
additionalParametersField.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new PageException(e);
}
}
static class PageSqlResolver {
public static final String SQL_SIGN = "AS limitable";
public static final String LIMIT_SIGN = "LIMIT ?";
public static final String LIMIT_SIGN_EX = "LIMIT ?, ?";
public static String resolveLimit(String pageSql, String changer) {
if (pageSql == null) {
return null;
}
if (pageSql.contains(SQL_SIGN)) {// 如果需要特殊分页
pageSql = pageSql.replace(changer, "");
StringBuilder sqlBuilder = new StringBuilder(pageSql);
StringBuilder mae = new StringBuilder(sqlBuilder.substring(0, sqlBuilder.indexOf(SQL_SIGN)));// mae
// limitable
StringBuilder uShiRo = new StringBuilder(
sqlBuilder.substring(sqlBuilder.indexOf(SQL_SIGN), sqlBuilder.length()));// 剩余的
mae.insert(mae.lastIndexOf(")"), String.format(" %s", changer));
return mae.append(uShiRo).toString();
} else {
return pageSql;
}
}
}
}
配置好pagehelper拦截器,再添加一个类去使用
import javax.sql.DataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import com.clcreate.interceptor.MumuPageInterceptor;
import com.github.pagehelper.autoconfigure.PageHelperProperties;
@Configuration
public class MybatisConfiguration {
@Bean
public SqlSessionFactory sqlSessionFactoryBean(@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setTypeAliasesPackage("com.clcreate.entity");//实体类
// 配置分页插件,详情请查阅官方文档
PageHelperProperties properties = new PageHelperProperties();
properties.setPageSizeZero("true");// 分页尺寸为0时查询所有纪录不再执行分页
properties.setReasonable("true");// 页码<=0 查询第一页,页码>=总页数查询最后一页
properties.setSupportMethodsArguments("true");// 支持通过 Mapper 接口参数来传递分页参数
MumuPageInterceptor pageInterceptor = new MumuPageInterceptor();
pageInterceptor.setProperties(properties.getProperties());
// 添加插件
factory.setPlugins(new Interceptor[]{pageInterceptor});
// 添加XML目录
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
factory.setMapperLocations(resolver.getResources("classpath*:com.clcreate.mapper/*.xml"));//sql语句xml
return factory.getObject();
}
}
如果SpringBoot集成PageHelper时出现“在系统中发现了多个分页插件,请检查系统配置!”出现这个问题的,在启动类加上
@SpringBootApplication(exclude = PageHelperAutoConfiguration.class)
不想加这个的,可以看看这个作者的这篇文章 (转载) https://blog.csdn.net/mmlik8878/article/details/88322723
来源:CSDN
作者:老火锅
链接:https://blog.csdn.net/lianghaigui/article/details/104671316