太长不看系列:在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