gbase8s之MyBatis批量update问题
源代码
<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
。