pagehelper 4.x 多线程 bug
当查询同一sql,在高并发情况下
或出现 (4.1.x)
NullPointerException
或者
无法处理该类型[class com.github.pagehelper.sqlsource.PageDynamicSqlSource]的SqlSource
分析:
SqlUtils 处
private Page doProcessPage(Invocation invocation, Page page, Object[] args) throws Throwable {
...
//判断并处理为PageSqlSource
if (!isPageSqlSource(ms)) {
processMappedStatement(ms);
}
try {
...
//简单的通过total的值来判断是否进行count查询
if (page.isCount()) {
page.setCountSignal(Boolean.TRUE);
//替换MS
args[0] = msCountMap.get(ms.getId());
...
} else {
page.setTotal(-1l);
}
...
} finally {
((PageSqlSource)ms.getSqlSource()).removeParser();
}
//返回结果
return page;
}
public void processMappedStatement(MappedStatement ms) throws Throwable {
SqlSource sqlSource = ms.getSqlSource();
MetaObject msObject = SystemMetaObject.forObject(ms);
SqlSource pageSqlSource;
if (sqlSource instanceof StaticSqlSource) {
pageSqlSource = new PageStaticSqlSource((StaticSqlSource) sqlSource);
} else if (sqlSource instanceof RawSqlSource) {
pageSqlSource = new PageRawSqlSource((RawSqlSource) sqlSource);
} else if (sqlSource instanceof ProviderSqlSource) {
pageSqlSource = new PageProviderSqlSource((ProviderSqlSource) sqlSource);
} else if (sqlSource instanceof DynamicSqlSource) {
pageSqlSource = new PageDynamicSqlSource((DynamicSqlSource) sqlSource);
} else {
throw new RuntimeException("无法处理该类型[" + sqlSource.getClass() + "]的SqlSource");
}
msObject.setValue("sqlSource", pageSqlSource);
//由于count查询需要修改返回值,因此这里要创建一个Count查询的MS
msCountMap.put(ms.getId(), MSUtils.newCountMappedStatement(ms));
}
1、NullPointerException 分析 线程1 进入 processMappedStatement 方法, 将 pageSqlSource 替换 pageSqlSource = new PageDynamicSqlSource((DynamicSqlSource) sqlSource); 但未执行 msCountMap.put(ms.getId(), MSUtils.newCountMappedStatement(ms)); 线程2进入 isPageSqlSource 方法,判断为true,进入后面的逻辑 执行 args[0] = msCountMap.get(ms.getId()) ,此时 获取的值为空,则报异常 2、 无法处理该类型[class com.github.pagehelper.sqlsource.PageDynamicSqlSource]的SqlSource 分析
线程1刚进入 processMappedStatement 方法,此时 sqlSource 的值还未替换 线程2进入 isPageSqlSource 方法,为 false,进入processMappedStatement 方法,此时线程 1 已经替换sqlSource 则线程2抛出异常