mybatis foreach标签(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

什么是MyBatis的foreach标签?

MyBatis的foreach标签是动态SQL中一个非常强大的工具,它允许我们在SQL语句中循环遍历集合数据(如List、数组、Map等),从而实现灵活的条件查询或批量操作。简单来说,它就像是一个“批量处理的瑞士军刀”,能够将原本需要多次执行的SQL操作合并为一次执行,显著提升效率。

想象一下,快递分拣中心的传送带:每个包裹(数据元素)被逐一扫描并分拣到对应的区域(SQL条件)。foreach标签的作用,就是让MyBatis自动完成这个分拣过程,开发者只需要定义规则即可。

基础语法与核心概念

1. 基本语法结构

foreach标签的XML语法如下:

<foreach  
    collection="集合名称"  
    item="元素别名"  
    index="索引变量(可选)"  
    open="循环前添加的字符串"  
    separator="元素之间的分隔符"  
    close="循环后添加的字符串"  
>  
    <!-- 被循环的SQL片段 -->  
</foreach>  
  • collection:指定遍历的集合类型,如listarraymap.entrySet等。
  • item:定义每次循环中当前元素的变量名。
  • index:当遍历Map时,可用此变量表示键值对的索引(默认为键)。
  • open/separator/close:分别定义循环前、元素间、循环后的字符串,类似模板拼接。

2. 参数传递与集合类型

在使用foreach时,需要将集合数据通过MyBatis的参数传递到SQL语句中。例如:

// Java代码传递参数  
List<String> ids = Arrays.asList("1", "2", "3");  
mapper.selectByIds(ids);  

// XML中接收参数  
<select id="selectByIds" resultType="User">  
    SELECT * FROM user  
    WHERE id IN  
    <foreach collection="list" item="id" open="(" separator="," close=")">  
        #{id}  
    </foreach>  
</select>  

这里,collection="list"表示接收的是一个List类型参数,每个元素被赋值给item="id"。最终生成的SQL为:

SELECT * FROM user WHERE id IN (1, 2, 3)  

常见用法场景与案例

1. IN条件的批量查询

这是foreach最经典的用法,适用于多个ID或值的快速筛选。

案例需求:根据用户输入的多个用户ID,查询对应的所有用户信息。

<!-- XML配置 -->  
<select id="selectUsersByIds" resultType="User">  
    SELECT * FROM user  
    WHERE id IN  
    <foreach item="id" collection="list" open="(" separator="," close=")">  
        #{id}  
    </foreach>  
</select>  
// 调用示例  
List<Integer> ids = Arrays.asList(101, 102, 103);  
List<User> users = userMapper.selectUsersByIds(ids);  

2. BETWEEN条件的动态生成

当需要根据多个区间进行查询时,foreach可以灵活生成多个BETWEEN条件。

案例需求:查询价格在多个区间内的商品,例如价格在[100, 200]或[500, 600]之间的商品。

<select id="selectProductsByPriceRanges" resultType="Product">  
    SELECT * FROM product  
    WHERE (  
        <foreach collection="list" item="range" separator=" OR ">  
            price BETWEEN #{range.start} AND #{range.end}  
        </foreach>  
    )  
</select>  
// Java参数构造  
List<PriceRange> ranges = Arrays.asList(  
    new PriceRange(100, 200),  
    new PriceRange(500, 600)  
);  
List<Product> products = productMapper.selectProductsByPriceRanges(ranges);  

此时,生成的SQL会自动组合多个BETWEEN条件,并用OR连接。

3. 批量插入与更新

foreach还能简化批量操作的SQL编写,避免手动拼接重复代码。

案例需求:批量插入多条用户数据。

<insert id="insertUsers">  
    INSERT INTO user (name, age) VALUES  
    <foreach collection="list" item="user" separator=",">  
        (#{user.name}, #{user.age})  
    </foreach>  
</insert>  
// 调用示例  
List<User> users = Arrays.asList(  
    new User("Alice", 30),  
    new User("Bob", 25)  
);  
userMapper.insertUsers(users);  

4. 自定义分隔符与模板

通过openseparatorclose属性,可以精确控制生成的SQL格式。例如:

<!-- 生成逗号分隔的字符串列表 -->  
<foreach item="tag" collection="tags" open="(" separator="," close=")">  
    #{tag}  
</foreach>  

输出结果类似:(Java, Python, SQL)

进阶技巧与高级用法

1. 遍历Map的键值对

当需要遍历Map时,可以通过indexitem分别获取键和值:

<!-- 查询多个字段的组合条件 -->  
<select id="selectByMap" resultType="User">  
    SELECT * FROM user  
    WHERE  
    <foreach collection="map.entrySet" item="entry" separator=" AND ">  
        ${entry.key} = #{entry.value}  
    </foreach>  
</select>  
// 参数示例  
Map<String, Object> params = new HashMap<>();  
params.put("name", "Alice");  
params.put("age", 30);  
List<User> users = userMapper.selectByMap(params);  

生成的SQL为:WHERE name = 'Alice' AND age = 30

2. 动态构建JOIN语句

在复杂查询中,foreach可动态拼接多个JOIN条件。例如:

<!-- 根据表名列表动态JOIN多个关联表 -->  
<select id="selectWithDynamicJoins">  
    SELECT * FROM main_table  
    <foreach collection="tables" item="table" open=" " separator=" " close="">  
        LEFT JOIN #{table} ON main_table.id = #{table}_id  
    </foreach>  
</select>  

但需注意:动态表名可能引发SQL注入风险,需严格校验输入。

3. 分页与性能优化

在使用foreach处理大数据时,需结合分页技术避免单次查询过大。例如:

<!-- 分页查询 -->  
<select id="selectUsersWithPagination" resultType="User">  
    SELECT * FROM user  
    WHERE id IN  
    <foreach item="id" collection="list" open="(" separator="," close=")">  
        #{id}  
    </foreach>  
    LIMIT #{offset}, #{limit}  
</select>  

或使用MyBatis的RowBounds分页参数:

List<User> users = userMapper.selectUsersByIds(ids, new RowBounds(offset, limit));  

性能优化与常见问题

1. 避免参数类型不匹配

若传递的参数类型与collection属性不匹配(如将Map传入collection="list"),会引发Unclosed cursor等异常。始终确保:

  • List → collection="list"
  • Map → collection="map"collection="map.entrySet"
  • 数组 → collection="array"

2. 分页与IN子句的冲突

当IN条件中的元素过多时(如超过数据库的SQL长度限制),需拆分请求。例如:

// 拆分List为多个小批量  
int batchSize = 1000;  
for (int i = 0; i < ids.size(); i += batchSize) {  
    List<Integer> subList = ids.subList(i, Math.min(i + batchSize, ids.size()));  
    // 执行分批查询  
}  

3. 预编译参数的使用

避免直接拼接字符串(如${item}),改用#{item}防止SQL注入。例如:

<!-- 正确写法 -->  
WHERE column = #{item}  

<!-- 错误写法(可能引发注入风险) -->  
WHERE column = ${item}  

实战案例:复杂查询的动态构建

假设需要根据用户输入的多个条件(如年龄范围、地区、性别)动态生成查询:

1. 需求描述

用户可输入任意组合的筛选条件,包括:

  • 年龄范围(如18-30岁)
  • 地区列表(如["北京", "上海"])
  • 性别(如男/女)

2. 实现步骤

<!-- Mapper XML -->  
<select id="searchUsers" resultType="User">  
    SELECT * FROM user  
    WHERE 1=1  
    <if test="ageStart != null and ageEnd != null">  
        AND age BETWEEN #{ageStart} AND #{ageEnd}  
    </if>  
    <if test="regions != null and !regions.isEmpty()">  
        AND region IN  
        <foreach item="region" collection="regions" open="(" separator="," close=")">  
            #{region}  
        </foreach>  
    </if>  
    <if test="gender != null">  
        AND gender = #{gender}  
    </if>  
</select>  
// 调用示例  
SearchRequest req = new SearchRequest();  
req.setAgeStart(18);  
req.setAgeEnd(30);  
req.setRegions(Arrays.asList("Beijing", "Shanghai"));  
req.setGender("M");  

List<User> users = userMapper.searchUsers(req);  

3. 生成的SQL示例

SELECT * FROM user  
WHERE 1=1  
AND age BETWEEN 18 AND 30  
AND region IN ('Beijing', 'Shanghai')  
AND gender = 'M'  

结论与扩展学习

通过本文,我们系统学习了MyBatis foreach标签的核心功能、语法细节及实战案例。它不仅是处理批量操作的高效工具,更是构建动态SQL的“瑞士军刀”。

对于进阶开发者,建议进一步探索以下方向:

  1. 动态SQL的其他标签:如<if><choose><trim>等,结合foreach实现更复杂的逻辑。
  2. MyBatis-Plus:基于MyBatis的增强框架,提供更简洁的批量操作API。
  3. 数据库性能调优:分析foreach生成的SQL执行计划,避免全表扫描。

掌握foreach标签,不仅能提升代码的复用性和可维护性,更是构建灵活、高效数据访问层的关键一步。希望本文能成为你MyBatis学习路上的可靠指南!

最新发布