You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MyBatis中foreach批量更新返回-1的原因及处理咨询

MyBatis批量更新(逻辑删除)返回-1的原因及解决办法

原因分析

你当前的SQL是将多个UPDATE语句包装在Oracle的PL/SQL匿名块(DECLARE BEGIN ... END;)中执行的。JDBC驱动执行这类匿名块时,默认不会返回DML语句的受影响行数,而是返回-1——因为匿名块本身属于PL/SQL执行单元,并非直接的DML操作,驱动无法直接提取单个或多个UPDATE的影响行数。

你之前认知的“返回第一条SQL执行结果”,仅适用于**不使用PL/SQL块、直接用foreach拼接多个独立UPDATE语句(以分号分隔)**的场景,但Oracle JDBC默认不支持这种多语句执行方式,所以你才需要用PL/SQL块包裹,这也导致了返回值的变化。

解决办法

方案1:改用单条批量UPDATE语句(推荐)

用CASE WHEN实现单条SQL完成批量逻辑删除,既能直接返回受影响的总行数,性能也更优:

<update id="deleteVhcleInfos" parameterType="java.util.List">
    UPDATE TB_VHCL_MANAGE
    SET 
        DEL_YN = 'Y',
        MDFC_USERNO = CASE 
            <foreach collection="list" item="item" separator=" ">
                WHEN VHCLNO = #{item.vhclno} THEN #{item.mdfcUserno}
            </foreach>
            ELSE MDFC_USERNO
        END,
        MDFC_DTTM = CASE 
            <foreach collection="list" item="item" separator=" ">
                WHEN VHCLNO = #{item.vhclno} THEN SYSDATE
            </foreach>
            ELSE MDFC_DTTM
        END
    WHERE VHCLNO IN 
        <foreach collection="list" item="item" open="(" separator="," close=")">
            #{item.vhclno}
        </foreach>
</update>

注意:需确保传入的list中vhclno非空且唯一,避免更新逻辑冲突。执行后返回的数值就是实际被修改的行数,完全适配你原有的代码判断逻辑。

方案2:保留PL/SQL块并获取总行数

若必须保留原循环写法,可在PL/SQL块中累计受影响行数并通过输出参数返回:

  1. 修改MyBatis XML:
<update id="deleteVhcleInfos" parameterType="java.util.Map">
    DECLARE
        total_rows NUMBER := 0;
    BEGIN
        <foreach collection="list" item="item" separator=" ">
            UPDATE TB_VHCL_MANAGE
            <set>
                <if test='item.vhclno != null and item.vhclno != ""'>
                    DEL_YN = 'Y'
                </if>
                <if test='item.mdfcUserno != null and item.mdfcUserno != ""'>
                    , MDFC_USERNO = #{item.mdfcUserno}
                    , MDFC_DTTM = SYSDATE
                </if>
            </set>
            WHERE VHCLNO = #{item.vhclno};
            total_rows := total_rows + SQL%ROWCOUNT;
        </foreach>
        #{total, mode=OUT, jdbcType=INTEGER} := total_rows;
    END;
</update>
  1. 修改Mapper接口方法,添加输出参数:
void deleteVhcleInfos(@Param("list") List<VhcleInfo> list, @Param("total") AtomicInteger total);

调用时传入AtomicInteger对象,执行后total.get()即可获取总受影响行数。这种写法相对繁琐,性能也不如方案1。

方案3:调整代码判断逻辑

如果不想修改SQL,可将返回-1视为执行成功(前提是确保执行过程无SQL异常):

int result = vhcleInfoMapper.deleteVhcleInfos(list);
// 无异常情况下,返回-1表示PL/SQL块执行成功
if (result == 0) {
    // 无数据被更新,可根据业务判定为失败或成功
} else if (result < 0) {
    // 执行成功
} else {
    // 执行失败
}

注意:这种方式存在风险,若PL/SQL块内部某条UPDATE执行失败但未抛出异常(如未处理的约束违规),代码无法感知,因此不推荐。

内容的提问来源于stack exchange,提问作者sinichee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 23:20:12