在 MyBatis 中编写 IN 子句,最核心的工具是 <foreach> 标签。根据您的需求,通常分为两种场景:单字段 IN(如 id IN (1,2,3))和 多字段组合 IN(如 (id, name) IN ((1,'张三'), (2,'李四')))。
1. 单字段 IN(最常见)
如果您只是传入一个 List 或数组,查询某个字段是否在集合中:
Java 接口:
Java
List<User> selectByIds(@Param("idList") List<Long> idList);
XML 映射文件:
XML
<select id="selectByIds" resultType="User">
SELECT * FROM sys_user
WHERE id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
2. 多字段组合 IN (Tuple IN)
如果您需要同时匹配多个字段(例如:根据“编码”和“类型”两个维度锁定一组数据),可以使用 SQL 的元组语法。
Java 接口:
Java
// 假设 ProductDTO 包含 id 和 category 属性
List<Product> selectByCompositeKeys(@Param("productList") List<ProductDTO> productList);
XML 映射文件:
XML
<select id="selectByCompositeKeys" resultType="Product">
SELECT * FROM product
WHERE (id, category) IN
<foreach collection="productList" item="item" open="(" separator="," close=")">
(#{item.id}, #{item.category})
</foreach>
</select>
注:这种语法在 MySQL、PostgreSQL、Oracle 中通常支持良好,但在 SQL Server 中可能不支持,需要改写为多组 OR 连接。
3. <foreach> 标签参数详解
为了确保代码不出错,理解这几个属性非常重要:
| 属性 | 说明 |
| collection | 必须。指定传入的集合参数名。若没用 @Param,List 默认为 list,数组为 array。 |
| item | 必须。集合中元素迭代时的别名,在 #{} 中使用。 |
| open | 循环开始时的字符串(通常是 ()。 |
| separator | 每次循环之间的分隔符(通常是 ,)。 |
| close | 循环结束时的字符串(通常是 ))。 |
| index | 可选。当前迭代的索引值(Map 时为 Key)。 |
💡 进阶避坑指南
1. 集合为空的判空处理
如果 idList 为空,生成的 SQL 会变成 WHERE id IN (),这会导致 SQL 语法错误。
建议写法:
XML
<where>
<if test="idList != null and idList.size() > 0">
AND id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
<if test="idList == null or idList.size() == 0">
AND 1 = 0 </if>
</where>
2. 集合长度限制
- MySQL:
max_allowed_packet限制了 SQL 的大小,虽然可以设置很大,但IN里的元素过多(如超过 1000 个)会导致性能急剧下降。 - Oracle:
IN列表中的表达式数量硬限制为 1000 个。 - 方案: 如果数据量极大,建议分批查询,或者将数据插入临时表进行
JOIN查询。
3. 性能优化
如果您只需要查询一个字段且不需要去重,直接使用 IN 即可。但如果列表非常长,考虑使用 EXISTS 配合子查询,有时性能会更优。
您目前的列表数据量大概在什么级别?如果超过 1000 条,我建议为您提供分批处理的代码示例。
欢迎关注公众号