项目中常用的分页查询方式及原理

1.mybatis-plus自带的分页方法

通过调用selectPage方法实现分页

示例代码如下:

@Override
    public PageResult<RegionResDTO> page(RegionPageQueryReqDTO regionPageQueryReqDTO) {
        Page<Region> page = PageUtils.parsePageQuery(regionPageQueryReqDTO, Region.class);
        Page<Region> serveTypePage = baseMapper.selectPage(page, new QueryWrapper<>());
        return PageUtils.toPage(serveTypePage, RegionResDTO.class);
    }

2.pagehelper分页组件

此方法适用于自定义sql的分页查询,比如涉及到多表操作的问题,就不能用方法一了,使用时要引入对应的依赖。

示例代码如下:

@Override
    public PageResult<ServeResDTO> page(ServePageQueryReqDTO servePageQueryReqDTO) {
        return PageHelperUtils.selectPage(servePageQueryReqDTO, () -> baseMapper.queryServeListByRegionId(servePageQueryReqDTO.getRegionId()));
    }
  • servePageQueryReqDTO:这是传递给 selectPage 方法的第一个参数,它包含了分页查询所需的各种信息,如当前页码、每页显示的记录数等。
  • () -> baseMapper.queryServeListByRegionId(servePageQueryReqDTO.getRegionId()):这是一个Lambda表达式,作为 selectPage 方法的第二个参数。

实现分页插件的原理,我们阅读源代码:

public static <T> PageResult<T> selectPage(PageQueryDTO pageQueryDTO, QueryExecutor<T> condition) {
        PageHelper.startPage(pageQueryDTO.getPageNo().intValue(), pageQueryDTO.getPageSize().intValue(), getOrder(pageQueryDTO));
        List<T> data = condition.query();
        if (data instanceof Page) {
            Page page = (Page)data;
            return new PageResult((long)page.getPages() * 1L, page.getTotal(), data);
        } else {
            long total = (long)CollUtils.size(data);
            long pages = total % pageQueryDTO.getPageSize() == 0L ? total / pageQueryDTO.getPageSize() : total / pageQueryDTO.getPageSize() + 1L;
            return new PageResult(pages, total, data);
        }
    }

首先通过调用PageHelper.startPage方法设置分页参数,一层层进入源码可以知道,最终将分页参数设置到ThreadLocal<Page>类型的静态变量LOCAL_PAGE中

即设置到线程变量里,传参除了作为形参传递以外,还可以用这种方式。ThreadLocal是Java中提供的一种线程局部变量机制,它允许每个线程都拥有该变量的一个独立副本,从而避免了多线程环境下的数据共享问题。调用selectPage方法会起一个线程,将变量设置到这个线程里,需要分页参数时,可以直接从ThreadLocal中拿,简化了分页查询的代码实现,提高系统的可维护性和可扩展性。

通过PageInterceptor拦截器拦截Mybatis的Executor的query方法得到原始的sql语句,首先得到count总数,然后从ThreadLocal中取出分页参数,在原始的sql语句中添加分页参数查询分页数据。

每次分页查询之后清ThreadLocal中的分页参数,以免影响后面SQL语句的执行。

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) {
                boundSql = ms.getBoundSql(parameter);
                cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
            } else {
                cacheKey = (CacheKey)args[4];
                boundSql = (BoundSql)args[5];
            }

            this.checkDialectExists();
            List resultList;
            if (!this.dialect.skip(ms, parameter, rowBounds)) {
                if (this.dialect.beforeCount(ms, parameter, rowBounds)) {
                    Long count = this.count(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                    if (!this.dialect.afterCount(count, parameter, rowBounds)) {
                        Object var12 = this.dialect.afterPage(new ArrayList(), parameter, rowBounds);
                        return var12;
                    }
                }

                resultList = ExecutorUtil.pageQuery(this.dialect, executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
            } else {
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }

            Object var16 = this.dialect.afterPage(resultList, parameter, rowBounds);
            return var16;
        } finally {
            if (this.dialect != null) {
                this.dialect.afterAll();
            }

        }
    }

 跟进ExecutorUtil.pageQuery()

public static <E> List<E> pageQuery(Dialect dialect, Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, CacheKey cacheKey) throws SQLException {
        if (!dialect.beforePage(ms, parameter, rowBounds)) {
            return executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
        } else {
            CacheKey pageKey = cacheKey;
            parameter = dialect.processParameterObject(ms, parameter, boundSql, pageKey);
            String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
            BoundSql pageBoundSql = new BoundSql(ms.getConfiguration(), pageSql, boundSql.getParameterMappings(), parameter);
            Map<String, Object> additionalParameters = getAdditionalParameter(boundSql);
            Iterator var12 = additionalParameters.keySet().iterator();

            while(var12.hasNext()) {
                String key = (String)var12.next();
                pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
            }

            return executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
        }
    }

跟进dialect.getPageSql方法可以看到获取分页参数

public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {
        String sql = boundSql.getSql();
        Page page = this.getLocalPage();
        String orderBy = page.getOrderBy();
        if (StringUtil.isNotEmpty(orderBy)) {
            pageKey.update(orderBy);
            sql = OrderByParser.converToOrderBySql(sql, orderBy);
        }

        return page.isOrderByOnly() ? sql : this.getPageSql(sql, page, pageKey);
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值