#{param} → **預編譯參數標記**(?),防 SQL 注入,類型安全${param} → **純字符串替換**,動態 SQL 解析階段直接拼接,不防注入前端傳入多選參數 ruleName=A,B,C,業務層處理為 'A','B','C' 字符串,Mapper 中需用 IN 查詢。
<if test="ruleName != null and ruleName != ''">
AND a.rule_name IN (#{ruleName})
</if>
生成 SQL:
... AND a.rule_name IN (?) ...
參數綁定:'A','B','C' 作為單個字符串綁定到唯一佔位符,導致查詢無結果或報錯。
<if test="ruleName != null and ruleName != ''">
AND a.rule_name IN (${ruleName})
</if>
生成 SQL:
... AND a.rule_name IN ('A','B','C') ...
字符串直接內嵌到 SQL,IN 子句語法正確。
| 維度 | #{} | ${} |
|---|---|---|
| 解析階段 | 預編譯前(BoundSql) | 動態 SQL 解析階段 |
| 生成內容 | ? 佔位符 | 變量值直接拼接 |
| 類型處理 | TypeHandler 自動轉換 | 調用 toString() |
| SQL 注入 | 防護 | 無防護,極高風險 |
| 適用場景 | WHERE 條件值、INSERT/UPDATE 值 | 表名、列名、排序欄位、IN 列表等結構性動態 |
#{},90%+ 場景適用IN 列表、ORDER BY 欄位)用 ${}${} 時必須嚴格驗證/白名單過濾輸入,防注入IN 列表優選:MyBatis foreach 標籤 + #{},兼具安全與動態<foreach collection="nameList" item="name" open="(" separator="," close=")">
#{name}
</foreach>
生成 IN (?,?,?),參數逐個綁定,完全安全。