java.sql.SQLException異常原因排查與解決
前言
在日常開發(fā)中,大家應(yīng)該或多或少都遇到過這種情況:SQL 在本地跑得好好的,一放到服務(wù)里執(zhí)行就報(bào) java.sql.SQLException
。很多同學(xué)看到這個(gè)異常時(shí),第一反應(yīng)就是“是不是數(shù)據(jù)庫掛了?”。其實(shí)絕大多數(shù)情況跟數(shù)據(jù)庫無關(guān),而是 SQL 拼接、參數(shù)綁定或者日志缺失導(dǎo)致的。
這篇文章我結(jié)合一個(gè)小 Demo,帶大家看一下 SQLException 的常見原因,以及如何一步步排查。
場景描述:常見的 SQLException 問題
假設(shè)我們有一張 users
表,結(jié)構(gòu)很簡單:
CREATE TABLE users ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, age INT );
在 Java 項(xiàng)目里寫了一個(gè)最普通的查詢:
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();
看似沒問題,但在真實(shí)項(xiàng)目里,很容易因?yàn)橄旅鎺讉€(gè)問題報(bào) SQLException:
- SQL 拼接錯(cuò)誤:比如忘了
AND
,或者參數(shù)占位符數(shù)量不對。 - 參數(shù)綁定異常:明明是數(shù)字,結(jié)果 setString();或者順序錯(cuò)了。
- SQL 沒有打印日志:導(dǎo)致無法復(fù)現(xiàn)真實(shí)執(zhí)行的 SQL。
排查思路:怎么快速鎖定問題?
遇到 SQLException 時(shí),不要慌,通常從以下幾個(gè)角度來排查:
1.打印完整 SQL
很多時(shí)候,你以為你執(zhí)行的是 SELECT * FROM users WHERE username = 'zhangfei'
,實(shí)際上可能變成了 SELECT * FROM users WHERE username = 'null'
。
2.檢查參數(shù)綁定
確認(rèn)每個(gè) ?
是否都被正確賦值,并且類型匹配。
3.用日志記錄 SQL
不僅要打印原始 SQL,還要把 參數(shù)替換后的 SQL 打出來,方便直接拿去數(shù)據(jù)庫執(zhí)行。
Demo:帶日志的 SQL 執(zhí)行封裝
我們可以寫一個(gè)簡單的工具方法來封裝 SQL 執(zhí)行和日志打印。這樣每次執(zhí)行 SQL 時(shí),都能清晰看到完整的 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)) { // 參數(shù)綁定 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 執(zhí)行異常: " + e.getMessage()); e.printStackTrace(); } } // 將參數(shù)替換到 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); } }
運(yùn)行效果:
Executing SQL: SELECT * FROM users WHERE username = 'zhangfei' AND age = 18
User: zhangfei, Age: 18
一旦 SQL 寫錯(cuò),比如參數(shù)缺失,就能立刻在日志里看到:
Executing SQL: SELECT * FROM users WHERE username = 'zhangfei' AND age = null
SQL 執(zhí)行異常: Unknown column 'null' in 'where clause'
是不是就一目了然了?
結(jié)合實(shí)際開發(fā)的應(yīng)用
在真實(shí)的業(yè)務(wù)開發(fā)中,SQLException 的定位通常會踩到幾個(gè)坑:
- 多服務(wù)場景:調(diào)用鏈太長,不知道 SQL 是在哪個(gè)微服務(wù)里執(zhí)行的。
- ORM 框架二次封裝:比如 MyBatis,把 SQL 隱藏在 XML 里,導(dǎo)致排查困難。
- 日志打印不全:只打印了原始 SQL,沒有參數(shù),運(yùn)維無法復(fù)現(xiàn)。
因此,建議大家在項(xiàng)目里加一個(gè) SQL 攔截器,不論是 MyBatis 的 Interceptor
,還是 JPA 的日志配置,都要確保能拿到 完整 SQL。
總結(jié)
java.sql.SQLException
本質(zhì)上不是“數(shù)據(jù)庫壞了”,而是代碼邏輯和 SQL 執(zhí)行之間的溝通問題。核心思路就是:
- 先把完整 SQL 打印出來
- 確認(rèn)參數(shù)綁定是否正確
- 保證日志可復(fù)現(xiàn)
這樣基本上 90% 的 SQL 問題都能快速解決。
到此這篇關(guān)于java.sql.SQLException異常原因排查與解決的文章就介紹到這了,更多相關(guān)java.sql.SQLException解決內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- mysql字符集引起的java.sql.SQLException:Incorrect?string?value:問題
- java.sql.SQLException:?connection?holder?is?null錯(cuò)誤解決辦法
- Java中java.sql.SQLException異常的正確解決方法(親測有效!)
- java報(bào)錯(cuò)Cause: java.sql.SQLException問題解決
- java.sql.SQLException問題解決以及注意事項(xiàng)
- java.sql.SQLException: 內(nèi)部錯(cuò)誤: Unable to construct a Datum from the specified input
相關(guān)文章
解決SpringBoot2.1.0+RocketMQ版本沖突問題
這篇文章主要介紹了解決SpringBoot2.1.0+RocketMQ版本沖突問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06Java聊天室之使用Socket實(shí)現(xiàn)傳遞圖片
這篇文章主要為大家詳細(xì)介紹了Java簡易聊天室之使用Socket實(shí)現(xiàn)傳遞圖片功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以了解一下2022-10-10Java開發(fā)框架spring實(shí)現(xiàn)自定義緩存標(biāo)簽
這篇文章主要介紹了Java開發(fā)框架spring實(shí)現(xiàn)自定義緩存標(biāo)簽的詳細(xì)代碼,感興趣的小伙伴們可以參考一下2015-12-12Spring Boot 中集成 Lombok 和 MapStruct最
文章詳解SpringBoot項(xiàng)目中Lombok與MapStruct整合實(shí)踐,涵蓋版本兼容、IDE配置、代碼分層、映射配置、測試驗(yàn)證及性能優(yōu)化,重點(diǎn)解決注解沖突、依賴注入等常見問題,強(qiáng)調(diào)分層管理和組件掃描配置,提升開發(fā)效率與代碼簡潔性,本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2025-08-08使用idea搭建一個(gè)spring mvc項(xiàng)目的圖文教程
這篇文章主要介紹了使用idea直接創(chuàng)建一個(gè)spring mvc項(xiàng)目的圖文教程,本文通過圖文并茂的方式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03