SpringBoot优雅的实现分页功能及原理简析

前言

分页功能是我们开发过程中常见的一个小功能,如何快速高效的实现一个分页功能呢。本文将带领大家快速搭建一个分页功能。

引入依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
     <version>5.1.2</version>
</dependency>

实体封装

接收分页参数实体封装

需要分页的实体继承该实体,用以接收分页的参数 

public class BasePageReqListMsg {
    
    protected final static int SIZE = 20;
    protected final static int NO = 1;

    protected int pageNum=NO;
    protected int pageSize = SIZE;

    public static int getInitSize() {
        return SIZE;
    }


    public int getPageNum() {
        return pageNum;
    }


    public int getPageSize() {
        return pageSize;
    }

  
    public int getStartIndex() {
        return (getPageNum() - 1) * this.pageSize;
    }
    
    public int getEndIndex() {
        return getPageNum() * this.pageSize;
    }
}

封装返回分页实体

该实体将分页数据封装,返给前端 

@Data
public class PageResult<T> {

    private Integer pageNum;

    private Integer pageSize;

    private Long total;

    private List<T> list;

    public PageResult() {

    }

    public PageResult(Integer pageNum, Integer pageSize, Long total, List<T> list) {
        this.pageNum = pageNum;
        this.pageSize = pageSize;
        this.total = total;
        this.list = list;
    }

    public static PageResult getResult(int pageNum, int pageSize, long total, List result) {
        return new PageResult(pageNum, pageSize, total, result);
    }

    public static PageResult getResult(int pageNum, int pageSize) {
        return new PageResult(pageNum, pageSize, 0L, new ArrayList());
    }
}

分页查询

public PageResult<User> queryByParamPageList(QueryDTO queryDTO) {
        PageHelper.startPage(queryDTO.getPageNum(), queryDTO.getPageSize());
        List<User> users=  userMapper.selectByParam(queryDTO);
        PageInfo<User> pageInfo = new PageInfo<>(users);
        return new PageResult<>(pageInfo.getPageNum(), pageInfo.getPageSize(), pageInfo.getTotal(), users);
    }

源码讲解

分页是从这个方法开始的

/**
 * 开始分页
 *
 * @param pageNum      页码
 * @param pageSize     每页显示数量
 * @param count        是否进行count查询
 * @param reasonable   分页合理化,null时用默认配置
 * @param pageSizeZero true且pageSize=0时返回全部结果,false时分页,null时用默认配置
 */
public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
	Page<E> page = new Page<E>(pageNum, pageSize, count);
	page.setReasonable(reasonable);
	page.setPageSizeZero(pageSizeZero);
	//当已经执行过orderBy的时候
	Page<E> oldPage = getLocalPage();
	if (oldPage != null && oldPage.isOrderByOnly()) {
		page.setOrderBy(oldPage.getOrderBy());
	}
	setLocalPage(page);
	return page;
}

跟进setLocalPage(page),是讲分页参数放到了ThreadLocal里了。ThreadLocal的讲解参见:

https://blog.csdn.net/zhangzijiejiayou/article/details/103425789 

protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
protected static boolean DEFAULT_COUNT = true;

/**
 * 设置 Page 参数
 *
 * @param page
 */
protected static void setLocalPage(Page page) {
	LOCAL_PAGE.set(page);
}

/**
 * 获取 Page 参数
 *
 * @return
 */
public static <T> Page<T> getLocalPage() {
	return LOCAL_PAGE.get();
}

PageInterceptor是分页的核心实现了mybatis的拦截器Interceptor,进行分页拦截和频接核心方法如下

public class PageInterceptor implements Interceptor {
//缓存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);
				BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), 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();
	}
}

 参考

http://www.dengb.com/Javabc/1381088.html 

 

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值