MyBatis自定义查询对象触发PageHelper隐式分页解析

MyBatis自定义查询对象触发PageHelper隐式分页解析,第1张

MyBatis自定义查询对象触发PageHelper隐式分页解析

先说结论,满足以下两点即可触发PageHelper隐式分页:

1、application.properties/application.yml中配置了pagehelper.support-methods-arguments=true

2、自定义查询对象中同时包含pageNum和pageSize属性,且可以转换成数字类型

下面从源码进行分析,为什么在进行MyBatis查询的时候,我明明没有调用PageHelper.startPage方法,但是却自动进行了分页。

1、PageHelperAutoConfiguration 自动配置类

@PostConstruct
public void addPageInterceptor() {
    PageInterceptor interceptor = new PageInterceptor();
    Properties properties = new Properties();
    properties.putAll(this.pageHelperProperties());
    properties.putAll(this.properties.getProperties());
    interceptor.setProperties(properties);
    Iterator var3 = this.sqlSessionFactoryList.iterator();

    while(var3.hasNext()) {
        SqlSessionFactory sqlSessionFactory = (SqlSessionFactory)var3.next();
        sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
    }

}

我们从PageHelper的自动配置类入手,在PageHelper的配置类中,会创建一个PageInterceptor拦截器。

2、PageInterceptor 分页拦截器

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();
        if (this.dialect instanceof Chain) {
            boundSql = ((Chain)this.dialect).doBoundSql(Type.ORIGINAL, boundSql, cacheKey);
        }

        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)null, 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;
    }
}

this.dialect.skip方法判断是否跳过分页,其实也就是查看是否具备分页的条件。

3、PageHelper

public boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
    if (ms.getId().endsWith("_COUNT")) {
        throw new RuntimeException("在系统中发现了多个分页插件,请检查系统配置!");
    } else {
        Page page = this.pageParams.getPage(parameterObject, rowBounds);
        if (page == null) {
            return true;
        } else {
            if (StringUtil.isEmpty(page.getCountColumn())) {
                page.setCountColumn(this.pageParams.getCountColumn());
            }

            this.autoDialect.initDelegateDialect(ms);
            return false;
        }
    }
}

this.pageParams.getPage方法是从参数中获取分页信息。

4、PageParams  分页参数

public Page getPage(Object parameterObject, RowBounds rowBounds) {
    Page page = PageHelper.getLocalPage();
    if (page == null) {
        if (rowBounds != RowBounds.DEFAULT) {
            if (this.offsetAsPageNum) {
                page = new Page(rowBounds.getOffset(), rowBounds.getLimit(), this.rowBoundsWithCount);
            } else {
                page = new Page(new int[]{rowBounds.getOffset(), rowBounds.getLimit()}, this.rowBoundsWithCount);
                page.setReasonable(false);
            }

            if (rowBounds instanceof PageRowBounds) {
                PageRowBounds pageRowBounds = (PageRowBounds)rowBounds;
                page.setCount(pageRowBounds.getCount() == null || pageRowBounds.getCount());
            }
        } else if (parameterObject instanceof IPage || this.supportMethodsArguments) {
            try {
                page = PageObjectUtil.getPageFromObject(parameterObject, false);
            } catch (Exception var5) {
                return null;
            }
        }

        if (page == null) {
            return null;
        }

        PageHelper.setLocalPage(page);
    }
    return page;
}

如果this.supportMethodsArguments为true,也就是在application.properties/application.yml中配置了pagehelper.support-methods-arguments=true,就会执行PageObjectUtil.getPageFromObject方法,从参数中提取分页信息。

5、PageObjectUtil

public static  Page getPageFromObject(Object params, boolean required) {
    if (params == null) {
        throw new PageException("无法获取分页查询参数!");
    } else if (params instanceof IPage) {
        IPage pageParams = (IPage)params;
        Page page = null;
        if (pageParams.getPageNum() != null && pageParams.getPageSize() != null) {
            page = new Page(pageParams.getPageNum(), pageParams.getPageSize());
        }

        if (StringUtil.isNotEmpty(pageParams.getOrderBy())) {
            if (page != null) {
                page.setOrderBy(pageParams.getOrderBy());
            } else {
                page = new Page();
                page.setOrderBy(pageParams.getOrderBy());
                page.setOrderByonly(true);
            }
        }

        return page;
    } else {
        metaObject paramsObject = null;
        if (hasRequest && requestClass.isAssignableFrom(params.getClass())) {
            try {
                paramsObject = metaObjectUtil.forObject(getParameterMap.invoke(params));
            } catch (Exception var11) {
            }
        } else {
            paramsObject = metaObjectUtil.forObject(params);
        }

        if (paramsObject == null) {
            throw new PageException("分页查询参数处理失败!");
        } else {
            Object orderBy = getParamValue(paramsObject, "orderBy", false);
            boolean hasOrderBy = false;
            if (orderBy != null && orderBy.toString().length() > 0) {
                hasOrderBy = true;
            }

            int pageNum;
            int pageSize;
            Object _count;
            try {
                Object _pageNum = getParamValue(paramsObject, "pageNum", required);
                _count = getParamValue(paramsObject, "pageSize", required);
                if (_pageNum == null || _count == null) {
                    if (hasOrderBy) {
                        Page page = new Page();
                        page.setOrderBy(orderBy.toString());
                        page.setOrderByonly(true);
                        return page;
                    }

                    return null;
                }

                pageNum = Integer.parseInt(String.valueOf(_pageNum));
                pageSize = Integer.parseInt(String.valueOf(_count));
            } catch (NumberFormatException var12) {
                throw new PageException("分页参数不是合法的数字类型!", var12);
            }

            Page page = new Page(pageNum, pageSize);
            _count = getParamValue(paramsObject, "count", false);
            if (_count != null) {
                page.setCount(Boolean.valueOf(String.valueOf(_count)));
            }

            if (hasOrderBy) {
                page.setOrderBy(orderBy.toString());
            }

            Object reasonable = getParamValue(paramsObject, "reasonable", false);
            if (reasonable != null) {
                page.setReasonable(Boolean.valueOf(String.valueOf(reasonable)));
            }

            Object pageSizeZero = getParamValue(paramsObject, "pageSizeZero", false);
            if (pageSizeZero != null) {
                page.setPageSizeZero(Boolean.valueOf(String.valueOf(pageSizeZero)));
            }

            return page;
        }
    }
}

可以看到,如果是自定义的参数对象,会将对象的属性都解析出来。如果同时存在pageNum和pageSize属性,并且能转化为数字类型,则创建并向上级返回Page对象,然后在拦截器中调用ExecutorUtil.pageQuery方法进行分页查询。

至此,就能明白为什么会触发PageHelper的隐式分页了。

PS:如果在使用MyBatis时,未对参数进行对象的封装,采用直接传多个参数的方式,如下形式:

List getStudents(@Param("pageNum")Integer num, @Param("pageSize")Integer size, @Param("age")Integer age);

也是可以触发隐式分页的。这是由于@Param指定的参数名为pageNum和pageSize,导致这两个参数会被提取。如果改成@Param("pageNo")和@Param("pageSize"),便不会触发。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5710191.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存