源代碼
<update id="updateDynamicTableData"><foreach collection="mapList" item="map" separator=";">UPDATE ${tableName} SET<foreach collection="map" item="value" index="key" separator=",">${key}=#{value} <!-- 注意:${key}需防注入 --></foreach>WHERE ARCHIVAL_CODE = #{map.ARCHIVAL_CODE}</foreach>
</update>
報錯:
從錯誤信息和代碼來看,問題出在?處理 NULL 值時 MyBatis 無法確定 JDBC 類型。具體分析如下:
問題原因
-
NULL 值處理問題:
-
當?
map
?中的值為?null
?時,MyBatis 無法自動推斷 JDBC 類型(錯誤信息:Error setting null for parameter
) -
批量更新時(2條數據),只要有一條記錄包含 NULL 值就會觸發此錯誤
-
-
批量更新差異:
-
單條數據能成功是因為:可能該條數據的所有字段值都非 NULL
-
兩條數據失敗是因為:第二條數據包含 NULL 值字段
-
解決方案
方法 1:指定 NULL 值的 JDBC 類型(推薦)
修改動態 SQL,為每個參數添加?jdbcType
?配置:
<update id="updateDynamicTableData"><foreach collection="mapList" item="map" separator=";">UPDATE ${tableName} SET<foreach collection="map" item="value" index="key" separator=",">${key}=#{value, jdbcType=VARCHAR} <!-- 添加jdbcType --></foreach>WHERE ARCHIVAL_CODE = #{map.ARCHIVAL_CODE}</foreach>
</update>
說明:
-
jdbcType=VARCHAR
?是示例,實際需根據字段類型選擇:-
數字字段:
jdbcType=NUMERIC
-
日期字段:
jdbcType=TIMESTAMP
-
通用類型:
jdbcType=OTHER
-
方法 2:全局配置 NULL 值處理
在 MyBatis 配置文件中添加:
<settings><!-- 指定 NULL 值的默認 JDBC 類型 --><setting name="jdbcTypeForNull" value="NULL"/>
</settings>
這會為所有 NULL 值統一設置?jdbcType=NULL
。
方法 3:接口參數指定類型
修改 DAO 接口,明確字段類型:
void updateDynamicTableData(@Param("mapList") List<Map<String, Object>> mapList,@Param("tableName") String tableName
);
#雖然不能直接解決,但可配合其他方案使用。
為什么單條成功、多條失敗?
-
單條數據:可能所有字段值恰好都非 NULL,無需處理類型推斷
-
多條數據:只要有一條記錄包含 NULL 值字段,MyBatis 就需要處理類型映射,此時:
// 示例數據
List<Map> mapList = Arrays.asList({ "NAME": "張三", "AGE": 25 }, // 無NULL{ "NAME": null, "ADDRESS": "北京" } // 包含NULL
);
第二條的?NAME=null
?觸發了類型推斷錯誤。
完整修正方案(推薦)
關鍵點:
-
確保?
separator=","
(內層循環用逗號分隔) -
所有?
#{value}
?必須指定?jdbcType
-
動態字段?
${key}
?需防范 SQL 注入(確保來源可信)
實際開發中,建議創建字段類型映射表,根據?
${key}
?動態設置對應的?jdbcType
。