• Mybatis分页插件2.0版本号公布


    项目地址:http://git.oschina.net/free/Mybatis_PageHelper

    软件介绍:http://www.oschina.net/p/mybatis_pagehelper

    分页插件演示样例: http://blog.csdn.net/isea533/article/details/24700339


    v2.0更新内容:

    1. 支持Mybatis缓存。count和分页同一时候支持(二者同步)
    2. 改动拦截器签名。拦截Executor,签名例如以下:
      @Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})) 
    3. 将Page类移到外面,方便调用


        有位朋友留言说插件不支持缓存。在使用缓存的情况下无法正常执行。


        这个问题确实存在,由于缓存是CachingExecutor在v1.0版本号中的拦截器之前进行的,也就是说,当查询结果被缓存后。使用缓存的时候是进不到分页插件中的。并且分页插件无法取得返回的结果。因而不能正常执行,既然找到了原因,并且Executor也能够被拦截。我就在想是否能直接拦截Executor并且支持缓存。


        通过查看源代码以及调试。找到一种可行的方法。而且经过多项測试到最后完毕,共计4小时。我測试的内容有缓存/不缓存两种情况,以及返回为resultType/resultMap两种情况,共计4种情况下的使用情况。

    測试仍然不算全面,所以我希望使用该插件的各位朋友遇到问题能及时反馈。


        这次更新和前一个版本号的实现方法毫无关联。并且也从来没人这么做过。所以有问题请及时反馈,以下说说详细的实现方法。


        首先拦截器签名变了:

    1 @Intercepts(@Signature(type = Executor.class,
    2                        method = "query",
    3                        args = {MappedStatement.class,
    4                                Object.class,
    5                                RowBounds.class,
    6                                ResultHandler.class}))


        查看完整代码请点击这里,以下讲一些实现的细节:

    1 List<SqlNode> contents = (List<SqlNode>) msObject.getValue("sqlSource.rootSqlNode.contents");
        这段代码获取了SqlNode集合,Mybatis要运行的Sql从通过这个集合拼出来了,在第一个版本号的中的BoundSql就是通过contents中的内容生成的, 所以这样的分页方式绝对是意想不到的。获取这个对象后,做了例如以下处理:
    1 //求count - 重写sql
    2 contents.add(0new TextSqlNode("select count(0) from ("));
    3 contents.add(new TextSqlNode(")"));

        往第一行插入了求count的sql,最后一行加了一个“)”,通过头尾加入的这两个,就让这一个正常的sql语句变成了一个求count的sql,以下这一句代码:

    1 Class<?> resultType = (Class<?

    >) msObject.getValue("resultMaps[0].type");

        这一句代码的作用是先把正常的返回值类型保存,然后做了例如以下改动:

    1 msObject.setValue("resultMaps[0].type"int.class);

        将返回值改为int类型。用于接收返回的count总数,另一句非常重要的代码:

    1 List<ResultMapping> resultMappings = (List<ResultMapping>) msObject.getValue("resultMaps[0].resultMappings");

        这里也是先把resultMappings备份,这个值在使用resultMap的情况会对返回结果造成干扰。所以这里先备份,然后用一个空的list去取代:

    1 msObject.setValue("resultMaps[0].resultMappings", EMPTY_RESULTMAPPING);


        然后就去运行这个改动后的sql:

    01 //查询总数
    02 Object result = null;
    03 try {
    04     result = invocation.proceed();
    05     int totalCount = Integer.parseInt(((List) result).get(0).toString());
    06     page.setTotal(totalCount);
    07     int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 1);
    08     page.setPages(totalPage);
    09 finally {
    10     //清理count sql
    11     contents.remove(0);
    12     contents.remove(contents.size() - 1);
    13     //恢复类型
    14     msObject.setValue("resultMaps[0].type", resultType);
    15     msObject.setValue("resultMaps[0].resultMappings", resultMappings);
    16 }

        由于这里运行了result = invocation.proceed()方法,所以在开启缓存的情况下,该sql也会被缓存,这里将结果totalCount保存到Page,然后在finally中运行了清理sql的方法和恢复类型的方法。这样就又变成了原来的sql。

        这里通过运行过程中的截图来看一下实际效果:


    添加头尾的count语句


    运行的sql

        后面使用同样的原理改动为分页SQL:

    1 //分页sql
    2 contents.add(0new TextSqlNode("select * from ( select temp.*, rownum row_id from ( "));
    3 StringBuilder pageSql = new StringBuilder(200);
    4 pageSql.append(" ) temp where rownum <= ").append(page.getEndRow());
    5 pageSql.append(") where row_id > ").append(page.getStartRow());
    6 contents.add(new TextSqlNode(pageSql.toString()));

        运行过程图:


    添加头尾的分页sql


    运行的sql


        最后运行改动后的sql,保存处理结果:

    01 //将运行权交给下一个拦截器
    02 try {
    03     result = invocation.proceed();
    04 finally {
    05     //清理分页sql
    06     contents.remove(0);
    07     contents.remove(contents.size() - 1);
    08 }
    09 //得到处理结果
    10 page.setResult((List) result);
    11 //返回结果
    12 return result;

        这里仍然不能忘记在finally中清理sql。


        通过这样的方法,全然使用了Mybatis的内容来操作,并且能够支持缓存。缓存对求count语句来说效果更明显,由于你查看下一页内容的时候不会反复运行count。会使用缓存。并且分页sql和count sql是同步缓存的。所以不用操心数据不一致的情况。并且这样的分页方式不影响原方法的单独运行(不分页)。


         假设对你有帮助,或者有什么建议欢迎留言!


         转载请注明原文地址和作者。

  • 相关阅读:
    iOS
    2016-北京游线路规划
    OC 组合实现多继承
    如何把项目托管到GitHub
    iOS6和iOS7代码的适配(2)——status bar
    iOS6和iOS7代码的适配(1)
    Xcode ARC需要什么版本的环境支持
    内容页直接输出图集函数及使用方法
    vue-cli如何引入bootstrap工具
    实现Sublime Text3中vue文件高亮显示的最有效的方法
  • 原文地址:https://www.cnblogs.com/mfmdaoyou/p/6891343.html
Copyright © 2020-2023  润新知