太长不看系列:在mybatis的动态SQL中""会被解析成0;
在mybtais中我们一般会用动态SQL处理自己的语句,也就是可以通过if语句判断是否成立,然后在进行赋值操作,
比如:
<if test="item.useNumDecimal != null and item.useNumDecimal != '' " > use_num_decimal = #{item.useNumDecimal}, </if>
这里的意思就是判断useNumDecimal不是null并且也不等于 '' ",然后就把useNumDecimal的值赋值给use_num_decimal ;
如果useNumDecimal 是JAVA中的BigDecimal类型,这里的判断在其值为0的情况下就会造成误判,此处的 item.useNumDecimal 为0,”“也在mybatis的语法分析中也是0,这就导致0==0 而0 != 0的条件不成立,所以不会赋值;
那么'' "为什么会转换成0呢,
就是下面的源码导致的。注意doubleValue方法中的 return s.length() == 0 ? 0.0D : Double.parseDouble(s);
public static String stringValue(Object value, boolean trim) { String result; if (value == null) { result = OgnlRuntime.NULL_STRING; } else { result = value.toString(); if (trim) { result = result.trim(); } } return result; } 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); } } }
分析:
因为”“是String类型的且s.length() == 0成立,所以会转换成0.0D 也就是Doule类型的0,这也就是问题的根源;
处理方法只需要把这个and后面的判断去掉就好了也就是说把 and item.useNumDecimal != '' "去掉;
下面是Mybatis解析的一些思路及源码;
思路:
大致思路就是生成一个解析树,然后解析里面的值,看看条件是否成立,如果成立就把它生成到SQL语句中;
========================================================================
源码地址:
解析的入口在这里:
MappedStatement public BoundSql getBoundSql(Object parameterObject) { //获取SQL BoundSql boundSql = sqlSource.getBoundSql(parameterObject); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings == null || parameterMappings.isEmpty()) { boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject); } // check for nested result maps in parameter mappings (issue #30) for (ParameterMapping pm : boundSql.getParameterMappings()) { String rmId = pm.getResultMapId(); if (rmId != null) { ResultMap rm = configuration.getResultMap(rmId); if (rm != null) { hasNestedResultMaps |= rm.hasNestedResultMaps(); } } } return boundSql; }
DynamicSqlSource public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); rootSqlNode.apply(context); 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); for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) { boundSql.setAdditionalParameter(entry.getKey(), entry.getValue()); } return boundSql; }
MixedSqlNode public boolean apply(DynamicContext context) { for (SqlNode sqlNode : contents) { sqlNode.apply(context); } return true; }
StaticTextSqlNode @Override public boolean apply(DynamicContext context) { context.appendSql(text); return true; } DynamicContext //如果返回的值为true,则把SQL加入到sqlBuilder 中 private final StringBuilder sqlBuilder = new StringBuilder(); public void appendSql(String sql) { sqlBuilder.append(sql); sqlBuilder.append(" "); } SimpleNode //获取值 public final Object getValue(OgnlContext context, Object source) throws OgnlException { Object result = null; if (context.getTraceEvaluations()) { EvaluationPool pool = OgnlRuntime.getEvaluationPool(); Throwable evalException = null; Evaluation evaluation = pool.create(this, source); context.pushEvaluation(evaluation); boolean var13 = false; try { var13 = true; result = this.evaluateGetValueBody(context, source); var13 = false; } catch (OgnlException var14) { evalException = var14; throw var14; } catch (RuntimeException var15) { evalException = var15; throw var15; } finally { if (var13) { Evaluation eval = context.popEvaluation(); eval.setResult(result); if (evalException != null) { eval.setException((Throwable)evalException); } if (evalException == null && context.getRootEvaluation() == null && !context.getKeepLastEvaluation()) { pool.recycleAll(eval); } } } Evaluation eval = context.popEvaluation(); eval.setResult(result); if (evalException != null) { eval.setException((Throwable)evalException); } if (evalException == null && context.getRootEvaluation() == null && !context.getKeepLastEvaluation()) { pool.recycleAll(eval); } } else { result = this.evaluateGetValueBody(context, source); } return result; } protected Object evaluateGetValueBody(OgnlContext context, Object source) throws OgnlException { context.setCurrentObject(source); context.setCurrentNode(this); if (!this._constantValueCalculated) { this._constantValueCalculated = true; //判断是否为常量 boolean constant = this.isConstant(context); if (constant) { this._constantValue = this.getValueBody(context, source); } this._hasConstantValue = constant; } //不是常量就调用getValueBody方法 return this._hasConstantValue ? this._constantValue : this.getValueBody(context, source); } ExpressionNode //判断常量的方法 public boolean isConstant(OgnlContext context) throws OgnlException { boolean result = this.isNodeConstant(context); if (this._children != null && this._children.length > 0) { result = true; for(int i = 0; result && i < this._children.length; ++i) { if (this._children[i] instanceof SimpleNode) { result = ((SimpleNode)this._children[i]).isConstant(context); } else { result = false; } } } return result; } ASTAnd //获取getValueBody的方法 protected Object getValueBody(OgnlContext context, Object source) throws OgnlException { Object result = null; int last = this._children.length - 1; for(int i = 0; i <= last; ++i) { result = this._children[i].getValue(context, source); if (i != last && !OgnlOps.booleanValue(result)) { break; } } return result; } OgnlRuntime //如果不是常量最终会通过method.invoke获得其值 public static Object invokeMethod(Object target, Method method, Object[] argsArray) throws InvocationTargetException, IllegalAccessException { boolean syncInvoke = false; boolean checkPermission = false; synchronized(method) { if (_methodAccessCache.get(method) == null) { if (Modifier.isPublic(method.getModifiers()) && Modifier.isPublic(method.getDeclaringClass().getModifiers())) { _methodAccessCache.put(method, Boolean.FALSE); } else if (!method.isAccessible()) { _methodAccessCache.put(method, Boolean.TRUE); } else { _methodAccessCache.put(method, Boolean.FALSE); } } if (_methodAccessCache.get(method) == Boolean.TRUE) { syncInvoke = true; } if (_methodPermCache.get(method) == null) { if (_securityManager != null) { try { _securityManager.checkPermission(getPermission(method)); _methodPermCache.put(method, Boolean.TRUE); } catch (SecurityException var12) { _methodPermCache.put(method, Boolean.FALSE); throw new IllegalAccessException("Method [" + method + "] cannot be accessed."); } } else { _methodPermCache.put(method, Boolean.TRUE); } } if (_methodPermCache.get(method) == Boolean.FALSE) { checkPermission = true; } } Object result; if (syncInvoke) { synchronized(method) { if (checkPermission) { try { _securityManager.checkPermission(getPermission(method)); } catch (SecurityException var10) { throw new IllegalAccessException("Method [" + method + "] cannot be accessed."); } } method.setAccessible(true); //在这里可以通过反射得到对应的值 这个案例中得到的值就是0 result = method.invoke(target, argsArray); method.setAccessible(false); } } else { if (checkPermission) { try { _securityManager.checkPermission(getPermission(method)); } catch (SecurityException var9) { throw new IllegalAccessException("Method [" + method + "] cannot be accessed."); } } result = method.invoke(target, argsArray); } return result; } ASTNotEq protected Object getValueBody(OgnlContext context, Object source) throws OgnlException { Object v1 = this._children[0].getValue(context, source);//0 Object v2 = this._children[1].getValue(context, source);//因为是常量所以值为"" //因为是and条件,所以判断两个值是否相等 return OgnlOps.equal(v1, v2) ? Boolean.FALSE : Boolean.TRUE; } OgnlOps //判断是否相等的方法 public static boolean equal(Object v1, Object v2) { if (v1 == null) { return v2 == null; } else if (v1 != v2 && !isEqual(v1, v2)) { if (v1 instanceof Number && v2 instanceof Number) { return ((Number)v1).doubleValue() == ((Number)v2).doubleValue(); } else { return false; } } else { return true; } } public static boolean isEqual(Object object1, Object object2) { boolean result = false; if (object1 == object2) { result = true; } else if (object1 != null && object1.getClass().isArray()) { if (object2 != null && object2.getClass().isArray() && object2.getClass() == object1.getClass()) { result = Array.getLength(object1) == Array.getLength(object2); if (result) { int i = 0; for(int icount = Array.getLength(object1); result && i < icount; ++i) { result = isEqual(Array.get(object1, i), Array.get(object2, i)); } } } } else { result = object1 != null && object2 != null && (object1.equals(object2) || compareWithConversion(object1, object2) == 0); } return result; } //最终来到了这里 public static int compareWithConversion(Object v1, Object v2) { int result; if (v1 == v2) { result = 0; } else { int t1 = getNumericType(v1); int t2 = getNumericType(v2); int type = getNumericType(t1, t2, true); switch(type) { case 6: result = bigIntValue(v1).compareTo(bigIntValue(v2)); break; case 9: result = bigDecValue(v1).compareTo(bigDecValue(v2)); break; case 10: if (t1 == 10 && t2 == 10) { if (v1 instanceof Comparable && v1.getClass().isAssignableFrom(v2.getClass())) { result = ((Comparable)v1).compareTo(v2); break; } throw new IllegalArgumentException("invalid comparison: " + v1.getClass().getName() + " and " + v2.getClass().getName()); } case 7: case 8: double dv1 = doubleValue(v1); double dv2 = doubleValue(v2); return dv1 == dv2 ? 0 : (dv1 < dv2 ? -1 : 1); default: long lv1 = longValue(v1); long lv2 = longValue(v2); return lv1 == lv2 ? 0 : (lv1 < lv2 ? -1 : 1); } } return result; } public static int getNumericType(Object value) { if (value != null) { Class c = value.getClass(); if (c == Integer.class) { return 4; } if (c == Double.class) { return 8; } if (c == Boolean.class) { return 0; } if (c == Byte.class) { return 1; } if (c == Character.class) { return 2; } if (c == Short.class) { return 3; } if (c == Long.class) { return 5; } if (c == Float.class) { return 7; } if (c == BigInteger.class) { return 6; } if (c == BigDecimal.class) { return 9; } } return 10; } 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); } } } 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); //在这里""->0 return s.length() == 0 ? 0.0D : Double.parseDouble(s); } } }
如图 最终执行得到的结果是false;所以并不会进入赋值语句;
image.png