13_pageHelper在mybatis的分析

如果我们要在mybatis中使用PageHelper,我们第一步需要将PageHelper配置到mybatis的配置中

<plugins>

                      <plugin interceptor="com.github.pagehelper.PageHelper">

                 <property name="dialect" value="mysql"/> 

             </plugin>

         </plugins>

 

还记得上篇文章中提到,在进行创建SqlSessionFactoryBean的时候会解析配置文件,然后会将plugin加入到interceptorChain中。而且我们也提到,在SqlSession进行doQuery的时候调用的是executor的query方法,而executor的创建是通过Configuration创建的,那么我们下面进入到Configuration的newExecutor的方法中。如下

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

    executorType = executorType == null ? defaultExecutorType : executorType;

    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;

    Executor executor;

    if (ExecutorType.BATCH == executorType) {

      executor = new BatchExecutor(this, transaction);

    } else if (ExecutorType.REUSE == executorType) {

      executor = new ReuseExecutor(this, transaction);

    } else {

      executor = new SimpleExecutor(this, transaction);

    }

    if (cacheEnabled) {

      executor = new CachingExecutor(executor);

    }

    executor = (Executor) interceptorChain.pluginAll(executor);

    return executor;

  }

从其中可以看到会调用所有的interceptor,然后调用interceptor的plugin方法,我们进入到PageHelper的plugin

  public Object plugin(Object target) {

        if (target instanceof Executor) {

            //通过调用mybatis的plugin方法进行包装。

            return Plugin.wrap(target, this);

        } else {

            return target;

        }

    }

 

进入Plugin的wrap方法

public static Object wrap(Object target, Interceptor interceptor) {

        //该方法是通过当前的拦截器,获取需要拦截的方法的入参,方法名等

       //通过解析Intercepts注解。

       //key为Executor.class, value为,配置的query,并且参数全部匹配

        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);

        //

        Class<?> type = target.getClass();

        //查看当前的目标对象的实现的接口以及父类实现的接口,看是否在上面的

        //map中。

        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);

        //存在,说明当前需要代理,返回代理对象。代理的尸体类为Plugin

        //

        return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;

    }

PageHelper对应的注解如下

@Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))

 

通过上面我们可以知道,调用query(MappedStatement, Object, RowBounds, ResultHandler)

下面进入Plugin的invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        try {

           //首先从刚才的map中查找是否需要进行拦截,即

           //是不是调用query(MappedStatement, Object, RowBounds, ResultHandler)

            Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());

            return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);

        } catch (Exception var5) {

            throw ExceptionUtil.unwrapThrowable(var5);

        }

    }

通过上面可知,在调用query(MappedStatement, Object, RowBounds, ResultHandler)

会调用PageHelper的intercept方法。

public Object intercept(Invocation invocation) throws Throwable {

        if (autoDialect) {

            //初始化一些数据,获取dataSource等。

            initSqlUtil(invocation);

        }

        return sqlUtil.processPage(invocation);

    }

    /**

     * 初始化sqlUtil

     *

     * @param invocation

     */

    public synchronized void initSqlUtil(Invocation invocation) {

        if (sqlUtil == null) {

            String url = null;

            try {

                MappedStatement ms = (MappedStatement) invocation.getArgs()[0];

                MetaObject msObject = SystemMetaObject.forObject(ms);

                DataSource dataSource = (DataSource) msObject.getValue("configuration.environment.dataSource");

                url = dataSource.getConnection().getMetaData().getURL();

            } catch (SQLException e) {

                throw new RuntimeException("分页插件初始化异常:" + e.getMessage());

            }

            if (url == null || url.length() == 0) {

                throw new RuntimeException("无法自动获取jdbcUrl,请在分页插件中配置dialect参数!");

            }

            String dialect = Dialect.fromJdbcUrl(url);

            if (dialect == null) {

                throw new RuntimeException("无法自动获取数据库类型,请通过dialect参数指定!");

            }

            sqlUtil = new SqlUtil(dialect);

            sqlUtil.setProperties(properties);

            properties = null;

            autoDialect = false;

        }

    }

进入到SqlUtil中的processPage

public Object processPage(Invocation invocation) throws Throwable {

        try {

            Object result = _processPage(invocation);

            return result;

        } finally {

             //清除线程变量

            clearLocalPage();

            OrderByHelper.clear();

        }

    }

进入processPage

private Object _processPage(Invocation invocation) throws Throwable {

         //获取执行当前方法的入参,由于当前已经限定了入参的个数以及各个类型,

       //所以可以强转

        final Object[] args = invocation.getArgs();

       

  RowBounds rowBounds = (RowBounds) args[2];

  //判断是否需要通过pageHelper进行分页。

        if (SqlUtil.getLocalPage() == null && rowBounds == RowBounds.DEFAULT) {

            if (OrderByHelper.getOrderBy() != null) {

                OrderByHelper.processIntercept(invocation);

            }

            return invocation.proceed();

        } else {

            //获取原始的ms

            MappedStatement ms = (MappedStatement) args[0];

            //判断并处理为PageSqlSource

            if (!isPageSqlSource(ms)) {

                processMappedStatement(ms, parser);

            }

            //忽略RowBounds-否则会进行Mybatis自带的内存分页

            args[2] = RowBounds.DEFAULT;

            //分页信息

            Page page = getPage(rowBounds);

            //pageSizeZero的判断

            if ((page.getPageSizeZero() != null && page.getPageSizeZero()) && page.getPageSize() == 0) {

                COUNT.set(null);

                //执行正常(不分页)查询

                Object result = invocation.proceed();

                //得到处理结果

                page.addAll((List) result);

                //相当于查询第一页

                page.setPageNum(1);

                //这种情况相当于pageSize=total

                page.setPageSize(page.size());

                //仍然要设置total

                page.setTotal(page.size());

                //返回结果仍然为Page类型 - 便于后面对接收类型的统一处理

                return page;

            }

 

            //简单的通过total的值来判断是否进行count查询

            if (page.isCount()) {

                COUNT.set(Boolean.TRUE);

                //替换MS,之前上面的processMappedStatement修改的MappedStatement

                args[0] = msCountMap.get(ms.getId());

                //查询总数,替换入参

                Object result = invocation.proceed();

                //把入参还原ms

                args[0] = ms;

                //设置总数

                page.setTotal((Integer) ((List) result).get(0));

                if (page.getTotal() == 0) {

                    return page;

                }

            }

            //pageSize>0的时候执行分页查询,pageSize<=0的时候不执行相当于可能只返回了一个count

            if (page.getPageSize() > 0 &&

                    ((rowBounds == RowBounds.DEFAULT && page.getPageNum() > 0)

                            || rowBounds != RowBounds.DEFAULT)) {

                //将参数中的MappedStatement替换为新的qs

                COUNT.set(null);

                BoundSql boundSql = ms.getBoundSql(args[1]);

                args[1] = parser.setPageParameter(ms, args[1], boundSql, page);

                COUNT.set(Boolean.FALSE);

                //执行分页查询

                Object result = invocation.proceed();

                //得到处理结果

                page.addAll((List) result);

            }

            //返回结果

            return page;

        }

    }

其中的if (!isPageSqlSource(ms)) {

                processMappedStatement(ms, parser);

}这句话很关键。会在这里创建一个新的MappedStatement。

public static void processMappedStatement(MappedStatement ms, Parser parser) throws Throwable {

        SqlSource sqlSource = ms.getSqlSource();

        MetaObject msObject = SystemMetaObject.forObject(ms);

        SqlSource tempSqlSource = sqlSource;

        if (sqlSource instanceof OrderBySqlSource) {

            tempSqlSource = ((OrderBySqlSource) tempSqlSource).getOriginal();

        }

        SqlSource pageSqlSource;

        if (tempSqlSource instanceof StaticSqlSource) {

            pageSqlSource = new PageStaticSqlSource((StaticSqlSource) tempSqlSource, parser);

        } else if (tempSqlSource instanceof RawSqlSource) {

            pageSqlSource = new PageRawSqlSource((RawSqlSource) tempSqlSource, parser);

        } else if (tempSqlSource instanceof ProviderSqlSource) {

            pageSqlSource = new PageProviderSqlSource((ProviderSqlSource) tempSqlSource, parser);

        } else if (tempSqlSource instanceof DynamicSqlSource) {

            pageSqlSource = new PageDynamicSqlSource((DynamicSqlSource) tempSqlSource, parser);

        } else {

            throw new RuntimeException("无法处理该类型[" + sqlSource.getClass() + "]的SqlSource");

        }

        msObject.setValue("sqlSource", pageSqlSource);

        //由于count查询需要修改返回值,因此这里要创建一个Count查询的MS

        msCountMap.put(ms.getId(), MSUtils.newCountMappedStatement(ms));

    }

通过上面我们可以看到,其实他的主要方式是通过新建替换MappedStatement来进行修改sql语句从而实现分页的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值