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

MyBatis 中 #{} 與 ${} 區別深度解析與防注入指南

     編輯:Java 相關知識

核心區別一覽

特性#{param}${param}
處理方式預編譯,參數化佔位符字符串直接替換
類型轉換自動類型匹配與轉換無類型處理,原樣輸出
SQL 注入防護強力防護無防護,極高風險
適用場景值參數傳遞(WHERE 條件、INSERT 值等)數據庫對象名(表名、列名、排序字段)

詳細機制解析

#{}: 安全的參數綁定

<select id="getUser" resultType="User">
    SELECT * FROM users WHERE id = #{id}
</select>

MyBatis 將其轉換為 JDBC PreparedStatement:

SELECT * FROM users WHERE id = ?

執行時綁定參數,數據庫引擎預編譯 SQL 結構,參數作為數據傳入,徹底阻斷注入攻擊。若 id 為字符串類型,自動添加引號 'value';數值類型直接傳值。

${}: 直接字符串拼接

<select id="getTableData" resultType="Map">
    SELECT * FROM ${tableName} WHERE status = 'active'
</select>

MyBatis 直接將變量值拼接進 SQL 字符串:

SELECT * FROM user_table WHERE status = 'active'

警告:若 tableName 來自用戶輸入,攻擊者可構造 user_table; DROP TABLE users; -- 導致災難性後果。

實戰最佳實踐

1. 默認使用 #{}

所有值參數(ID、名稱、日期、枚舉值等)統一用 #{},除非有明確不可替代理由。

2. ${} 僅用於動態 SQL 結構

  • 動態表名/列名:ORDER BY ${sortColumn}
  • 分庫分表路由:SELECT * FROM user_${shard}
  • 複雜條件構建:WHERE ${dynamicCondition}(需嚴格白名單校驗)

3. ${} 使用時的安全加固

// Mapper 接口
List<User> query(@Param("sort") String sortColumn);

// Service 層校驗
private static final Set<String> ALLOWED_COLUMNS = Set.of("id", "name", "created_at");
public List<User> safeQuery(String sort) {
    if (!ALLOWED_COLUMNS.contains(sort)) {
        throw new IllegalArgumentException("非法排序字段");
    }
    return mapper.query(sort);
}

底層原理:預編譯防注入

MyBatis 啟用預編譯後,SQL 模板發送給數據庫編譯生成執行計劃,參數佔位符 ? 標記數據位置。運行時僅傳遞參數值,數據庫引擎區分「代碼」與「數據」,使注入代碼無法被解析為 SQL 指令。

總結

鐵律:能用 #{} 絕不使用 ${}。必須用 ${} 時,嚴格白名單校驗輸入,並在代碼審查中重點關注。

掌握二者區別是 MyBatis 安全開發的基礎,亦是面試高頻考點。養成代碼審查習慣,從源頭杜絕 SQL 注入隱患。

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