支持连表等复杂查询的PageHelper修改 —— 支持PageHelper5.1.4 Mybatis3.4.6

这是旧版本的做法以及一些当时遇到的问题《支持连表等复杂查询的PageHelper修改》

不想看理论分析的可以点击这里直接移步: 六、改造步骤详解

一、PageHelper中分页存在的问题:

- 主表结构如下:

provider_order表,订单主表。我们这里简化处理,它只有一个字段,即id。


VARCHAR  |  po_id   |    订单表id


- 从表结构如下:

order_info表,订单从表。它负责记录订单的一些信息,与主表是一对多的关系。


VARCHAR  |  oi_id  |  订单信息id
VARCHAR  |  po_id   |    订单表id
TEXT    |  info   |   信息详情


  • 现在我们需要一个简单的连表查询操作,sql语句如下:
    <select id="getProviderOrderWithOrderInfoTest" resultMap="ResultMapWithOrderInfo" parameterType="java.lang.String">
        SELECT *
        FROM (SELECT * FROM provider_order WHERE po_id LIKE #{po_id}) AS limitable
            LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id
    </select>
  • 进行PageHelper的分页操作,查询前十条记录:
        Page page = PageHelper.startPage(0, 10);
        List<ProviderOrder> providerOrderList = testService.getProviderOrderWithOrderInfoTest("%-%");
        PageInfo<ProviderOrder> pageInfo = new PageInfo<>(providerOrderList);

        System.out.println(pageInfo);
  • 目前我们的主表有两条记录,从表有二十条记录。

这里写图片描述

我们会发现,查询出来的总数是20,然后查询结果是两个list(各5条从表信息),不仅如此,分页数据也很奇怪,我们只有两条主表记录,但却被分成了两页(pages = 2),并且hasNextPage = true。这并不是我们要的结果。我们想要查询的效果是:

前十条 订单 记录,然后连表查询出该十条订单记录下的所有 订单信息

也就是说,结果集中,首先我们应该得到两个list,list中各10条从表信息,而且也不该有下一页,总页数应为1,总数据数应为2。

查看PageHelper的源码我们来看看,它的count,以及limit,到底是怎么回事。

二、 PageHelper的count实现

    private Long executeAutoCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql, RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException,
        SQLException {
        Map<String, Object> additionalParameters = (Map) this.additionalParametersField.get(boundSql);
        CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
        String countSql = this.dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
        BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);
        Iterator var11 = additionalParameters.keySet()
                                             .iterator();

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

        Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
        Long count = (Long) ((List) countResultList).get(0);

        // 获取count
        return count;
    }

我们很容易看出来,count的sql语句,在这里经过了处理,这里调用了dialect的getCountSql方法。
它的实现如下:

    public String getSmartCountSql(String sql, String name) {
        Statement stmt = null;
        if (sql.indexOf("/*keep orderby*/") >= 0) {
            return this.getSimpleCountSql(sql);
        } else {
            try {
                stmt = CCJSqlParserUtil.parse(sql);
            } catch (Throwable var8) {
                return this.getSimpleCountSql(sql);
            }

            Select select = (Select)stmt;
            SelectBody selectBody = select.getSelectBody();

            try {
                this.processSelectBody(selectBody);
            } catch (Exception var7) {
                return this.getSimpleCountSql(sql);
            }

            this.processWithItemsList(select.getWithItemsList());
            this.sqlToCount(select, name);
            String result = select.toString();
            return result;
        }
    }

    public String getSimpleCountSql(String sql) {
        return this.getSimpleCountSql(sql, "0");
    }

    public String getSimpleCountSql(String sql, String name) {
        StringBuilder stringBuilder = new StringBuilder(sql.length() + 40);
        stringBuilder.append("select count(");
        stringBuilder.append(name);
        stringBuilder.append(") from (");
        stringBuilder.append(sql);
        stringBuilder.append(") tmp_count");
        return stringBuilder.toString();
    }

简单来说,就是在sql外面套一层select count语句,我们的sql语句变成了 SELECT count(0) FROM (我们的sql语句)(真实情况没有这么简单,这只是其中一种情况。)这样就很容易得知,为什么最后的total是20(所有数据数),而不是2(主表数据数)了。

这里写图片描述

三、 PageHelper的分页实现

与上面相同,这里以dialect(为了适配各种数据库)为mysql为例,我们可以看到,如果你没有在sql语句里面写limit,就会在sql语句的最后面,添加limit语句。

    public String getPageSql(String sql, RowBounds rowBounds, CacheKey pageKey) {
        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
        sqlBuilder.append(sql);
        if (rowBounds.getOffset() == 0) {
            sqlBuilder.append(" LIMIT ");
            sqlBuilder.append(rowBounds.getLimit());
        } else {
            sqlBuilder.append(" LIMIT ");
            sqlBuilder.append(rowBounds.getOffset());
            sqlBuilder.append(",");
            sqlBuilder.append(rowBounds.getLimit());
            pageKey.update(rowBounds.getOffset());
        }

        pageKey.update(rowBounds.getLimit());
        return sqlBuilder.toString();
    }

程序运行得到sql语句如下,PageHelper对查询的总结果集(包括主表数据与从表数据),进行了分页

这里写图片描述

在得知PageHelper在这两个步骤的原理后,我们开始对PageHelper进行改造。

四、 count结果修正

在上面的count源码中,我们可以看到这么一段代码:

    public String getSmartCountSql(String sql, String name) {
        Statement stmt = null;
        if (sql.indexOf("/*keep orderby*/") >= 0) {
            return this.getSimpleCountSql(sql);
        } else {
            try {
                stmt = CCJSqlParserUtil.parse(sql);
            } catch (Throwable var8) {
                return this.getSimpleCountSql(sql);
            }

            Select select = (Select)stmt;
            SelectBody selectBody = select.getSelectBody();

            
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值