mybatis中关于使用in查询数据为空时处理方式

本文通过一个具体的MyBatis动态SQL示例,详细解释了如何使用<if>和<foreach>标签来构建复杂的SQL查询语句,特别是在处理数组或集合参数时的技巧。
select count(*) from eco_inspect_info

<where>
    <if test="sqlParam!=null and sqlParam!=''">and stcd in
        <foreach item="stcd" index="index" collection="sqlParam" open="(" separator="," close=")">
            #{stcd}
        </foreach>
    </if>
</where>

红色部分为数据集合,其中sqlParam为数组 或者map,item为循环遍历的值,与#{}里面一致

dao里的请求方法为

List<EcoInspectInfo> findByEcoInspect(@Param("sqlParam") String[] sqlParam);

 

<think> 在MyBatis中,使用<if>标签进行条件判断,如果判断的是一个集合(如List)是否为,通常我们会使用`collection != null and collection.size() > 0`这样的表达式。但是,如果传入的集合为null或者集合,我们不希望执行这个条件,以避免因为条件不满足而查询全表的情况。 然而,有候我们可能不小心在动态SQL中只判断了集合不为,但没有考虑到如果其他条件都不满足,可能会导致查询全表。为了避免这种情况,我们可以采取以下措施: 1. 确保在动态SQL的<where>标签内,至少有一个有效的条件(比如一个必须存在的条件)来避免全表查询。或者,我们可以使用一个永远成立的条件(如1=1)来兜底,但这样可能不太优雅。 2. 在<if>判断集合,除了判断非,还要确保在集合为,该条件不会成为查询条件的一部分,从而不会导致where子句为。 3. 另外,我们可以在业务逻辑层进行判断,如果集合为,并且没有其他条件,则直接返回结果,而不执行数据查询。 下面是一个示例,展示如何在MyBatis中安全地使用<if>判断集合,并避免查询全表: 假设我们有一个查询,需要根据传入的id列表查询用户,同还有一个用户名作为可选条件。我们想要避免当id列表为并且用户名也为查询全表。 ```xml <select id="selectUsers" resultType="User"> SELECT * FROM user <where> <!-- 如果ids不为,则添加id in条件 --> <if test="ids != null and ids.size() > 0"> id IN <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach> </if> <!-- 如果同有username条件,可以加上 --> <if test="username != null and username != ''"> AND username = #{username} </if> <!-- 为了防止前面的条件都不满足,我们可以在where标签内部添加一个永远不成立的条件(例如1=0),这样当没有条件满足,where子句就不会生成,也就不会查询全表。但是这样会导致查询不到数据,而不是查询全表。 --> <!-- 或者,我们可以要求至少有一个条件,如果没有条件,就不执行查询(在业务层控制)。 --> </where> </select> ``` 但是上面的例子中,如果ids为且username为,那么<where>标签内部就不会生成任何条件,因此SQL会变成`SELECT * FROM user`,这就是全表查询。 为了避免全表查询,我们可以这样做: 方案一:在业务层进行判断,如果所有条件都为,则直接返回列表。 方案二:在MyBatis的SQL映射中,添加一个永远不成立的条件(例如`1=0`)作为兜底,但是这样会导致没有条件查询不到数据。但是这样还是生成了where条件,避免了全表扫描,但可能不是我们想要的行为(我们可能希望在没有条件不返回任何数据,而不是查询全表)。 例如: ```xml <select id="selectUsers" resultType="User"> SELECT * FROM user <where> <choose> <!-- 当ids不为使用ids查询 --> <when test="ids != null and ids.size() > 0"> id IN <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach> </when> <!-- 当ids为,但是username不为使用username查询 --> <when test="username != null and username != ''"> username = #{username} </when> <!-- 否则,加上一个永远不成立的条件 --> <otherwise> 1=0 </otherwise> </choose> </where> </select> ``` 这样,当ids为且username为,就会加上`1=0`条件,从而不会返回任何数据,同也避免了全表扫描。 方案三:在MyBatis的SQL映射中,使用<trim>标签,并设置一个默认条件(比如1=0),然后动态添加其他条件。但这样写起来可能比较复杂。 方案四:在方法调用前,在业务逻辑层进行校验,如果集合为且其他条件都为,则直接返回集合,不执行数据查询。这是推荐的做法,因为这样可以避免不必要的数据库访问。 综上所述,我们可以在业务层进行控制,也可以使用MyBatis的动态SQL来避免全表查询。具体采用哪种方式,取决于业务需求。 下面是一个在业务层进行控制的例子(Java代码): ```java public List<User> selectUsers(List<Long> ids, String username) { // 如果ids为且username为,则直接返回集合 if ((ids == null || ids.isEmpty()) && (username == null || username.isEmpty())) { return Collections.emptyList(); } // 否则调用MyBatis查询 return userMapper.selectUsers(ids, username); } ``` 这样,在条件都不满足,就不会执行SQL查询,也就避免了全表扫描。 在MyBatis中,我们也可以使用`<if>`标签结合`<where>`标签的智能处理(它会去掉开头的AND或OR),但是仍然无法避免条件全部为where子句不存在的问题。因此,最安全的方式还是在业务层判断。 §§相关问题§§: 1. MyBatis中动态SQL的<where>标签有什么作用? 2. 如何使用MyBatis的<foreach>标签遍历集合? 3. 在MyBatis中,除了<if>和<foreach>,还有哪些常用的动态SQL标签? 4. 如何避免在MyBatis动态SQL中因为条件不满足而导致查询全表? </think>在 MyBatis 中,使用 `<if test>` 标签判断集合,如果直接使用 `collection != null` 可能会导致集合被判定为有效条件,从而生成 `WHERE IN ()` 这样的非法 SQL(报语法错误),或者当其他条件都不满足导致查询全表。以下是解决方案: ### 解决方案 1. **同检查 `null` 和集合**:使用 `collection != null and !collection.isEmpty()` 2. **添加兜底条件**:在动态 SQL 中设置一个永假条件(如 `1=0`)避免全表查询 3. **结合 `<choose>` 标签**:提供更清晰的条件分支逻辑 ### 示例代码 ```xml <select id="selectUsers" resultType="User"> SELECT * FROM users <where> <choose> <!-- 情况1: idList 非 --> <when test="idList != null and !idList.isEmpty()"> id IN <foreach collection="idList" item="id" open="(" separator="," close=")"> #{id} </foreach> </when> <!-- 情况2: name 非 --> <when test="name != null and name != ''"> name = #{name} </when> <!-- 兜底: 所有条件都为返回结果 --> <otherwise> 1 = 0 <!-- 永假条件避免全表查询 --> </otherwise> </choose> </where> </select> ``` ### 关键点解释: 1. **双重条件检查**: ```xml test="idList != null and !idList.isEmpty()" ``` - `null` 检查:防止未传入集合参数出错 - `isEmpty()` 检查:防止传入集合生成 `IN ()` 的非法 SQL 2. **`<choose>` 标签**: - 按顺序检查条件分支 - 第一个满足的 `<when>` 会被执行 - `<otherwise>` 作为兜底处理 3. **永假条件 `1=0`**: - 当所有条件都不满足激活 - 确保返回结果而不是全表数据 - 比 `WHERE 1=1` 更安全(不会意外查询全表) 4. **`<where>` 标签作用**: - 自动处理 `WHERE` 关键字 - 智能去除开头多余的 `AND`/`OR` - 无有效条件整个 `WHERE` 子句不生成 ### 业务层配合方案 在调用 Mapper 前进行校验: ```java // Service 层调用前检查 public List<User> findUsers(List<Long> idList, String name) { if ((idList == null || idList.isEmpty()) && (name == null || name.isBlank())) { return Collections.emptyList(); // 直接返回集合 } return userMapper.selectUsers(idList, name); } ``` ### 最佳实践总结 | 场景 | 处理方式 | 效果 | |----------------------|----------------------------------|--------------------------| | 集合为 null | `idList != null` 拦截 | 不进入条件分支 | | 集合 (`[]`) | `!idList.isEmpty()` 拦截 | 避免生成 `IN ()` | | 所有条件无效 | `<otherwise>1=0</otherwise>` | 返回结果 | | 业务层预校验 | 提前返回集合 | 避免执行 SQL |
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值