mybatis的空字符串判断产生的问题

本文讨论了在Mybatismapper查询中,使用`iftest`进行条件判断时遇到的问题,即数值0被误判为等同于空字符串。解决办法是移除`type!==null`中的`!=null`,因为数值类型无需再与空字符串作比较,只需检查是否为null即可避免问题。

问题描述

当我在mybatis的mapper查询中使用

<if test="type != null and type != ''">

条件判断时,当type的类型是数值,并且为0时,type != ''type不为空字符串为false,这是因为mybatis在将数值与字符串做比较时,0是等于空字符串的,这样显然会出问题,导致在查询时type=0无法生效。

解决

去掉type != ''只保留type != null,因为type是数值类型,所以只需要判断不为null即可

<think>根据用户需求,需要在MyBatis的XML文件中使用<if>标签判断多个集合(casList、cnNameList、enNameList)是否都为。如果都为,则不执行某些SQL片段;如果至少有一个不为,则执行。 在MyBatis中,判断集合是否为通常使用`!= null`和`size() > 0`(或者`!empty`,但MyBatis的OGNL表达式没有直接的empty,所以一般用`size()>0`或`!=null and 集合.size>0`)。要判断多个集合都为,即三个集合都为null或者都为集合(size为0)。 我们可以这样写: 条件:casList为 且 cnNameList为 且 enNameList为 注意:有两种情况:null或者集合。 因此,判断一个集合为可以写为:`集合 == null or 集合.size() == 0`(或者简写为`集合 == null or 集合.isEmpty()`,但MyBatis中通常用`size()`方法)。 那么判断多个集合都为,就是: (casList == null or casList.size() == 0) and (cnNameList == null or cnNameList.size() == 0) and (enNameList == null or enNameList.size() == 0) 但是,在<if>标签中,我们通常需要的是当条件不满足(即不是都为)的时候才执行SQL片段。所以,我们可能更常用的是判断至少有一个不为,然后执行。不过用户要求判断都为,那么我们可以用: 方案1:使用<if>判断,当三个集合都为时,不执行;否则执行。但是,如果我们想表达的是“当不是三个都为”时执行,那么我们可以用: <if test="!( (casList == null or casList.size() == 0) and (cnNameList == null or cnNameList.size() == 0) and (enNameList == null or enNameList.size() == 0) )"> ... 这里写要执行的SQL ... </if> 方案2:或者,我们也可以写成: <if test="(casList != null and casList.size() > 0) or (cnNameList != null and cnNameList.size() > 0) or (enNameList != null and enNameList.size() > 0)"> ... 这里写要执行的SQL ... </if> 这两种写法是等价的,都是判断至少有一个集合不为且非。 但是,根据用户的需求,他是希望当三个都为的时候不执行,否则执行。所以两种写法都可以。 然而,第一种写法使用了否定和多个and,表达式较长;第二种写法更直观,即只要有一个集合不为就执行。 因此,推荐使用第二种写法。 示例: ```xml <select id="selectByCondition" resultMap="someResultMap"> SELECT * FROM some_table WHERE some_condition <if test="(casList != null and casList.size() > 0) or (cnNameList != null and cnNameList.size() > 0) or (enNameList != null and enNameList.size() > 0)"> AND ( <if test="casList != null and casList.size() > 0"> cas_column IN <foreach collection="casList" item="cas" open="(" separator="," close=")"> #{cas} </foreach> </if> <if test="cnNameList != null and cnNameList.size() > 0"> <if test="casList != null and casList.size() > 0">OR</if> cn_name_column IN <foreach collection="cnNameList" item="cn" open="(" separator="," close=")"> #{cn} </foreach> </if> <if test="enNameList != null and enNameList.size() > 0"> <if test="(casList != null and casList.size() > 0) or (cnNameList != null and cnNameList.size() > 0)">OR</if> en_name_column IN <foreach collection="enNameList" item="en" open="(" separator="," close=")"> #{en} </foreach> </if> ) </if> </select> ``` 但是注意:上面的内部条件拼接使用了OR,并且每个条件内部是IN查询。另外,注意在多个条件之间用OR连接时,需要判断前面的条件是否存在,如果不存在就不需要加OR。 不过,上面的写法在动态SQL中可能会产生语法错误,比如当第一个条件不存在而第二个条件存在时,会出现以OR开头的情况。为了避免这种情况,我们可以使用<trim>或者<where>标签,但这里是在AND后面的括号内,我们可以这样优化: 使用<where>标签通常用于整个WHERE子句,但这里只是WHERE子句的一部分(已经有一个AND),所以我们可以用<trim>来去除可能多余的OR。 但是,更简单的方式是使用一个<choose>来分别处理,或者使用一个更巧妙的动态拼接。 另一种思路:将三个条件分别用<if>生成,然后使用OR连接,但注意处理连接词。我们可以这样: ```xml <select id="selectByCondition" resultMap="someResultMap"> SELECT * FROM some_table WHERE some_condition <if test="(casList != null and casList.size() > 0) or (cnNameList != null and cnNameList.size() > 0) or (enNameList != null and enNameList.size() > 0)"> AND ( <trim prefixOverrides="OR | AND"> <!-- 这里使用trim去除可能出现在开头的OR或AND --> <if test="casList != null and casList.size() > 0"> cas_column IN <foreach collection="casList" item="cas" open="(" separator="," close=")"> #{cas} </foreach> </if> <if test="cnNameList != null and cnNameList.size() > 0"> OR cn_name_column IN <foreach collection="cnNameList" item="cn" open="(" separator="," close=")"> #{cn} </foreach> </if> <if test="enNameList != null and enNameList.size() > 0"> OR en_name_column IN <foreach collection="enNameList" item="en" open="(" separator="," close=")"> #{en} </foreach> </if> </trim> ) </if> </select> ``` 但是这样写,如果第一个条件存在,那么第二个和第三个条件前的OR是正常的;但如果第一个条件不存在,第二个条件存在,那么第二个条件前面就会多一个OR(因为trim是在整个内容开头有OR才会去除,而这里第二个条件前面有OR,但它是第一个出现的条件,所以开头会有OR)。因此,我们需要在生成条件时避免在开头出现OR。 我们可以这样调整:每个条件前都加上OR,然后用trim去除开头的OR(注意:trim的prefixOverrides可以同时去除多个,用竖线分割,注意格)。 所以,修改为: ```xml <trim prefixOverrides="OR | AND"> <if test="casList != null and casList.size() > 0"> OR cas_column IN ... </if> <if test="cnNameList != null and cnNameList.size() > 0"> OR cn_name_column IN ... </if> <if test="enNameList != null and enNameList.size() > 0"> OR en_name_column IN ... </if> </trim> ``` 这样,不管哪个条件存在,trim都会去掉开头的OR(如果有的话)。但是,这样写会导致每个条件前都有OR,包括第一个条件,所以去掉第一个条件前的OR后,剩下的条件之间的OR是正常的。 但是,我们也可以换一种写法:使用`<where>`标签包裹整个条件,但这里已经有一个AND,所以不适用。 或者,我们可以这样:将三个条件用`OR`连接,然后放在括号里,用`1=0`来避免没有条件的情况。但是,我们外面已经判断了至少有一个集合不为,所以内部至少有一个条件,因此可以不用考虑没有条件的情况。 所以,我们这样写: ```xml <select id="selectByCondition" resultMap="someResultMap"> SELECT * FROM some_table WHERE some_condition <if test="(casList != null and casList.size() > 0) or (cnNameList != null and cnNameList.size() > 0) or (enNameList != null and enNameList.size() > 0)"> AND ( <!-- 使用trim去除开头可能多余的OR --> <trim prefixOverrides="OR"> <if test="casList != null and casList.size() > 0"> OR cas_column IN <foreach collection="casList" item="cas" open="(" separator="," close=")"> #{cas} </foreach> </if> <if test="cnNameList != null and cnNameList.size() > 0"> OR cn_name_column IN <foreach collection="cnNameList" item="cn" open="(" separator="," close=")"> #{cn} </foreach> </if> <if test="enNameList != null and enNameList.size() > 0"> OR en_name_column IN <foreach collection="enNameList" item="en" open="(" separator="," close=")"> #{en} </foreach> </if> </trim> ) </if> </select> ``` 注意:这里每个条件前都加了OR,然后trim的prefixOverrides设置为"OR",这样如果第一个条件存在,它前面会有OR,但trim会去掉开头的OR(实际上trim会从整个内容开头匹配OR并去掉),所以第一个条件前的OR被去掉了,后面的条件前的OR保留,作为条件之间的OR。 但是,这样写每个条件前都加OR,然后trim去掉开头的OR,这样就能保证条件之间用OR连接,且不会有多余的OR在开头。 另外,如果用户希望的是三个条件之间是AND关系,那么把OR换成AND即可,同样用trim去除开头的AND。 不过,根据用户的问题描述,他并没有说明这三个条件之间的关系。这里假设是OR关系(即满足casList中的条件,或者cnNameList中的条件,或者enNameList中的条件)。 总结:判断多个集合都为用: (casList == null or casList.isEmpty()) and (cnNameList == null or cnNameList.isEmpty()) and (enNameList == null or enNameList.isEmpty()) 但通常我们更希望判断至少有一个不为,所以用: (casList != null and !casList.isEmpty()) or (cnNameList != null and !cnNameList.isEmpty()) or (enNameList != null and !enNameList.isEmpty()) 在MyBatis的OGNL表达式中,集合的size方法可以用`size>0`,也可以写成`size()>0`,但通常写成`size>0`更简洁(OGNL支持属性访问,实际上会调用size()方法)。 所以,我们可以这样写: ```xml <if test="(casList != null and casList.size > 0) or (cnNameList != null and cnNameList.size > 0) or (enNameList != null and enNameList.size > 0)"> ``` 注意:在MyBatis 3.3.0之前,使用`集合.size`可能会报错,需要改为`集合.size()`,但3.3.0及之后支持属性风格的写法。 因此,如果使用较新版本的MyBatis,可以直接用`集合.size>0`;如果使用旧版本,则用`集合.size()>0`。 根据引用[5]中的写法,也是直接使用`reliability.size>0`,所以我们可以采用属性风格的写法。 最终,判断多个集合都为的条件就是: ```xml <if test="(casList == null or casList.size == 0) and (cnNameList == null or cnNameList.size == 0) and (enNameList == null or enNameList.size == 0)"> <!-- 都为时执行的内容 --> </if> ``` 或者,判断至少有一个不为: ```xml <if test="(casList != null and casList.size > 0) or (cnNameList != null and cnNameList.size > 0) or (enNameList != null and enNameList.size > 0)"> <!-- 至少有一个不为时执行的内容 --> </if> ``` 用户可以根据实际需求选择使用。 注意:在MyBatis中,判断集合是否为,除了判断null和size>0,还可以使用`@org.apache.commons.collections.CollectionUtils@isNotEmpty(集合)`,但需要导入包,一般不推荐,因为这样增加了依赖。 另外,引用[3]中提到,对于Integer类型,判断`!= ''`时,如果值为0会被误判为空字符。但是,这里我们判断的是集合,所以不会出现这个问题。集合的判断主要是null和size。 因此,针对用户的问题,给出以下两种写法: 写法一:判断三个集合都为(使用否定条件) ```xml <if test="!((casList != null and casList.size > 0) or (cnNameList != null and cnNameList.size > 0) or (enNameList != null and enNameList.size > 0))"> ... <!-- 当三个集合都为时,执行这里 --> </if> ``` 写法二:直接判断三个集合都为 ```xml <if test="(casList == null or casList.size == 0) and (cnNameList == null or cnNameList.size == 0) and (enNameList == null or enNameList.size == 0)"> ... </if> ``` 这两种写法是等价的。 但是,如果用户希望当三个集合都为时执行某个操作,就可以用上面的写法。如果希望当至少有一个集合不为时执行,则用: ```xml <if test="(casList != null and casList.size > 0) or (cnNameList != null and cnNameList.size > 0) or (enNameList != null and enNameList.size > 0)"> ... </if> ``` 请用户根据实际业务逻辑选择。</think>在 MyBatis 的 XML 文件中,判断多个集合(`casList`、`cnNameList`、`enNameList`)是否都为时,可以使用以下两种等效写法: ### 方法一:直接判断所有集合为(推荐) ```xml <if test="(casList == null or casList.isEmpty()) and (cnNameList == null or cnNameList.isEmpty()) and (enNameList == null or enNameList.isEmpty())"> <!-- 当所有集合都为时执行的 SQL --> </if> ``` ### 方法二:使用否定表达式 ```xml <if test="!((casList != null and !casList.isEmpty()) or (cnNameList != null and !cnNameList.isEmpty()) or (enNameList != null and !enNameList.isEmpty()))"> <!-- 当所有集合都为时执行的 SQL --> </if> ``` ### 关键点说明: 1. **集合判断标准**: - `list == null`:集合对象为 null - `list.isEmpty()`:集合为(size=0) - 两者需结合使用:`(list == null or list.isEmpty())` 2. **逻辑运算符**: - 判断**都为**需使用 `and` 连接所有条件 - 判断**至少一个非**需使用 `or` 连接 3. **集合大小判断优化**: ```xml <!-- 等价写法(更简洁) --> <if test="(casList == null or casList.size == 0) and (cnNameList == null or cnNameList.size == 0) and (enNameList == null or enNameList.size == 0)"> ``` ### 完整使用示例: ```xml <select id="searchData" resultType="YourResult"> SELECT * FROM your_table <where> <!-- 当至少有一个集合非时执行 --> <if test="(casList != null and casList.size > 0) or (cnNameList != null and cnNameList.size > 0) or (enNameList != null and enNameList.size > 0)"> AND ( <if test="casList != null and casList.size > 0"> cas_column IN <foreach collection="casList" item="cas" open="(" separator="," close=")"> #{cas} </foreach> </if> <if test="cnNameList != null and cnNameList.size > 0"> <if test="casList != null and casList.size > 0"> OR </if> cn_name_column IN <foreach collection="cnNameList" item="cn" open="(" separator="," close=")"> #{cn} </foreach> </if> <if test="enNameList != null and enNameList.size > 0"> <if test="(casList != null and casList.size > 0) or (cnNameList != null and cnNameList.size > 0)"> OR </if> en_name_column IN <foreach collection="enNameList" item="en" open="(" separator="," close=")"> #{en} </foreach> </if> ) </if> <!-- 当所有集合都为时的特殊处理 --> <if test="(casList == null or casList.isEmpty()) and (cnNameList == null or cnNameList.isEmpty()) and (enNameList == null or enNameList.isEmpty())"> AND default_column = 'special_value' </if> </where> </select> ``` ### 注意事项: 1. **值处理**: - 优先使用 `isEmpty()` 或 `size == 0` 判断集合 - 避免使用 `list != ''`(对集合类型无效)[^3] 2. **版本兼容性**: - MyBatis 3.3.0+ 支持 `list.size` 属性语法 - 旧版本需使用 `list.size()` 方法调用 3. **性能考虑**: - 当集合较大时,`IN` 查询可能有性能问题 - 可考虑分批查询或改用 `JOIN` 方式[^4][^5] 4. **动态 SQL 调试**: ```xml <!-- 开启 SQL 日志 --> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> ``` --- ### 相关问题 1. **如何在 MyBatis判断集合非并遍历元素?** > 使用 `<if test="list != null and list.size > 0">` 结合 `<foreach>` 标签实现安全遍历[^4][^5] 2. **MyBatis 的 `<if>` 标签中如何判断 Boolean 类型字段?** > 直接使用字段名:`<if test="isValid">`(true 时生效),注意避免使用 `isValid == true` 的冗余写法[^2] 3. **为什么 MyBatis 判断 Integer 类型字段时 `0 == ''` 成立?如何解决?** > OGNL 会将 `0` 视为空字符,应使用:`<if test="status != null">` 避免数值判断错误[^3] 4. **如何优化 MyBatis 中大型 `IN` 查询的性能?** > 可采用:① 分批次查询 ② 临时表 JOIN ③ 程序端拆分查询 ④ 数据库端数组参数(取决于数据库)[^4]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值