PageHelper不安全的分页问题,导致ParserException: syntax error, error in :'it 1 LIMIT ? ', expect LIMIT, actual ...

简介: PageHelper不安全的分页问题,导致ParserException: syntax error, error in :'it 1 LIMIT ? ', expect LIMIT, actual ...

背景

项目中使用PageHlper插件进行分页,今日发现有多处SQL查询语句都出现了如下的报错。

com.alibaba.druid.sql.parser.ParserException: syntax error, error in :'it 1 LIMIT ? ', expect LIMIT, actual LIMIT pos 249, line 12, column 16, token LIMIT
at com.alibaba.druid.sql.parser.SQLParser.printError(SQLParser.java:284)
at com.alibaba.druid.sql.parser.SQLStatementParser.parseStatementList(
at com.alibaba.druid.sql.parser.SQLStatementParser.parseStatementList(
at com.alibaba.druid.sql.SQLUtils.format(SQLUtils.java:255)
at com.alibaba.druid.filter.logging.LogFilter.statement_executeErrorAfter(LogFilter.java:767)
at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(
at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3407)
at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(
at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3407)
at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.execute(PreparedStatementProxyImpl.java:167)
at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:498)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:63)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63)
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:324)
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
at com.github.pagehelper.PageInterceptor.intercept(PageInterceptor.java:136)
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)
at com.sun.proxy.$Proxy467.query(Unknown Source)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:77)
at sun.reflect.GeneratedMethodAccessor239.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)
at com.sun.proxy.$Proxy137.selectOne(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:166)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:82)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
at com.sun.proxy.$Proxy243.getOneSomthing(Unknown Source)
at com.lingyejun.project.impl.GetOneThingServiceImpl.getOneThingFromDb(GetOneThingServiceImpl.java:23)

调查

我们检视堆栈信息发现有一行关键信息,在进行查询的的时候使用的PageHelper进行了拦截。

at com.github.pagehelper.PageInterceptor.intercept(PageInterceptor.java:136)

但是看SQL语句并未发现有分页的代码,而且报错的不止这一处,还有其他几个地方,查看提交记录发现最近都没有改动。

我们想到那肯定是因为其他地方有改动导致的。调查原因后发现有一处代码在调用了PageHelper.startPage后直接返回了,导致的报错,大致代码如下。

package com.lingyejun.authenticator;
import com.github.pagehelper.PageHelper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PageHelperTest {
    @Resource
    private TeacherMapper teacherMapper;
    @Resource
    private StudentMapper studentMapper;
    public void doQueryByPage(byte type) {
        PageHelper.startPage(1,10);
        if (type == 1){
            studentMapper.query();
        }else if (type ==2){
            teacherMapper.query();
        }
        // 如果type不是1或者2那么此方法执行完是没有释放和清理page变量
        // 会导致其他地方的查询语句报错,或者结果与预期不符
        return;
    }
}

原理

PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。只要我们保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。

一次PageHelper的分页过程如下

  1. 设置 page 参数
  2. 执行 query 方法
  3. Interceptor 接口 中校验 ThreadLocal 中是否存在有设置的 page 参数
  4. 存在 page 参数,重新生成 count sql 和 page sql,并执行查询。不存在 page 参数,直接返回 查询结果
  5. 执行 LOCAL_PAGE.remove() 清除 page 参数

但是如果使用线程池的话,当前线程执行完毕,并不会被销毁,而是会将当前线程再次存放到池中,标记为空闲状态,以便后续使用。在后续使用这个线程的时候,由于 线程 的 threadLocals 依旧存在有值,尽管我们在第 1 步时未设置 page 参数,第 3 步 的也能获取到page参数,从而生成 count sql 和 page sql,从而影响我们的正常查询。

解决

以上问题属于人为bug,没有考虑到type为其他值的情况,即出现else时缺少后续逻辑处理,会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。所以我们把对应的逻辑进行调整修改即可, 将else if改成else即可解决这个问题。

 

本篇文章如有帮助到您,请给「翎野君」点个赞,感谢您的支持。

原文链接:https://www.cnblogs.com/lingyejun/p/15675208.html

目录
相关文章
|
6月前
|
Java
【ERROR】‘<>‘ operator is not allowed for source level below 1.7
【ERROR】‘<>‘ operator is not allowed for source level below 1.7
75 0
|
SQL Java 数据库连接
sql injection violation, syntax error: syntax error, error in :‘**‘expect IDENTIFIER, actual IDENTIF
sql injection violation, syntax error: syntax error, error in :‘**‘expect IDENTIFIER, actual IDENTIF
222 0
|
7月前
|
前端开发
Fatal error_ ENOSPC_ System limit for number of file watchers reached, watch '...path...'
Fatal error_ ENOSPC_ System limit for number of file watchers reached, watch '...path...'
62 0
|
Java Go
ES 查询时提示:all shards failed [type=search_phase_execution_exception]
ES 查询时提示:all shards failed [type=search_phase_execution_exception]
ES 查询时提示:all shards failed [type=search_phase_execution_exception]
|
SQL 关系型数据库 MySQL
postgre分页查询报错:ERROR: LIMIT #,# syntax is not supported 建议:Use separate LIMIT and OFFSET clauses
postgre分页查询报错:ERROR: LIMIT #,# syntax is not supported 建议:Use separate LIMIT and OFFSET clauses
392 0
postgre分页查询报错:ERROR: LIMIT #,# syntax is not supported 建议:Use separate LIMIT and OFFSET clauses
syntax error, expect {, actual [, pos 0 at
syntax error, expect {, actual [, pos 0 at
224 0
|
JSON 数据格式
报错:应用json.parseObject()方法时JSONException: syntax error, expect {, actual [, pos 0
报错:应用json.parseObject()方法时JSONException: syntax error, expect {, actual [, pos 0
1096 0
|
Linux
WARNING: Re-reading the partition table failed with error 22: Invalid argument
在划分磁盘分区时,遇到错误“WARNING: Re-reading the partition table failed with error 22: Invalid argument” 如下所示: [root@DB-Server u02]# fdisk -l   Disk /dev/sda: 500.
2547 0
成功解决ParserError: Error tokenizing data. C error: Expected 2 fields in line 53, saw 3
成功解决ParserError: Error tokenizing data. C error: Expected 2 fields in line 53, saw 3
|
SQL 关系型数据库 数据库
[Err] ERROR: invalid input syntax for integer: "1.0"
[Err] ERROR: invalid input syntax for integer: "1.0"
1097 0
[Err] ERROR: invalid input syntax for integer: "1.0"