看似完美的 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();
ORA-00933 不直觀指向「缺空格」,誤導排查方向。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 = ?");
String sql = """
SELECT * FROM bookBasicInfo b, bookTypeInfo t
WHERE b.BelongType = t.BookTypeID
AND b.bookName LIKE ?
AND b.BelongType = ?
"""; // 自動保留換行縮進,零拼接風險
<!-- MyBatis XML -->
@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 統一管理、每段結尾強制留空格、單測斷言關鍵字完整性。細節決定成敗,這個坑踩一次就夠記一輩子了。