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

MyBatis 參數處理機制深度解析:從介面入參到動態 SQL 與 #{}/${} 完整流程

     編輯:Java 相關知識

前言

MyBatis 參數處理是開發者最易踩坑的領域:Parameter 'xxx' not found、Could not get property、Expression evaluated to null、Return value was not iterable。本文從源碼層面完整追蹤參數從 Mapper 介面方法進入、轉換、封裝、動態 SQL 綁定、SQL 參數映射的全生命週期,建立精確心智模型。

一、參數進入與轉換:MapperMethod.convertArgsToSqlCommandParam

介面方法調用時,MyBatis 先將陣列參數 Object[] args 轉換為統一參數物件:


關鍵規則

介面簽名params Map 內容轉換結果
selectUser(String name){0: "0"} (無 @Param,編譯無參數名)直接返回 name 字串 (單參數特例)
selectUser(@Param("name") String name){0: "name"}{name=value, param1=value}
selectUser(String name, int age){0: "0", 1: "1"}{param1=name, param2=age, 0=name, 1=age}
selectUser(@Param("n") String name, @Param("a") int age){0: "n", 1: "a"}{n=name, a=age, param1=name, param2=age}

建議:多參數**務必加 @Param**,單參數建議加 @Param 避免編譯選項影響。

二、集合/陣列自動封裝:DefaultSqlSession.wrapCollection

若轉換後參數為單一 Collection、List、陣列,MyBatis 會再封裝為 Map:

 map = new StrictMap<>();
        map.put("array", object);
        return map;
    }
    return object;
}

這就是 <foreach collection="list"> 預設可用 list/array/collection 的原因。**Map 類型不會被封裝**,需自行指定 key。

三、動態 SQL 參數綁定:DynamicContext 與 ContextMap

<if>, <where>, <foreach> 等標籤中的 OGNL 表達式求值,依賴 DynamicContext.bindings (型別 ContextMap)。

ContextMap 結構

// 簡化邏輯
if (boundSql.hasAdditionalParameter(prop)) value = boundSql.getAdditionalParameter(prop); // foreach 動態參數
else if (parameterObject == null) value = null;
else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) value = parameterObject; // 簡單型別/單值
else value = metaObject.getValue(prop); // 反射取值 (支援巢狀 user.address.city)

typeHandler.setParameter(ps, i+1, value, jdbcType);

特性:型別安全、防注入、支援 TypeHandler 自訂轉換、jdbcType 控制 NULL 值處理。

${ } : 字串模板替換 (OGNL 取值 → 直接拼入 SQL)

處理類:TextSqlNode.BindingTokenParser.handleToken

用途:動態表名、欄位名、排序欄位、IN 列表 (需自行拼接)、靜態常數引用 ${@com.Const@VAL}。

風險:SQL 注入、無型別轉換、不走 PreparedStatement 快取。

五、foreach 深度解析:動態參數生成機制

 index, value -> item
    applyIndex(context, indexKey, index);
    applyItem(context, itemKey, o);
    // 遞歸處理 foreach 體內容
}

動態參數命名規則:__frch_{itemName}_{index}。巢狀屬性存取:__frch_user_0.username。

六、常見錯誤對應解決方案

錯誤訊息根因解決
Parameter 'xxx' not found動態 SQL/OGNL 找不到 key檢查 @Param 名稱、wrapCollection 產生的 key (list/array/collection)、foreach 動態參數名
Could not get property 'xxx'反射取 POJO 屬性失敗確認 getter 存在、屬性名拼寫、巢狀路徑正確
Expression evaluated to nullforeach collection 表達式為 null傳入非空集合、或用 <if test="list != null"> 包裹
Return value was not iterablecollection 非 Collection/Array/Map轉換為 List、或實作 Iterable
Map 子元素屬性找不到Map 值為 null 導致 key 缺失map.put(key, null) 保留 key

七、最佳實務總結

  1. ✅ **介面多參數全部加 @Param**,語義清晰、除錯容易。
  2. ✅ **單參數傳 POJO/Map**,動態 SQL 直接用屬性名/key。
  3. ✅ **集合參數用 @Param("list") List<T>**,foreach 寫 collection="list"。
  4. ✅ **#{ } 為預設選擇**,${ } 僅用於不可參數化處 (表名、欄位名、排序),並嚴格白名單驗證。
  5. ✅ **foreach 迭代 Map 時**,確保所有 value 含有預期 key,null 值也要 put(key, null)。
  6. ✅ **複雜條件構建** 改用 CriteriaBuilder / SQL Builder 類,減少 XML 動態 SQL 複雜度。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved