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