不规范使用PageHelper导致线程污染出现报错
问题复现:
在原有项目基础上开发获取最新的第一个模板的接口。接口中只有一个查询sql:select x from x where x limit 1。
调试总是报错:
org.springframework.jdbc.BadSqlGrammarException: ### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 10000' at line 5 ### The error may exist in class path resource [mapper/AlipayCardTemplateMapper.xml] ### The error may involve defaultParameterMap ### The error occurred while setting parameters ### SQL: select template_id,callback from alipay_card_template order by pkno desc limit 1 LIMIT ? ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 10000' at line 5 ; bad SQL grammar []; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 10000' at line 5 at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:234) ~[spring-jdbc-5.1.6.RELEASE.jar!/:5.1.6.RELEASE] at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72) ~[spring-jdbc-5.1.6.RELEASE.jar!/:5.1.6.RELEASE] at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73) ~[mybatis-spring-1.3.2.jar!/:1.3.2] at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446) ~[mybatis-spring-1.3.2.jar!/:1.3.2] at com.sun.proxy.$Proxy128.selectOne(Unknown Source) ~[na:na] at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:166) ~[mybatis-spring-1.3.2.jar!/:1.3.2]
发现打印sql语句,莫名后面多追加了LIMIT 10000导致报错,可能是其他地方分页影响到了,ThreadLocal线程污染。
最后排查,找到是有一个接口前面使用PageHelper.startPage(page, size);,后面并没有mapper查询,而是调用的远程接口查询。并且经过前后端搜索,发现pageSize=10000的参数只有这个接口。
很快定位接口,并修改:
在原有逻辑上加上try catch 并在finally中手动clearPage。
try { PageHelper.startPage(page, size); // 原有逻辑调用远程接口查询 。。。 PageInfo<String> pageInfo = new PageInfo<>(cusTags); 。。。 }catch (Exception e){ log.error("queryCusTags,异常:",e); return new ResponseObjectResult(new ResponseStatus(ResultCode.FAIL)); }finally { /** * 1.原因:分页后面没有紧跟mapper调用,而是调用远程接口返回列表。mybatis没有走拦截器不会自动PageHelper.clearPage()。需要手动PageHelper.clearPage(); * 否则会出现线程污染导致报错(getTemplateInfo()莫名后面多追加了LIMIT 10000导致报错,可能是其他地方分页影响到了,ThreadLocal线程污染。)。 * 2.复现:开发环境/getTemplateInfo接口去掉PageHelper.clearPage()。手机页面连续点击领取会员卡(调用的是/getTemplateInfo接口),postman同时连续请求/queryCusTags。 * 发现/getTemplateInfo接口报错BadSqlGrammarException:limit 1 LIMIT ? 。多追加了个LIMIT ?。 * 3.修改为此处在finally中手动clear。再次重复步骤2多次,未再出现连续点击‘领取会员卡’按钮报错。 */ PageHelper.clearPage(); }
修改后再次测试多遍,未发现错误。
看项目中有好几个地方在查询前都主动clearPage。估计是没找到产生的根源在哪儿,查询前加clearPage最简单有效。如果找到根源,那么其他地方完全不用加clearPage也不会出现报错。
参考:
https://www.jianshu.com/p/17ddc714b57f