前言
在日常开发中,大家应该或多或少都遇到过这种情况:sql 在本地跑得好好的,一放到服务里执行就报 java.sql.sqlexception
。很多同学看到这个异常时,第一反应就是“是不是数据库挂了?”。其实绝大多数情况跟数据库无关,而是 sql 拼接、参数绑定或者日志缺失导致的。
这篇文章我结合一个小 demo,带大家看一下 sqlexception 的常见原因,以及如何一步步排查。
场景描述:常见的 sqlexception 问题
假设我们有一张 users
表,结构很简单:
create table users ( id bigint primary key auto_increment, username varchar(50) not null, age int );
在 java 项目里写了一个最普通的查询:
string sql = "select * from users where username = ? and age = ?"; preparedstatement ps = connection.preparestatement(sql); ps.setstring(1, "zhangfei"); ps.setint(2, 18); resultset rs = ps.executequery();
看似没问题,但在真实项目里,很容易因为下面几个问题报 sqlexception:
- sql 拼接错误:比如忘了
and
,或者参数占位符数量不对。 - 参数绑定异常:明明是数字,结果 setstring();或者顺序错了。
- sql 没有打印日志:导致无法复现真实执行的 sql。
排查思路:怎么快速锁定问题?
遇到 sqlexception 时,不要慌,通常从以下几个角度来排查:
1.打印完整 sql
很多时候,你以为你执行的是 select * from users where username = 'zhangfei'
,实际上可能变成了 select * from users where username = 'null'
。
2.检查参数绑定
确认每个 ?
是否都被正确赋值,并且类型匹配。
3.用日志记录 sql
不仅要打印原始 sql,还要把 参数替换后的 sql 打出来,方便直接拿去数据库执行。
demo:带日志的 sql 执行封装
我们可以写一个简单的工具方法来封装 sql 执行和日志打印。这样每次执行 sql 时,都能清晰看到完整的 sql。
import java.sql.*; import java.util.arrays; public class jdbchelper { public static void executequery(connection conn, string sql, object... params) { try (preparedstatement ps = conn.preparestatement(sql)) { // 参数绑定 for (int i = 0; i < params.length; i++) { ps.setobject(i + 1, params[i]); } // 打印完整 sql system.out.println("executing sql: " + buildfullsql(sql, params)); try (resultset rs = ps.executequery()) { while (rs.next()) { system.out.println("user: " + rs.getstring("username") + ", age: " + rs.getint("age")); } } } catch (sqlexception e) { system.err.println("sql 执行异常: " + e.getmessage()); e.printstacktrace(); } } // 将参数替换到 sql 中(简易版) private static string buildfullsql(string sql, object... params) { string fullsql = sql; for (object param : params) { string value = (param instanceof string) ? "'" + param + "'" : string.valueof(param); fullsql = fullsql.replacefirst("\\?", value); } return fullsql; } // demo 入口 public static void main(string[] args) throws exception { connection conn = drivermanager.getconnection( "jdbc:mysql://localhost:3306/testdb", "root", "password"); executequery(conn, "select * from users where username = ? and age = ?", "zhangfei", 18); } }
运行效果:
executing sql: select * from users where username = 'zhangfei' and age = 18
user: zhangfei, age: 18
一旦 sql 写错,比如参数缺失,就能立刻在日志里看到:
executing sql: select * from users where username = 'zhangfei' and age = null
sql 执行异常: unknown column 'null' in 'where clause'
是不是就一目了然了?
结合实际开发的应用
在真实的业务开发中,sqlexception 的定位通常会踩到几个坑:
- 多服务场景:调用链太长,不知道 sql 是在哪个微服务里执行的。
- orm 框架二次封装:比如 mybatis,把 sql 隐藏在 xml 里,导致排查困难。
- 日志打印不全:只打印了原始 sql,没有参数,运维无法复现。
因此,建议大家在项目里加一个 sql 拦截器,不论是 mybatis 的 interceptor
,还是 jpa 的日志配置,都要确保能拿到 完整 sql。
总结
java.sql.sqlexception
本质上不是“数据库坏了”,而是代码逻辑和 sql 执行之间的沟通问题。核心思路就是:
- 先把完整 sql 打印出来
- 确认参数绑定是否正确
- 保证日志可复现
这样基本上 90% 的 sql 问题都能快速解决。
到此这篇关于java.sql.sqlexception异常原因排查与解决的文章就介绍到这了,更多相关java.sql.sqlexception解决内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论