开源插件PageHelper分析

1.分页插件的资源

PageHelper中文说明
PageHelper使用手册
PageHelper使用时的注意事项

2.使用方法

测试代码参考
pom.xml中加入:

        
            com.github.pagehelper
            pagehelper
            5.1.4
        

mybatis-config.xml中加入:

    
         
            
        
    

测试代码:

    // 多参数查询
    @Test
    public void testManyParamQuery() {
        // 2.获取sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 3.获取对应mapper
        TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);

        String email = "qq.com";
        Byte sex = 1;

        // 第一种方式使用map
        Map params = new HashMap();
        params.put("email", email);
        params.put("sex", sex);
        Page startPage = PageHelper.startPage(1, 2);
        List list1 = mapper.selectByEmailAndSex1(params);
        System.out.println(list1.size());
        System.out.println(startPage);

//        // 第二种方式直接使用参数
//        List list2 = mapper.selectByEmailAndSex2(email, sex);
//        System.out.println(list2.size());
//
//        // 第三种方式用对象
//        EmailSexBean esb = new EmailSexBean();
//        esb.setEmail(email);
//        esb.setSex(sex);
//        List list3 = mapper.selectByEmailAndSex3(esb);
//        System.out.println(list3.size());
    }

结果:

2018-09-29 17:53:40.994 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - ==>  Preparing: SELECT count(0) FROM t_user a WHERE a.email LIKE CONCAT('%', ?, '%') AND a.sex = ? 
2018-09-29 17:53:41.022 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - ==> Parameters: qq.com(String), 1(Byte)
2018-09-29 17:53:41.035 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - <==      Total: 1
sql语句:“com.mysql.jdbc.JDBC4PreparedStatement@498d318c: SELECT count(0) FROM t_user a WHERE a.email LIKE CONCAT('%', 'qq.com', '%') AND a.sex = 1”执行时间为:14毫秒,已经超过阈值!
2018-09-29 17:53:41.039 [main] DEBUG com.enjoylearning.mybatis.mapper.TUserMapper - Cache Hit Ratio [com.enjoylearning.mybatis.mapper.TUserMapper]: 0.0
2018-09-29 17:53:41.040 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - ==>  Preparing: select id, user_name, real_name, sex, mobile, email, note, position_id from t_user a where a.email like CONCAT('%', ?, '%') and a.sex = ? LIMIT ? 
2018-09-29 17:53:41.040 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - ==> Parameters: qq.com(String), 1(Byte), 2(Integer)
2018-09-29 17:53:41.042 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - <==      Total: 2
2
Page{count=true, pageNum=1, pageSize=2, startRow=0, endRow=2, total=2, pages=1, reasonable=false, pageSizeZero=true}[TUser [id=1, userName=lison, realName=李小宇, sex=1, mobile=186995587422, [email protected], note=lison的备注, positionId=], TUser [id=2, userName=james, realName=陈大雷, sex=1, mobile=18677885200, [email protected], note=james的备注, positionId=]]

2.1 测试时发生了莫名其妙的错误

找不到mybatis-config.xml文件,是由于setting配置有问题,javac/jvm的版本配置的乱七八糟,统一更改为1.8之后,就可以了!

3.PageHelper源码解析

@SuppressWarnings({"rawtypes", "unchecked"})
@Intercepts(
    {
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
    }
)
public class PageInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        ... 
    }

    @Override
    public Object plugin(Object target) {
        //TODO Spring bean 方式配置时,如果没有配置属性就不会执行下面的 setProperties 方法,就不会初始化,因此考虑在这个方法中做一次判断和初始化
        //TODO https://github.com/pagehelper/Mybatis-PageHelper/issues/26
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        //缓存 count ms
        msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);
        String dialectClass = properties.getProperty("dialect");
        if (StringUtil.isEmpty(dialectClass)) {
            dialectClass = default_dialect_class;
        }
        try {
            Class aClass = Class.forName(dialectClass);
            dialect = (Dialect) aClass.newInstance();
        } catch (Exception e) {
            throw new PageException(e);
        }
        dialect.setProperties(properties);

        String countSuffix = properties.getProperty("countSuffix");
        if (StringUtil.isNotEmpty(countSuffix)) {
            this.countSuffix = countSuffix;
        }

        try {
            //反射获取 BoundSql 中的 additionalParameters 属性
            additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters");
            additionalParametersField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            throw new PageException(e);
        }
    }

3.1 关键1——数据总条数怎么查到的

    private Long executeAutoCount(Executor executor, MappedStatement countMs,
                                   Object parameter, BoundSql boundSql,
                                   RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException, SQLException {
        Map additionalParameters = (Map) additionalParametersField.get(boundSql);
        //创建 count 查询的缓存 key
        CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
        //调用方言获取 count sql
        String countSql = dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
        //countKey.update(countSql);
        BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);
        //当使用动态 SQL 时,可能会产生临时的参数,这些参数需要手动设置到新的 BoundSql 中
        for (String key : additionalParameters.keySet()) {
            countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
        }
        //执行 count 查询
        Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
        Long count = (Long) ((List) countResultList).get(0);
        return count;
    }

在executeAutoCount中计算总数:

2018-09-29 18:08:12.701 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - ==>  Preparing: SELECT count(0) FROM t_user a WHERE a.email LIKE CONCAT('%', ?, '%') AND a.sex = ? 
2018-09-29 18:08:12.912 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - ==> Parameters: qq.com(String), 1(Byte)
2018-09-29 18:08:13.049 [main] DEBUG c.e.m.m.TUserMapper.selectByEmailAndSex1_COUNT - <==      Total: 1

3.2 关键2——分页怎么做到的

调用方言,获取分页sql


                    //调用方言获取分页 sql
                    String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
                    BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
                    //设置动态参数
                    for (String key : additionalParameters.keySet()) {
                        pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
                    }
                    //执行分页查询
                    resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
                } else {
                    //不执行分页的情况下,也不执行内存分页
                    resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
                }

分页使用LIMIT实现的:

2018-09-29 18:14:14.152 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - ==>  Preparing: select id, user_name, real_name, sex, mobile, email, note, position_id from t_user a where a.email like CONCAT('%', ?, '%') and a.sex = ? LIMIT ? 
2018-09-29 18:14:14.160 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - ==> Parameters: qq.com(String), 1(Byte), 2(Integer)
2018-09-29 18:14:14.174 [main] DEBUG c.e.m.mapper.TUserMapper.selectByEmailAndSex1 - <==      Total: 2

4.为什么不支持嵌套结果集查询

分页插件不支持嵌套结果映射
由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。

原因:

  • 1)互联网应用不推荐使用嵌套结果集查询
  • 2)数据库查询的总数与业务java查询的总数不同,因为业务java查询的总数会有合并

参考

  • 1)享学课堂Lison老师笔记

你可能感兴趣的:(开源插件PageHelper分析)