MyBatis使用foreach批量更新报ArrayIndexOutOfBoundsException问题求助
问题定位与解决方案
异常原因分析
你遇到的ArrayIndexOutOfBoundsException本质是MyBatis拼接多语句UPDATE时,未开启MySQL多语句执行支持,同时多语句预编译的参数绑定机制会触发索引计算错误。当传入1条数据时,仅生成单条UPDATE语句,不会触发多语句问题;传入2条及以上时,生成的多条UPDATE用;分隔,MySQL默认不允许一次执行多个语句,进而引发参数索引越界。
解决方案
方案1:开启MySQL多语句执行支持
在JDBC连接URL中添加allowMultiQueries=true参数,示例:
jdbc:mysql://localhost:3306/your_db?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
注意:开启该参数存在SQL注入风险,需确保批量数据来源可信,无外部恶意输入。
方案2:改用单语句批量更新写法(推荐)
放弃foreach拼接多条UPDATE的方式,用MySQL的CASE WHEN语法实现单语句批量更新,无需开启多语句支持,且执行效率更高。修改后的Mapper XML如下:
<update id="batchUpdate" parameterType="java.util.List"> UPDATE gas_mode_dtl <set> device_conf_id = CASE fid <foreach collection="list" item="item" index="index"> WHEN #{item.fid,jdbcType=VARCHAR} THEN #{item.deviceConfId} </foreach> ELSE device_conf_id END, mode_id = CASE fid <foreach collection="list" item="item" index="index"> WHEN #{item.fid,jdbcType=VARCHAR} THEN #{item.modeId} </foreach> ELSE mode_id END, control_status = CASE fid <foreach collection="list" item="item" index="index"> WHEN #{item.fid,jdbcType=VARCHAR} THEN #{item.controlStatus} </foreach> ELSE control_status END, field1 = CASE fid <foreach collection="list" item="item" index="index"> WHEN #{item.fid,jdbcType=VARCHAR} THEN #{item.field1} </foreach> ELSE field1 END, field2 = CASE fid <foreach collection="list" item="item" index="index"> WHEN #{item.fid,jdbcType=VARCHAR} THEN #{item.field2} </foreach> ELSE field2 END, field3 = CASE fid <foreach collection="list" item="item" index="index"> WHEN #{item.fid,jdbcType=VARCHAR} THEN #{item.field3} </foreach> ELSE field3 END, field4 = CASE fid <foreach collection="list" item="item" index="index"> WHEN #{item.fid,jdbcType=VARCHAR} THEN #{item.field4} </foreach> ELSE field4 END, field5 = CASE fid <foreach collection="list" item="item" index="index"> WHEN #{item.fid,jdbcType=VARCHAR} THEN #{item.field5} </foreach> ELSE field5 END </set> WHERE fid IN <foreach collection="list" item="item" index="index" open="(" separator="," close=")"> #{item.fid,jdbcType=VARCHAR} </foreach> </update>
若需保留字段非空判断,可在CASE WHEN中嵌套IF条件,示例:
device_conf_id = CASE fid <foreach collection="list" item="item" index="index"> <if test="item.deviceConfId != null and item.deviceConfId != ''"> WHEN #{item.fid,jdbcType=VARCHAR} THEN #{item.deviceConfId} </if> </foreach> ELSE device_conf_id END,
验证说明
- 方案1修改URL后,原有代码可正常执行,但需警惕SQL注入风险;
- 方案2是MyBatis批量更新的推荐写法,单语句执行更高效,彻底规避多语句相关异常。
内容的提问来源于stack exchange,提问作者穹龙cwl
相关产品推荐
相关产品推荐

