MyBatis Error evaluating expression 'ids'. Return value (3) was not iterable 错误详解
一、错误现象
运行测试时抛出异常:
php
org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: org.apache.ibatis.builder.BuilderException:
Error evaluating expression 'ids'. Return value (3) was not iterable.
错误位置在 com.xie.mapper.CarMapper.deleteById 方法。
二、错误含义
这个错误表明:MyBatis 在处理 <foreach> 标签时,预期 collection 属性的值是一个可迭代对象(Iterable,如 List、Set、Array 等),但实际接收到的是一个不可迭代的值(如单个 Long 类型的 3)。
错误信息中的 Return value (3) was not iterable 明确指出:
- 表达式
ids的返回值是3 3是一个数字,不是集合/数组,无法被foreach遍历
三、根本原因分析
3.1 代码示例(错误情况)
你的 Mapper 接口中可能定义了这样的方法:
java
public interface CarMapper {
// ❌ 错误:参数名为 ids,但实际传入的是单个值
int deleteById(Long ids);
}
对应的 XML 映射中使用了 <foreach> 标签:
xml
<delete id="deleteById">
DELETE FROM t_car
WHERE id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
测试代码中这样调用:
java
@Test
public void test5() {
// ❌ 传入的是单个 Long 值,而不是集合
carMapper.deleteById(3L);
}
3.2 执行流程
- MyBatis 解析 XML,遇到
<foreach collection="ids">,需要从参数中获取名为ids的值。 - 参数是
Long类型的3,没有@Param注解,MyBatis 将参数放入 Map 时使用默认键arg0或param1,但没有ids这个键。 - OGNL 表达式解析时,发现
ids不是一个可遍历的集合,而是数值3,抛出not iterable异常。
四、解决方案
方案一:参数改为集合类型(推荐)
如果业务是批量删除,将参数改为 List 或 Array:
java
public interface CarMapper {
// ✅ 正确:参数为 List
int deleteByIds(@Param("ids") List<Long> ids);
}
测试代码:
java
@Test
public void test5() {
List<Long> ids = Arrays.asList(1L, 2L, 3L);
carMapper.deleteByIds(ids);
}
XML 保持不变,collection="ids" 可以正常遍历。
方案二:单个删除使用 = 而不是 IN
如果业务是删除单条记录,不应使用 <foreach>,直接用 = 即可:
java
public interface CarMapper {
int deleteById(Long id);
}
xml
<delete id="deleteById">
DELETE FROM t_car WHERE id = #{id}
</delete>
方案三:多参数时使用 @Param 注解
如果确实需要传入集合,务必加上 @Param 注解:
java
public interface CarMapper {
int deleteByIds(@Param("ids") List<Long> ids);
}
这样 collection="ids" 才能正确匹配到参数。
五、错误排查流程图
java
┌─────────────────────────────────────────────────────────────┐
│ Error evaluating expression 'xxx'. Return value (?) │
│ was not iterable │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Step 1: 检查 XML 中 <foreach> 的 collection 属性值 │
│ 是否与 @Param 注解名称一致? │
└─────────────────────────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
一致 不一致
│ │
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────────┐
│ Step 2: 检查方法参数 │ │ 修改 collection 属性或 │
│ 是否是集合/数组类型? │ │ @Param 注解,保持一致 │
└─────────────────────────┘ └─────────────────────────────┘
│
┌─────────┴─────────┐
│ │
▼ ▼
是 否
│ │
▼ ▼
┌──────────────┐ ┌──────────────────────────────────────┐
│ 检查是否实际 │ │ 错误:传入的是单个值 │
│ 传入了空集合 │ │ 方案:改用 = 查询或改为传入集合 │
└──────────────┘ └──────────────────────────────────────┘
六、常见易错点总结
| 错误场景 | 错误现象 | 解决方案 |
|---|---|---|
传入单个值但 XML 使用 IN + foreach |
not iterable |
改用 = 查询,或传入集合 |
有 @Param("ids") 但 XML 写 list |
not found |
统一使用 ids |
无 @Param 但 XML 写自定义名称 |
not found |
使用 list/array 或加 @Param |
传入 null 集合 |
not iterable 或空结果 |
先判空再使用 <foreach> |
传入 List 但 XML 写成 array |
not found |
统一使用 @Param 注解 |
七、正确代码示例
7.1 批量删除(推荐写法)
java
// Mapper 接口
public interface CarMapper {
int deleteByIds(@Param("ids") List<Long> ids);
}
xml
<delete id="deleteByIds">
DELETE FROM t_car
WHERE id IN
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</if>
</delete>
7.2 单个删除
java
// Mapper 接口
public interface CarMapper {
int deleteById(@Param("id") Long id);
}
xml
<delete id="deleteById">
DELETE FROM t_car WHERE id = #{id}
</delete>
7.3 测试代码
java
@Test
public void test5() {
// 批量删除
List<Long> ids = Arrays.asList(1L, 2L, 3L);
carMapper.deleteByIds(ids);
// 或单个删除
carMapper.deleteById(3L);
}
八、总结
| 要点 | 说明 |
|---|---|
| 错误本质 | <foreach> 期望可迭代对象,但收到不可迭代的单个值 |
| 根本原因 | 方法参数类型与 XML 中 collection 对应的值类型不匹配 |
| 解决方案 | 传入集合类型,或改用 = 查询 |
| 最佳实践 | 批量操作使用 List + @Param;单个操作使用 = |
| 空值处理 | 使用 <if> 判断集合非空后再执行 <foreach> |
not iterable 错误是 MyBatis 动态 SQL 中非常常见的问题,核心在于正确理解 @Param 注解和 collection 属性的对应关系。记住:<foreach> 的 collection 必须指向一个可迭代的对象,不能指向单个值。