源代码
<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
。