遇到问题 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会有两种
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]
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源码)
除了非简单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()
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;
}