首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 拼接 SQL 的隱形殺手:一個空格引發的 ORA-00933 血案

     編輯:Java 相關知識

看似完美的 SQL,在 Java 中拼接執行卻拋出 ORA-00933: SQL 命令未正確結束。兇手竟是一個缺失的空格。本文還原現場、分析根因、提供防範最佳實踐,避免你在深夜對著屏幕發呆。

案發現場

// 代碼片段
String sql = "SELECT * FROM bookBasicInfo, bookTypeInfo " +
             "WHERE bookBasicInfo.BelongType = bookTypeInfo.BookTypeID " +
             "AND bookBasicInfo.bookName LIKE ? AND" +  // ← 這裡缺少空格!
             "bookBasicInfo.BelongType = ?";

PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "%" + bookName + "%");
ps.setInt(2, typeId);
ResultSet rs = ps.executeQuery(); // 拋出 SQLException: ORA-00933

根因還原

Java 字符串拼接原樣連接,不會自動添加分隔符。實際傳給 Oracle 的 SQL 為:

SELECT * FROM bookBasicInfo, bookTypeInfo 
WHERE bookBasicInfo.BelongType = bookTypeInfo.BookTypeID 
AND bookBasicInfo.bookName LIKE ? ANDbookBasicInfo.BelongType = ?

ANDbookBasicInfo 被解析為單一標識符,語法錯誤,Oracle 報 ORA-00933(命令未正確結束)。

修復方案

在拼接點補全空格(任一側均可):

// 方案 1:前段結尾加空格
"AND bookBasicInfo.bookName LIKE ? AND " + "bookBasicInfo.BelongType = ?"

// 方案 2:後段開頭加空格
"AND bookBasicInfo.bookName LIKE ? AND" + " bookBasicInfo.BelongType = ?"

// 方案 3:StringBuilder 統一管理(推薦)
String sql = new StringBuilder()
    .append("SELECT * FROM bookBasicInfo, bookTypeInfo ")
    .append("WHERE bookBasicInfo.BelongType = bookTypeInfo.BookTypeID ")
    .append("AND bookBasicInfo.bookName LIKE ? ")
    .append("AND bookBasicInfo.BelongType = ?")
    .toString();

為什麼這類錯誤極難發現?

  1. 代碼閱讀時,大腦自動補全空格(視覺欺騙)。
  2. 編譯器不報錯(字符串拼接合法),運行時才拋異常。
  3. 異常信息 ORA-00933 不直觀指向「缺空格」,誤導排查方向。
  4. SQL 較長時,肉眼掃描極易漏掉拼接縫隙。

防範最佳實踐:徹底杜絕手工拼接

1. 採用 StringBuilder + 統一格式規範

StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM bookBasicInfo b, bookTypeInfo t ")
   .append("WHERE b.BelongType = t.BookTypeID ")
   .append("AND b.bookName LIKE ? ")
   .append("AND b.BelongType = ?");

2. 使用文本塊 - JDK 15+ (標準化 SQL 書寫)

String sql = """
    SELECT * FROM bookBasicInfo b, bookTypeInfo t
    WHERE b.BelongType = t.BookTypeID
    AND b.bookName LIKE ?
    AND b.BelongType = ?
    """; // 自動保留換行縮進,零拼接風險

3. ORM 框架(MyBatis/JPA)動態 SQL

<!-- MyBatis XML -->

4. 單元測試覆蓋 SQL 生成邏輯

@Test
void testSqlGeneration() {
    String sql = BookDao.buildSelectSql("Java", 1);
    assertTrue(sql.contains("AND b.bookName LIKE ? ")); // 斷言關鍵字前有空格
    assertTrue(sql.contains("AND b.BelongType = ?"));
}

總結

一個空格,折騰半天。 Java 拼接 SQL 本質是字符串操作,缺乏語法感知。最佳策略:能不用拼接就不用——上文本塊、上 MyBatis、上 JPA。不得已拼接時,StringBuilder 統一管理、每段結尾強制留空格、單測斷言關鍵字完整性。細節決定成敗,這個坑踩一次就夠記一輩子了。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved