SQL 执行异常排查 java.sql.SQLException:从 SQLException 说起


网罗开发 (小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验 。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员

👋 大家好,我是展菲!

📱 全网搜索"展菲",即可纵览我在各大平台的知识足迹。

📣 公众号"Swift社区",每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。

💬 微信端添加好友"fzhanfei",与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。

📅 最新动态:2025 年 3 月 17 日

快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!

文章目录

前言

在日常开发中,大家应该或多或少都遇到过这种情况:SQL 在本地跑得好好的,一放到服务里执行就报 java.sql.SQLException 。很多同学看到这个异常时,第一反应就是"是不是数据库挂了?"。其实绝大多数情况跟数据库无关,而是 SQL 拼接、参数绑定或者日志缺失导致的。

这篇文章我结合一个小 Demo,带大家看一下 SQLException 的常见原因,以及如何一步步排查。

场景描述:常见的 SQLException 问题

假设我们有一张 users 表,结构很简单:

sql 复制代码
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    age INT
);

在 Java 项目里写了一个最普通的查询:

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:

  1. SQL 拼接错误 :比如忘了 AND,或者参数占位符数量不对。
  2. 参数绑定异常:明明是数字,结果 setString();或者顺序错了。
  3. 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。

java 复制代码
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);
    }
}

运行效果:

txt 复制代码
Executing SQL: SELECT * FROM users WHERE username = 'zhangfei' AND age = 18
User: zhangfei, Age: 18

一旦 SQL 写错,比如参数缺失,就能立刻在日志里看到:

txt 复制代码
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 执行之间的沟通问题。核心思路就是:

  1. 先把完整 SQL 打印出来
  2. 确认参数绑定是否正确
  3. 保证日志可复现

这样基本上 90% 的 SQL 问题都能快速解决。

相关推荐
freejackman6 小时前
【实战二】字段级血缘追踪:拆解 AST 迷局,精准穿透函数链与子查询
大数据·数据库·sql
这个昵称不容易7 小时前
SpringBoot「衣依」服装销售平台:144个Java文件180个接口,从挑衣服到收货评价整条链路都在
java·springboot·毕设
计算机毕设定制辅导-无忧学长7 小时前
《基于微信小程序的居家医疗陪护预约系统设计与实现》
java·spring boot·微信小程序·uniapp·居家医疗陪护预约系统
.痞老板.7 小时前
mysql初阶--增删改查操作
数据库·mysql
文丑颜不良啊9 小时前
LeetCode2221~2240题目解析
java·leetcode
秋饼9 小时前
Computer Use Agent Java 落地实战:感知-行动循环、执行器与安全围栏生产级实现
java·ai·技术分享·后端开发
文丑颜不良啊11 小时前
LeetCode2161~2180题目解析
java·leetcode
文丑颜不良啊12 小时前
LeetCode2141~2160题目解析
java·leetcode
LabVIEW开发13 小时前
LabVIEW 连接板:28 个端子怎么排,优先级怎么定
java·算法·labview
lambo mercy13 小时前
AI智能租房助手
数据库