MyBatis 报错:Parameter ‘xxx‘ not found 的原因与解决方案

文章目录

一、问题背景

在开发退款记录查询接口时,需要根据退款主表的 refundGuidList 批量查询退款明细中的应收金额。

本地没报错、生产报错,原因是:本地编译出来的 Mapper class 保留了方法参数名 refundGuidList,生产环境编译/打包后的 class 没保留,所以 MyBatis 只能看到 arg0 / collection / list。

业务代码大致如下:

java 复制代码
List<Long> refundGuidList = dataList.stream()
        .map(RefundRecordVo::getFldRefundGuid)
        .toList();

List<RefundReceivableDto> receivableDtoList =
        esBondRefundDetailMapper.queryReceivableAmountByRefundGuids(refundGuidList);

对应的 MyBatis XML 如下:

xml 复制代码
<select id="queryReceivableAmountByRefundGuids" resultType="com.es.bond.refund.dto.RefundReceivableDto">
    SELECT
        fld_refund_guid AS fldRefundGuid,
        SUM(fld_receivable_amount) AS receivableAmount
    FROM es_bond_refund_detail
    WHERE fld_refund_guid IN
    <foreach collection="refundGuidList"
             item="guid"
             open="("
             separator=","
             close=")">
        #{guid}
    </foreach>
    GROUP BY fld_refund_guid
</select>

启动接口后,程序报错:

text 复制代码
org.apache.ibatis.binding.BindingException: 
Parameter 'refundGuidList' not found. 
Available parameters are [arg0, collection, list]

二、问题原因

从报错信息可以看到:

text 复制代码
Available parameters are [arg0, collection, list]

说明 MyBatis 当前能识别的参数名只有:

text 复制代码
arg0
collection
list

但是 XML 中使用的是:

xml 复制代码
<foreach collection="refundGuidList">

也就是说,XML 中使用了 refundGuidList,但是 Mapper 接口方法中并没有通过 @Param 显式声明这个参数名。

如果 Mapper 接口是这样写的:

java 复制代码
List<RefundReceivableDto> queryReceivableAmountByRefundGuids(List<Long> refundGuidList);

那么对于单个 List 参数,MyBatis 默认不会识别 refundGuidList 这个名字,而是默认使用:

text 复制代码
list
collection
arg0

所以就会出现参数找不到的问题。


三、解决方案

方案一:推荐使用 @Param

修改 Mapper 接口方法:

java 复制代码
import org.apache.ibatis.annotations.Param;

List<RefundReceivableDto> queryReceivableAmountByRefundGuids(
        @Param("refundGuidList") List<Long> refundGuidList
);

XML 保持不变:

xml 复制代码
<foreach collection="refundGuidList"
         item="guid"
         open="("
         separator=","
         close=")">
    #{guid}
</foreach>

这样 MyBatis 就可以正确识别 refundGuidList 参数。


方案二:修改 XML 中的 collection 名称

如果不想修改 Mapper 接口,也可以把 XML 改成 MyBatis 默认识别的参数名:

xml 复制代码
<foreach collection="list"
         item="guid"
         open="("
         separator=","
         close=")">
    #{guid}
</foreach>

完整 SQL:

xml 复制代码
<select id="queryReceivableAmountByRefundGuids" resultType="com.es.bond.refund.dto.RefundReceivableDto">
    SELECT
        fld_refund_guid AS fldRefundGuid,
        SUM(fld_receivable_amount) AS receivableAmount
    FROM es_bond_refund_detail
    WHERE fld_refund_guid IN
    <foreach collection="list"
             item="guid"
             open="("
             separator=","
             close=")">
        #{guid}
    </foreach>
    GROUP BY fld_refund_guid
</select>

更推荐第一种方式。


四、最终代码

Mapper 接口:

java 复制代码
List<RefundReceivableDto> queryReceivableAmountByRefundGuids(
        @Param("refundGuidList") List<Long> refundGuidList
);

XML:

xml 复制代码
<select id="queryReceivableAmountByRefundGuids" resultType="com.es.bond.refund.dto.RefundReceivableDto">
    SELECT
        fld_refund_guid AS fldRefundGuid,
        SUM(fld_receivable_amount) AS receivableAmount
    FROM es_bond_refund_detail
    WHERE fld_refund_guid IN
    <foreach collection="refundGuidList"
             item="guid"
             open="("
             separator=","
             close=")">
        #{guid}
    </foreach>
    GROUP BY fld_refund_guid
</select>

Service 层调用:

java 复制代码
List<Long> refundGuidList = dataList.stream()
        .map(RefundRecordVo::getFldRefundGuid)
        .filter(Objects::nonNull)
        .toList();

if (CollectionUtils.isEmpty(refundGuidList)) {
    return Result.ok();
}

List<RefundReceivableDto> receivableDtoList =
        esBondRefundDetailMapper.queryReceivableAmountByRefundGuids(refundGuidList);

五、额外注意点

如果 refundGuidList 为空,SQL 可能会变成:

sql 复制代码
WHERE fld_refund_guid IN ()

这会导致 SQL 语法错误。

所以在调用 Mapper 前,最好先判断集合是否为空:

java 复制代码
if (CollectionUtils.isEmpty(refundGuidList)) {
    return Result.ok();
}

另外,如果需要返回分页结构,不建议直接返回空的 Result.ok(),最好返回完整的分页对象,避免前端解析异常。


六、总结

这次报错的核心原因是:

MyBatis XML 中使用的参数名,与 Mapper 接口中实际传入的参数名不一致。

对于单个 List 参数:

如果没有加 @Param,MyBatis 默认参数名是:

text 复制代码
list、collection、arg0

如果 XML 中想使用自定义名称,比如:

xml 复制代码
collection="refundGuidList"

就必须在 Mapper 接口中添加:

java 复制代码
@Param("refundGuidList")

最终推荐写法:

java 复制代码
List<RefundReceivableDto> queryReceivableAmountByRefundGuids(
        @Param("refundGuidList") List<Long> refundGuidList
);
相关推荐
Raas10013 分钟前
AI网关能做语义缓存吗?MAI Gateway(魔芋企业级AI网关)实战能力深度解读
java·后端·spring
天空鸟_时光不老20 分钟前
13-亮点提炼:把技术说辞翻译成业务价值
java·spring boot·spring·spring cloud·mybatis
小蒜学长24 分钟前
基于SpringBoot+Vue的租房管理系统的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·租房管理
C++ 老炮儿的技术栈26 分钟前
char (*csConnectName)[256]; 和 char csConnectName [256] 有什么区别
java·c语言·开发语言·c++·人工智能·算法·c
洋就在江州30 分钟前
gitlab-cicd 离线集成——springboot-cicd (非docker形式,shell形式)
java·spring boot·后端·ci/cd·gitlab·gitlab-runner
CEZ37 分钟前
JeeWMS 开源 WMS 全景导览:一套 Java 仓库管理系统从单据流到代码入口的阅读地图
java·开源
Nebula_g37 分钟前
JavaSE加强:线程Thread(线程安全重点)
java·开发语言·jvm·算法·javase·技术栈
令狐少侠201142 分钟前
centos7 下,使用docker 部署IOT 调试平台,ThingsBoard 并完美启动
java·spring boot·iot
JAVA面经实录9171 小时前
Java高级后端 · 全套面试通关手册(线上故障排查)
java·jvm·面试
IT_Octopus1 小时前
【零基础入门 LLM 开发 · Day 11】:ChatOpenAI vs init_chat_model——一行换供应商
java·前端·javascript