JAVA-MyBatis 当传入参数为Integer的0时,动态sql解析为空
之前开发时遇到前台传来参数为0 时,使用mybatis拼接动态sql时将0解析为空从而跳过if判断参数是否为空,无法拼接限制条件的问题,现在深入研究问题的原因
-
现象:
当mybatis的xml文件中使用动态sql的if判段,如下:
<if test="warningId !=null and warningId !=''"> and l.warning_id = #{warningId} </if>
warningId为Integer的0时,if判断会为false
-
表面原因:
mybatis会将0解析为 '' 空 warningId!='' 为false跳过拼接条件
-
解决方法:
前台处理:将传入参数改为字符串的0
后台处理:判断条件加上等于0的时候,如
<if test="warningId !=null and warningId !='' or warningId == 0 " > and l.warning_id = #{warningId} </if>
-
原理及源码:
查询资料以及查看mybatis源码,搞清楚mybatis解析xml文件时对if元素的处理
-
当mybatis遍历节点时,遇到节点类型为元素,会使用NodeHandler处理各个类型的元素,NodeHandler是XMLScriptBuilder的一个内部接口,其实现类包括TrimHandler、WhereHandler、SetHandler、IfHandler、ChooseHandler,处理后,会生成IfSqlNode
private class IfHandler implements NodeHandler { public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { List<SqlNode> contents = parseDynamicTags(nodeToHandle); MixedSqlNode mixedSqlNode = new MixedSqlNode(contents); String test = nodeToHandle.getStringAttribute("test"); IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test); targetContents.add(ifSqlNode); } }
-
当处理完所有节点后,开始构造sql
@Override public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); rootSqlNode.apply(context);//此处调用sqlnode的apply方法 SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); context.getBindings().forEach(boundSql::setAdditionalParameter); return boundSql; }
rootSqlNode.apply(context);该处调用sql的apply方法,然后查看IfSqlNode的结构:
public class IfSqlNode implements SqlNode { private final ExpressionEvaluator evaluator; private final String test; private final SqlNode contents; public IfSqlNode(SqlNode contents, String test) { this.test = test; this.contents = contents; this.evaluator = new ExpressionEvaluator(); } @Override public boolean apply(DynamicContext context) { if (evaluator.evaluateBoolean(test, context.getBindings())) { contents.apply(context); return true; } return false; } }
其中appl方法中使用evaluator.evaluateBoolean方法对表达式进行判断,evaluator是一个ExpressionEvaluator 类型,它解析表达式使用的是OGNL,对象导航图语言(Object Graph Navigation Language),简称OGNL,是应用于Java中的一个开源的表达式语言.
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)).equals(BigDecimal.ZERO); return value != null; }
查询OGNL的官方文档,
Interpreting Objects as Booleans
Any object can be used where a boolean is required. OGNL interprets objects as booleans like this:
-
If the object is a
Boolean
, its value is extracted and returned; -
If the object is a
Number
, its double-precision floating-point value is compared with zero; non-zero is treated astrue
, zero asfalse
; -
If the object is a
Character
, its boolean value istrue
if and only if its char value is non-zero; -
Otherwise, its boolean value is
true
if and only if it is non-null
.其中说明,如果对象是一个Number的类型,值为0时将被解析为false,否则为true,即'' == 0 == false
-
-
网友评论