现象
在测试中发现,以上sql在mybatis解析后,sortord无论是"0"或者"1"单个字符串,都会倒序执行。
源码分析原因
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.apache.ibatis.scripting.xmltags;public class IfSqlNode implements SqlNode { private ExpressionEvaluator evaluator; private String test; private SqlNode contents; public IfSqlNode(SqlNode contents, String test) { this.test = test; this.contents = contents; this.evaluator = new ExpressionEvaluator(); } public boolean apply(DynamicContext context) { if (this.evaluator.evaluateBoolean(this.test, context.getBindings())) { this.contents.apply(context); return true; } else { return false; } }}
sql节点调用apply
方法中this.evaluator.evaluateBoolean
方法
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.apache.ibatis.scripting.xmltags;import java.lang.reflect.Array;import java.math.BigDecimal;import java.util.ArrayList;import java.util.List;import java.util.Map;import org.apache.ibatis.builder.BuilderException;public class ExpressionEvaluator { public ExpressionEvaluator() { } public boolean evaluateBoolean(String expression, Object parameterObject) { Object value = OgnlCache.getValue(expression, parameterObject); if (value instanceof Boolean) { return (Boolean)value; } else if (value instanceof Number) { return !(new BigDecimal(String.valueOf(value))).equals(BigDecimal.ZERO); } else { return value != null; } } public Iterable evaluateIterable(String expression, Object parameterObject) { Object value = OgnlCache.getValue(expression, parameterObject); if (value == null) { throw new BuilderException("The expression '" + expression + "' evaluated to a null value."); } else if (value instanceof Iterable) { return (Iterable)value; } else if (!value.getClass().isArray()) { if (value instanceof Map) { return ((Map)value).entrySet(); } else { throw new BuilderException("Error evaluating expression '" + expression + "'. Return value (" + value + ") was not iterable."); } } else { int size = Array.getLength(value); List
Object value = OgnlCache.getValue(expression, parameterObject);
行使用ognl方式获取test中表达式test='sortord!="0"'
的值
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.apache.ibatis.scripting.xmltags;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import org.apache.ibatis.builder.BuilderException;import org.apache.ibatis.ognl.Ognl;import org.apache.ibatis.ognl.OgnlException;public final class OgnlCache { private static final MapexpressionCache = new ConcurrentHashMap(); private OgnlCache() { } public static Object getValue(String expression, Object root) { try { Map
package org.apache.ibatis.ognl;import java.io.StringReader;import java.lang.reflect.Member;import java.util.Map;public abstract class Ognl { public static Object parseExpression(String expression) throws OgnlException { try { OgnlParser parser = new OgnlParser(new StringReader(expression)); return parser.topLevelExpression(); } catch (ParseException var2) { throw new ExpressionSyntaxException(expression, var2); } catch (TokenMgrError var3) { throw new ExpressionSyntaxException(expression, var3); } } public static Map createDefaultContext(Object root) { return addDefaultContext(root, (ClassResolver)null, (TypeConverter)null, (MemberAccess)null, new OgnlContext()); } public static Map createDefaultContext(Object root, ClassResolver classResolver) { return addDefaultContext(root, classResolver, (TypeConverter)null, (MemberAccess)null, new OgnlContext()); } public static Map createDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter) { return addDefaultContext(root, classResolver, converter, (MemberAccess)null, new OgnlContext()); } public static Map createDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess) { return addDefaultContext(root, classResolver, converter, memberAccess, new OgnlContext()); } public static Map addDefaultContext(Object root, Map context) { return addDefaultContext(root, (ClassResolver)null, (TypeConverter)null, (MemberAccess)null, context); } public static Map addDefaultContext(Object root, ClassResolver classResolver, Map context) { return addDefaultContext(root, classResolver, (TypeConverter)null, (MemberAccess)null, context); } public static Map addDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter, Map context) { return addDefaultContext(root, classResolver, converter, (MemberAccess)null, context); } public static Map addDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess, Map context) { OgnlContext result; if (!(context instanceof OgnlContext)) { result = new OgnlContext(); result.setValues(context); } else { result = (OgnlContext)context; } if (classResolver != null) { result.setClassResolver(classResolver); } if (converter != null) { result.setTypeConverter(converter); } if (memberAccess != null) { result.setMemberAccess(memberAccess); } result.setRoot(root); return result; } public static void setClassResolver(Map context, ClassResolver classResolver) { context.put("_classResolver", classResolver); } public static ClassResolver getClassResolver(Map context) { return (ClassResolver)context.get("_classResolver"); } public static void setTypeConverter(Map context, TypeConverter converter) { context.put("_typeConverter", converter); } public static TypeConverter getTypeConverter(Map context) { return (TypeConverter)context.get("_typeConverter"); } public static void setMemberAccess(Map context, MemberAccess memberAccess) { context.put("_memberAccess", memberAccess); } public static MemberAccess getMemberAccess(Map context) { return (MemberAccess)context.get("_memberAccess"); } public static void setRoot(Map context, Object root) { context.put("root", root); } public static Object getRoot(Map context) { return context.get("root"); } public static Evaluation getLastEvaluation(Map context) { return (Evaluation)context.get("_lastEvaluation"); } public static Object getValue(Object tree, Map context, Object root) throws OgnlException { return getValue((Object)tree, context, root, (Class)null); } public static Object getValue(Object tree, Map context, Object root, Class resultType) throws OgnlException { OgnlContext ognlContext = (OgnlContext)addDefaultContext(root, context); Object result = ((Node)tree).getValue(ognlContext, root); if (resultType != null) { result = getTypeConverter(context).convertValue(context, root, (Member)null, (String)null, result, resultType); } return result; } public static Object getValue(String expression, Map context, Object root) throws OgnlException { return getValue((String)expression, context, root, (Class)null); } public static Object getValue(String expression, Map context, Object root, Class resultType) throws OgnlException { return getValue(parseExpression(expression), context, root, resultType); } public static Object getValue(Object tree, Object root) throws OgnlException { return getValue((Object)tree, (Object)root, (Class)null); } public static Object getValue(Object tree, Object root, Class resultType) throws OgnlException { return getValue(tree, createDefaultContext(root), root, resultType); } public static Object getValue(String expression, Object root) throws OgnlException { return getValue((String)expression, (Object)root, (Class)null); } public static Object getValue(String expression, Object root, Class resultType) throws OgnlException { return getValue(parseExpression(expression), root, resultType); } public static void setValue(Object tree, Map context, Object root, Object value) throws OgnlException { OgnlContext ognlContext = (OgnlContext)addDefaultContext(root, context); Node n = (Node)tree; n.setValue(ognlContext, root, value); } public static void setValue(String expression, Map context, Object root, Object value) throws OgnlException { setValue(parseExpression(expression), context, root, value); } public static void setValue(Object tree, Object root, Object value) throws OgnlException { setValue(tree, createDefaultContext(root), root, value); } public static void setValue(String expression, Object root, Object value) throws OgnlException { setValue(parseExpression(expression), root, value); } public static boolean isConstant(Object tree, Map context) throws OgnlException { return ((SimpleNode)tree).isConstant((OgnlContext)addDefaultContext((Object)null, context)); } public static boolean isConstant(String expression, Map context) throws OgnlException { return isConstant(parseExpression(expression), context); } public static boolean isConstant(Object tree) throws OgnlException { return isConstant(tree, createDefaultContext((Object)null)); } public static boolean isConstant(String expression) throws OgnlException { return isConstant(parseExpression(expression), createDefaultContext((Object)null)); } public static boolean isSimpleProperty(Object tree, Map context) throws OgnlException { return ((SimpleNode)tree).isSimpleProperty((OgnlContext)addDefaultContext((Object)null, context)); } public static boolean isSimpleProperty(String expression, Map context) throws OgnlException { return isSimpleProperty(parseExpression(expression), context); } public static boolean isSimpleProperty(Object tree) throws OgnlException { return isSimpleProperty(tree, createDefaultContext((Object)null)); } public static boolean isSimpleProperty(String expression) throws OgnlException { return isSimpleProperty(parseExpression(expression), createDefaultContext((Object)null)); } public static boolean isSimpleNavigationChain(Object tree, Map context) throws OgnlException { return ((SimpleNode)tree).isSimpleNavigationChain((OgnlContext)addDefaultContext((Object)null, context)); } public static boolean isSimpleNavigationChain(String expression, Map context) throws OgnlException { return isSimpleNavigationChain(parseExpression(expression), context); } public static boolean isSimpleNavigationChain(Object tree) throws OgnlException { return isSimpleNavigationChain(tree, createDefaultContext((Object)null)); } public static boolean isSimpleNavigationChain(String expression) throws OgnlException { return isSimpleNavigationChain(parseExpression(expression), createDefaultContext((Object)null)); } private Ognl() { }}调用getValue方法获取表达式的值
package org.apache.ibatis.ognl;import java.lang.reflect.Member;import java.util.Map;public class DefaultTypeConverter implements TypeConverter { public DefaultTypeConverter() { } public Object convertValue(Map context, Object value, Class toType) { return OgnlOps.convertValue(value, toType); } public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType) { return this.convertValue(context, value, toType); }}
protected Object evaluateGetValueBody(OgnlContext context, Object source) throws OgnlException { context.setCurrentObject(source); context.setCurrentNode(this); if (!this.constantValueCalculated) { this.constantValueCalculated = true; this.hasConstantValue = this.isConstant(context); if (this.hasConstantValue) { this.constantValue = this.getValueBody(context, source); } } return this.hasConstantValue ? this.constantValue : this.getValueBody(context, source); }
以上getValueBody有多种AST开头的实现,如ASTList,ASTNotEq,ASTEq,ASTNotIn,ASTOr等,这里主要看ASTNotEq
package org.apache.ibatis.ognl;class ASTNotEq extends ExpressionNode { public ASTNotEq(int id) { super(id); } public ASTNotEq(OgnlParser p, int id) { super(p, id); } protected Object getValueBody(OgnlContext context, Object source) throws OgnlException { Object v1 = super.children[0].getValue(context, source); Object v2 = super.children[1].getValue(context, source); return OgnlOps.equal(v1, v2) ? Boolean.FALSE : Boolean.TRUE; } public String getExpressionOperator(int index) { return "!="; }}
当使用test="sorted !='0' "时,解析出结果如下 '0'被解析成Charactor字符类型
当使用test='sorted !="0"'时,'0'被解析成String字符串类型
当!=后面的值时''时,解析结果如下 即能解析出正确的结果
结论
变量与单个常量字符比较 需要使用test='sorted !="0"',变量与空字符常量比较,test="sorted !='0'"即可