一次Mybatis sql解析故障分析

现象

<select id="listOrderDetail" resultMap="OrderDTOResultMap">
        SELECT
        a.id,
        a.order_number,
        a.is_pay
        FROM (
        SELECT aa.*
        FROM ord_order aa
        WHERE aa.del_flag = '0'
        LIMIT #{pageStart}, #{pageSize}
        ) a
        LEFT JOIN ord_order_detail b ON a.id = b.ord_order_id
        <choose>
            <when test="sortord!='0'">
                ORDER BY a.create_date DESC
            </when>
            <otherwise>
                ORDER BY a.create_date ASC
            </otherwise>
        </choose>
    </select>

在测试中发现,以上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> answer = new ArrayList();

            for(int i = 0; i < size; ++i) {
                Object o = Array.get(value, i);
                answer.add(o);
            }

            return answer;
        }
    }
}

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 Map<String, Object> expressionCache = new ConcurrentHashMap();

    private OgnlCache() {
    }

    public static Object getValue(String expression, Object root) {
        try {
            Map<Object, OgnlClassResolver> context = Ognl.createDefaultContext(root, new OgnlClassResolver());
            return Ognl.getValue(parseExpression(expression), context, root);
        } catch (OgnlException var3) {
            throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + var3, var3);
        }
    }

    private static Object parseExpression(String expression) throws OgnlException {
        Object node = expressionCache.get(expression);
        if (node == null) {
            node = Ognl.parseExpression(expression);
            expressionCache.put(expression, node);
        }

        return node;
    }
}

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 AST实现

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'"即可

转载于:https://my.oschina.net/odetteisgorgeous/blog/3010296

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值