Tomcat应用运行后全查询触发java.sql.SQLException,重启恢复求原因
这问题我之前帮团队排查过类似的,结合你用的Tomcat 8.5.27 + MySQL 5.5.21 + mysql-connector 5.1.44技术栈,大概率是连接复用或JDBC资源管理的问题,以下几个场景你可以逐一排查:
1. 连接池连接污染(最常见)
如果应用里没有正确关闭ResultSet/Statement/Connection,连接池里的连接会被"污染"。比如某个线程执行了UPDATE/INSERT这类无ResultSet返回的语句,但没关闭对应的Statement,后续线程复用这个连接时,可能误将之前残留的Statement当成查询语句来调用next(),直接触发这个异常。
另外,Tomcat JDBC连接池如果没配置足够的连接校验逻辑,比如没设置validationQuery或者校验时机不对(比如testOnBorrow设为false),就会让污染的连接持续被复用,最终导致全量查询报错,重启后连接池重建才恢复正常。
2. MySQL Connector/J旧版本bug
你用的5.1.44是比较老的版本,5.1系列早期版本存在一些Statement/ResultSet状态管理的bug,尤其是在连接池复用场景下。比如连接被回收回池时,驱动没有正确重置Statement的内部状态,导致后续使用该连接时,错误地将之前更新操作的状态当成当前查询的状态。
建议查一下Connector/J的release notes,5.1.45及以后版本修复了不少这类连接复用相关的问题,升级到同系列的最新稳定版(比如5.1.49)可能直接解决问题。
3. 事务边界错误导致连接状态残留
如果应用的事务管理逻辑有问题,比如某个事务执行更新操作后,没有正确提交/回滚就将连接放回池,连接的事务状态会残留。后续线程复用这个连接时,可能因为事务上下文的影响,导致JDBC驱动返回错误的ResultSet状态。
比如手动管理事务时遗漏了commit()/rollback(),或者用Spring事务时,事务传播行为配置错误,导致事务没有正确收尾。
4. 错误的JDBC操作逻辑
有些代码可能混淆了更新语句和查询语句的处理逻辑,比如:
- 执行
executeUpdate()后,错误地尝试获取ResultSet并调用next(); - 在同一个
Statement对象上先执行更新操作,未关闭就直接执行查询,导致Statement内部状态混乱。
这种问题在低并发时可能偶尔出现,但当连接池复用被污染的连接后,就会批量触发异常。比如类似下面的错误代码:
Statement stmt = conn.createStatement(); // 执行更新操作,无ResultSet返回 stmt.executeUpdate("UPDATE user SET name='test' WHERE id=1"); // 错误:直接复用同一个stmt获取ResultSet并调用next() ResultSet rs = stmt.getResultSet(); rs.next(); // 触发"ResultSet is from UPDATE. No Data."
5. MySQL服务器端连接状态异常
MySQL 5.5.21也存在一些旧版本bug,比如连接长时间空闲后,服务器端的会话状态出现异常;或者某个连接修改了会话变量(比如SET autocommit=0)但未重置,后续复用连接时,执行更新后没有提交,导致ResultSet状态异常。
排查建议
- 检查连接池配置:确保配置了
validationQuery="SELECT 1",并且开启testOnBorrow=true或testWhileIdle=true,让连接池在借出或空闲时校验连接状态,过滤污染的连接。 - 规范JDBC资源管理:用try-with-resources语法(Java 7+)自动关闭ResultSet、Statement、Connection,或者在finally块中确保资源被正确关闭。
- 升级Connector/J:优先考虑升级到5.1系列最新稳定版,或者兼容MySQL 5.5的8.0系列驱动(注意URL参数变化,比如
useSSL默认值等)。 - 开启JDBC日志:设置
log4j.logger.com.mysql.jdbc=DEBUG,追踪具体是哪个Statement触发的错误,定位到问题代码。 - 校验事务逻辑:确保所有事务在正常/异常情况下都有正确的提交或回滚,避免事务状态残留。
内容的提问来源于stack exchange,提问作者Santhana Preethi

