MyBatis的坑(动态SQL会把0和空串比较相等为true)
文章目录
- 前言
- 一、场景如下
- 二、原因分析
- 1. 源码分析
- 2. 写代码验证
- 三、解决办法
- 代码及执行结果如下
- 总结
前言
在开发过程中遇到MyBatis的动态SQL的if条件不生效的情况,但经过debuger发现并不是参数问题,已经拿到了参数并传给了MyBatis,且从表达式来看是为true的,那么为什么不生效呢?
一、场景如下
- mapper.xml代码:
- 模拟Service代码
按照传入的条件subsidySum为0,那么对于<if test="subsidySum != '' and subsidySum != null">
来说应该是为true才对,那么执行完整的SQL应该为:
select long_subsidy_his_id, subsidy_name, subsidy_sum
from long_term_subsidy_his
where 1 = 1and subsidy_name like CONCAT("%", ?, "%")
- 执行结果
我们发现动态SQL并没有如期为true把条件拼上,那么是为什么呢
<if test="subsidySum != '' and subsidySum != null">AND subsidy_sum like CONCAT("%",#{subsidySum},"%")
</if>
二、原因分析
现在问题很明显就是表达式if test="subsidySum != '' and subsidySum != null"
为false了,那么是为什么呢?
首先我先将subsidySum != ''
这个条件去掉,结果意外发现表达式竟然成立为true了,那么很明显问题就是出现在这个空串比较了,接着我们去看源码。
1. 源码分析
前面找源码过程省略,直接跳到关键地方
MyBatis 会将if标签的test属性使用ExpressionEvaluator测试一下是否为true或者为false:
public class ExpressionEvaluator {public boolean evaluateBoolean(String expression, Object parameterObject) {Object value = OgnlCache.getValue(expression, parameterObject);if (value instanceof Boolean) {return (Boolean) value;}if (value instanceof Number) {return new BigDecimal(String.valueOf(value)).compareTo(BigDecimal.ZERO) != 0;}return value != null;}/*** @deprecated Since 3.5.9, use the {@link #evaluateIterable(String, Object, boolean)}.*/@Deprecatedpublic Iterable<?> evaluateIterable(String expression, Object parameterObject) {return evaluateIterable(expression, parameterObject, false);}/*** @since 3.5.9*/public Iterable<?> evaluateIterable(String expression, Object parameterObject, boolean nullable) {Object value = OgnlCache.getValue(expression, parameterObject);if (value == null) {if (nullable) {return null;} else {throw new BuilderException("The expression '" + expression + "' evaluated to a null value.");}}if (value instanceof Iterable) {return (Iterable<?>) value;}if (value.getClass().isArray()) {// the array may be primitive, so Arrays.asList() may throw// a ClassCastException (issue 209). Do the work manually// Curse primitives! :) (JGB)int size = Array.getLength(value);List<Object> answer = new ArrayList<>();for (int i = 0; i < size; i++) {Object o = Array.get(value, i);answer.add(o);}return answer;}if (value instanceof Map) {return ((Map) value).entrySet();}throw new BuilderException("Error evaluating expression '" + expression + "'. Return value (" + value + ") was not iterable.");}}
从源码看出,MyBatis使用的Ognl表达式来获取test属性的值
2. 写代码验证
结果发现真的为false
所以原因就是Ognl表达式会把0和空字符串比较为相等
三、解决办法
将空串比较条件去掉,也就是将subsidySum != ''
这个条件去掉
代码及执行结果如下
总结
- 本文简单讲述了MyBatis动态SQL的if条件不生效的情况,以及为什么MyBatis动态SQL会把0和空串比较为相等true的原因,这算是MyBatis的一个坑了。
- 欢迎大家提出建议以及批评,有任何问题可以私信。