这个问题有不少小伙伴遇到过,也给出了解决方案,但是没有探究原因,这次读一下源码,看看原因在哪里。
1. 条件失效情况复现
- Mapper.xml内的动态SQL如下【伪代码】
<select id="getInfoList" parameterType="java.util.Map" resultType="java.util.Map"> SELECT * ${schemaName}${tableName} <where> <if test="viewId != null and viewId != ''"> AND viewid = #{viewId} </if> </where> </select>
- 调用动态SQL的方法如下【伪代码主要是显示一下传递的参数值】
Map<String, Object> mapParam = new HashMap<>(4); mapParam.put("schemaName", "public"); mapParam.put("tableName", "info_table"); mapParam.put("viewId", 0); queryInterface.getInfoList(mapParam);
查看查询结果会发现对 viewId 没有进行筛选。
2. 解决方法
去掉判断条件 and viewId != ''
即可。
<select id="getInfoList" parameterType="java.util.Map" resultType="java.util.Map"> SELECT * ${schemaName}${tableName} <where> <if test="viewId != null"> AND viewid = #{viewId} </if> </where> </select>
3. 源码解析
到底是为什么呢?我们找到 Mybatis 的 IfSqlNode 对象:
下边是打断点进行的参数追踪:
evaluator.evaluateBoolean(test, context.getBindings())
为 true 时当前节点才会被应用。
进入 evaluateBoolean(test, context.getBindings())
方法。
进入 OgnlCache.getValue(expression, parameterObject)
方法。关键方法出现了:
Ognl.getValue(parseExpression(expression), context, root);
我们找到
Ognl.getValue(parseExpression(expression), context, root)
方法。
中间省略了部分方法,省略的方法主要是查找参数名称和参数值,不重要故未贴出。还有判断节点类型的过程,例子中用的的都是
不等于
类型的节点。下边的方法真正开始对表达式两侧的数值进行比较了。前半段的viewId != null
不再贴出,只截图有问题的后半段:
进入最核心的方法比较方法
compareWithConversion(Object v1, Object v2)
看一下转换的过程:
问题的核心代码:
public static double doubleValue(Object value) throws NumberFormatException { if (value == null) { return 0.0D; } else { Class c = value.getClass(); if (c.getSuperclass() == Number.class) { return ((Number)value).doubleValue(); } else if (c == Boolean.class) { return (Boolean)value ? 1.0D : 0.0D; } else if (c == Character.class) { return (double)(Character)value; } else { String s = stringValue(value, true); return s.length() == 0 ? 0.0D : Double.parseDouble(s); } } }
由于 "".length() = 0
,传递的参数 viewId
值是 0️⃣ 时 viewId != ''
就变成 0.0 != 0.0
这个自然是 false
此时,筛选条件不起作用就不足为奇了。
为什么会出现这种情况?是框架问题吗? 感觉不是的,我们在编程的时候,对数值类型值的判断本身就不应该使用 =='' 或者 !=''
这样的条件。