首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Java PreparedStatement 陷阱:佔位符 ? 不能用於欄位名,正確動態更新寫法

     編輯:程式設計綜合問答

錯誤代碼

String sql = "update emp set ?=? where empno=?"; // ❌ 錯誤
ps = conn.prepareStatement(sql);
ps.setString(1, oldInfo); // 欄位名
ps.setInt(2, infoInt);    // 欄位值
ps.setInt(3, empId);      // 條件值

報錯資訊

java.sql.SQLSyntaxErrorException: ORA-01747: user.table.column, table.column 或列說明無效

根本原因

JDBC PreparedStatement 的佔位符 ? 僅能替代「值」(Value),不能替代「標識符」(Identifier),如表名、欄位名、Schema 名。

  • 資料庫解析 SQL 時需先確定結構(欄位名),再綁定參數值
  • 欄位名屬於 SQL 結構一部分,必須在語句編譯前確定

正確解決方案

方案一:字串拼接欄位名(需防注入)

// 假設 oldInfo 經過白名單驗證,確認為合法欄位名
String sql = "update emp set " + oldInfo + " = ? where empno = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, infoInt);   // 參數索引從 1 重新計算
ps.setInt(2, empId);
count = ps.executeUpdate();

方案二:使用白名單映射(推薦,安全)

private static final Map COLUMN_MAP = Map.of(
    "salary", "SAL",
    "comm", "COMM",
    "deptno", "DEPTNO"
);

public void updateEmpField(String fieldKey, Object newValue, int empNo) {
    String column = COLUMN_MAP.get(fieldKey);
    if (column == null) throw new IllegalArgumentException("非法欄位: " + fieldKey);

    String sql = "update emp set " + column + " = ? where empno = ?";
    try (PreparedStatement ps = conn.prepareStatement(sql)) {
        ps.setObject(1, newValue);
        ps.setInt(2, empNo);
        ps.executeUpdate();
    }
}

防 SQL 注入檢查清單

  • ✅ 欄位名來自後端定義的常量/Enum/Map,不可直接使用前端傳入字串
  • ✅ 值類參數全部使用 setXxx() 綁定
  • ✅ 表名同理,需白名單驗證
  • ✅ 使用 ORM 框架(MyBatis/JPA)時利用 ${column}(MyBatis)或 Criteria API 避免手寫 SQL

延伸:MyBatis 中的正確寫法

<update id="updateEmpField">
  update emp
  <set>
    <!-- ${} 直接拼接,需確保 column 經過驗證 -->
    ${column} = #{value}
  </set>
  where empno = #{empNo}
</update>

記住鐵律:佔位符只能放值,結構必須寫死或經白名單驗證後拼接。

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