| 語法 | 處理階段 | SQL 形式 | 安全性 | 適用場景 |
|---|---|---|---|---|
#{param} | 預編譯階段 | WHERE name = ? (PreparedStatement) | 高 (防 SQL 注入) | 一般參數傳遞、WHERE 條件、INSERT/UPDATE 值 |
${param} | 動態 SQL 解析階段 | WHERE name = 'value' (字串直接替換) | 低 (有注入風險) | 動態表名、欄位名、排序欄位、IN 子句動態列表 |
<!-- 前端傳入 ruleName = "A,B,C" -->
<if test="ruleName != null and ruleName != ''">
AND a.rule_name IN (#{ruleName})
</if>
MyBatis 會將整個字串 'A,B,C' 視為單一參數綁定到 ?,產生的 SQL 類似:
SELECT ... WHERE a.rule_name IN (?) -- 參數為 'A,B,C'
資料庫會嘗試找 rule_name = 'A,B,C' 的資料,導致查無結果。
<if test="ruleName != null and ruleName != ''">
AND a.rule_name IN (${ruleName})
</if>
配合 Service 層將字串轉為 'A','B','C' 格式:
public String formatInStr(String input) {
if (input == null || input.trim().isEmpty()) return null;
String[] arr = input.replaceAll("[,;]", " ").split("\\s+");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
if (i > 0) sb.append(",");
sb.append("'").append(arr[i]).append("'");
}
return sb.toString(); // 回傳 'A','B','C'
}
最終 SQL 變為:
SELECT ... WHERE a.rule_name IN ('A','B','C')
ORDER BY ${sortColumn} ${sortOrder}SELECT * FROM ${tablePrefix}_userSELECT ${columns} FROM table絕對不可將未經驗證的使用者輸入直接放入
${ }。這會造成 SQL 注入漏洞。
必須在 Service 層對輸入進行嚴格白名單驗證(如欄位名必須在允許清單中),或自行構建安全字串後再傳入。
#{ },享受預編譯效能與安全性。${ }。${ } 時,務必在 Java 端完成嚴格的輸入驗證與轉義。