sharding-jdbc 与pageHelper分页报错踩坑

遇到问题 Sharding-jdbc4.0.1版本使用 pageHelper运行count报错,而且只有部分sql不能运行:
原始sql

<select id="selectRankByLessonId" resultType="com.sunlands.course.common.vo.CourseAttendanceRankVO" parameterType="com.sunlands.course.common.dto.CourseAttendanceRankDTO">
    SELECT user_id ,SUM(total_duration) totalDuration, SUM(live_total_duration) liveTotalDuration, SUM(replay_total_duration) replayTotalDuration
    FROM   lite_lesson_attendance
    WHERE  delete_flag=0
    AND rounds_id=#{dto.roundsId} AND course_type=#{dto.courseType}
    AND lesson_id IN
    <foreach collection="dto.lessonIdList" separator="," open="(" close=")" item="item">
        #{item}
    foreach>
    GROUP BY user_id
    <if test="dto.sortType!=null">
        ORDER BY
        <if test="dto.sortType==0">
            total_duration
        if>
        <if test="dto.sortType==1">
            live_total_duration
        if>
        <if test="dto.sortType==2">
            replay_total_duration
        if>
        DESC
    if>
select>

报错:
在这里插入图片描述
报错提示,子查询未匹配到分片键
部分sql没报错的原因是pagehepler产生的count 不是子查询

原因 sharding4.0.1版本不支持子查询,需要升级到4.1.1
点击查询详情

为什么pagehelper分页count 会产生子查询?
使用pageHeple工具进行分页操作的时候,生成的count会有两种

  1. 简单语句生成的count:

SELECT COUNT(0) [YOU SQL Body]

/
SELECT count(0) FROM lite_course_attendance WHERE delete_flag = 0 AND
rounds_id = ? AND course_type = ? ::: [128, 2]

  1. 非简单sql生成的count :

SELECT count(o) from [ you sql ] table_count

SELECT count(0) FROM
(SELECT user_id, SUM(total_duration) totalDuration,

SUM(live_total_duration) liveTotalDuration, SUM(replay_total_duration)
replayTotalDuration FROM lite_lesson_attendance WHERE delete_flag = 0
AND rounds_id = ? AND course_type = ? AND lesson_id IN (?, ?) GROUP
BY user_id) table_count ::: [128, 2, 655, 654]

非区分简单sql的定义(定义来源pagehelper源码)

  1. sql中包含group by
  2. sql中包含distinct 查列中包含包含参数?(动态列名)
  3. 查询列中使用了函数

除了非简单sql以外的都是简单sql

com.github.pagehelper.parser.CountSqlParser# isSimpleCount()

debug验证 入口:

1. com.github.pagehelper.PageInterceptor##intercept()

此方法执行count
count = executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler);

2. com.github.pagehelper.PageInterceptor##executeAutoCount

private Long executeAutoCount(Executor executor, MappedStatement countMs,
                               Object parameter, BoundSql boundSql,
                               RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException, SQLException {
    Map<String, Object> additionalParameters = (Map<String, Object>) 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;
}

3. com.github.pagehelper.dialect.AbstractHelperDialect##getCountSql()

  1. com.github.pagehelper.parser.CountSqlParser## getSmartCountSql()
public String getSmartCountSql(String sql, String name) {
    //解析SQL
    Statement stmt = null;
    //特殊sql不需要去掉order by时,使用注释前缀
    if(sql.indexOf(KEEP_ORDERBY) >= 0){
        return getSimpleCountSql(sql);
    }
    try {
        stmt = CCJSqlParserUtil.parse(sql);
    } catch (Throwable e) {
        //无法解析的用一般方法返回count语句
        return getSimpleCountSql(sql);
    }
    Select select = (Select) stmt;
    SelectBody selectBody = select.getSelectBody();
    try {
        //处理body-去order by
        processSelectBody(selectBody);
    } catch (Exception e) {
        //当 sql 包含 group by 时,不去除 order by
        return getSimpleCountSql(sql);
    }
    //处理with-去order by
    processWithItemsList(select.getWithItemsList());
    //处理为count查询 重点方法
    sqlToCount(select, name);
    String result = select.toString();
    return result;
}

public void sqlToCount(Select select, String name) {
    SelectBody selectBody = select.getSelectBody();
    // 是否能简化count查询
    List<SelectItem> COUNT_ITEM = new ArrayList<SelectItem>();
    COUNT_ITEM.add(new SelectExpressionItem(new Column("count(" + name +")")));
    ///判断是否是简单count,
    if (selectBody instanceof PlainSelect && isSimpleCount((PlainSelect) selectBody)) {
       //简单count
         ((PlainSelect) selectBody).setSelectItems(COUNT_ITEM);
    } else {
        //子查询count
        PlainSelect plainSelect = new PlainSelect();
        SubSelect subSelect = new SubSelect();
        subSelect.setSelectBody(selectBody);
        subSelect.setAlias(TABLE_ALIAS);
        plainSelect.setFromItem(subSelect);
        plainSelect.setSelectItems(COUNT_ITEM);
        select.setSelectBody(plainSelect);
    }
}


/**
*
*  是否简单sql判断
*/
public boolean isSimpleCount(PlainSelect select) {
    //包含group by的时候不可以
    if (select.getGroupByColumnReferences() != null) {
        return false;
    }
    //包含distinct的时候不可以
    if (select.getDistinct() != null) {
        return false;
    }
    for (SelectItem item : select.getSelectItems()) {
        //select列中包含参数的时候不可以,否则会引起参数个数错误
        if (item.toString().contains("?")) {
            return false;
        }
        //如果查询列中包含函数,也不可以,函数可能会聚合列
        if (item instanceof SelectExpressionItem) {
            if (((SelectExpressionItem) item).getExpression() instanceof Function) {
                return false;
            }
        }
    }
    return true;
}

你可能感兴趣的:(sql,数据库,mysql)