<if test="backCarType != null and backCarType != ''">
and m.back_car = #{backCarType,jdbcType=BIT}
if>
- MappedStatement.getBoundSql(Object parameterObject)
public BoundSql getBoundSql(Object parameterObject) {
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.getBoundSql(Object parameterObject)
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);
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
重点关注这个rootSqlNode,因为它就是解析我们写在mapper文件中的各种标签,比如if、choose、foreach等,接下来看一下是怎么解析if标签的:
public class IfSqlNode implements SqlNode {
private final ExpressionEvaluator evaluator;
private final String test;
private final SqlNode contents;
public IfSqlNode(SqlNode contents, String test) {
this.test = test;
this.contents = contents;
this.evaluator = new ExpressionEvaluator();
}
@Override
public boolean apply(DynamicContext context) {
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
}
}
- ExpressionEvaluator.evaluateBoolean(String expression, Object parameterObject)
public boolean evaluateBoolean(String expression, Object parameterObject) {
Object value = OgnlCache.getValue(expression, parameterObject);
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof Number) {
return new BigDecimal(String.valueOf(value)).compareTo(BigDecimal.ZERO) != 0;
}
return value != null;
}
上面代码中出现了一个很重要的东西,Ognl,现在来关注一下Ognl怎么处理and的:
- ASTAnd.getValueBody( OgnlContext context, Object source )
protected Object getValueBody( OgnlContext context, Object source )
throws OgnlException
{
Object result = null;
int last = _children.length - 1;
for ( int i=0; i <= last; ++i )
{
result = _children[i].getValue( context, source );
if ( i != last && ! OgnlOps.booleanValue(result) )
break;
}
return result;
}
解析如下:
如之前示例的if标签,它会解析成两个_children节点:
1.backCarType != null
2.backCarType != ‘’
如果判断结束或者有一个为false的话会结束循环,然后将结果返回.
接下来看一下Ognl是怎么判断这两个_children节点的:
- ASTNotEq.getValueBody( OgnlContext context, Object source )
protected Object getValueBody( OgnlContext context, Object source ) throws OgnlException
{
Object v1 = _children[0].getValue( context, source );
Object v2 = _children[1].getValue( context, source );
return OgnlOps.equal( v1, v2 ) ? Boolean.FALSE : Boolean.TRUE;
}
解释一下,这里的v1就是参数,v2就是判断值,当我们参数是0的时候,如我们示例的if标签,第一个_children节点判断的时候,v1是0,v2是null,第二个_children节点判断的时候,v1是0,v2是",重点关注一下第二种我们认为异常的情况:
- OgnlOps.equal(Object v1, Object v2)
public static boolean equal(Object v1, Object v2)
{
if (v1 == null) return v2 == null;
if (v1 == v2 || isEqual(v1, v2)) return true;
if (v1 instanceof Number && v2 instanceof Number)
return ((Number) v1).doubleValue() == ((Number) v2).doubleValue();
return false;
}
首先v1并不为null,然后会调用isEqual(v1, v2)方法进行判断:
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) {
for(int i = 0, icount = Array.getLength(object1); result && (i < icount); i++) {
result = isEqual(Array.get(object1, i), Array.get(object2, i));
}
}
}
} else {
// Check for converted equivalence first, then equals() equivalence
result = (object1 != null) && (object2 != null)
&& (object1.equals(object2) || (compareWithConversion(object1, object2) == 0));
}
}
return result;
}
很显然,v1不等于v2,并且v1不为null也不是数组,因此决定权在这个表达式(compareWithConversion(object1, object2) == 0,观察一下这个方法:
以下这些方法到在OgnlOps类中:
public static int compareWithConversion(Object v1, Object v2)
{
int result;
if (v1 == v2) {
result = 0;
} else {
int t1 = getNumericType(v1), t2 = getNumericType(v2), type = getNumericType(t1, t2, true);
switch(type) {
case BIGINT:
result = bigIntValue(v1).compareTo(bigIntValue(v2));
break;
case BIGDEC:
result = bigDecValue(v1).compareTo(bigDecValue(v2));
break;
case NONNUMERIC:
if ((t1 == NONNUMERIC) && (t2 == NONNUMERIC)) {
if ((v1 instanceof Comparable) && v1.getClass().isAssignableFrom(v2.getClass())) {
result = ((Comparable) v1).compareTo(v2);
break;
} else {
throw new IllegalArgumentException("invalid comparison: " + v1.getClass().getName() + " and "
+ v2.getClass().getName());
}
}
// else fall through
case FLOAT:
case DOUBLE:
double dv1 = doubleValue(v1),
dv2 = doubleValue(v2);
return (dv1 == dv2) ? 0 : ((dv1 < dv2) ? -1 : 1);
default:
long lv1 = longValue(v1),
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 INT;
if (c == Double.class) return DOUBLE;
if (c == Boolean.class) return BOOL;
if (c == Byte.class) return BYTE;
if (c == Character.class) return CHAR;
if (c == Short.class) return SHORT;
if (c == Long.class) return LONG;
if (c == Float.class) return FLOAT;
if (c == BigInteger.class) return BIGINT;
if (c == BigDecimal.class) return BIGDEC;
}
return NONNUMERIC;
}
public static int getNumericType(int t1, int t2, boolean canBeNonNumeric)
{
if (t1 == t2) return t1;
if (canBeNonNumeric && (t1 == NONNUMERIC || t2 == NONNUMERIC || t1 == CHAR || t2 == CHAR)) return NONNUMERIC;
if (t1 == NONNUMERIC) t1 = DOUBLE; // Try to interpret strings as doubles...
if (t2 == NONNUMERIC) t2 = DOUBLE; // Try to interpret strings as doubles...
if (t1 >= MIN_REAL_TYPE) {
if (t2 >= MIN_REAL_TYPE) return Math.max(t1, t2);
if (t2 < INT) return t1;
if (t2 == BIGINT) return BIGDEC;
return Math.max(DOUBLE, t1);
} else if (t2 >= MIN_REAL_TYPE) {
if (t1 < INT) return t2;
if (t1 == BIGINT) return BIGDEC;
return Math.max(DOUBLE, t2);
} else return Math.max(t1, t2);
}
public static double doubleValue(Object value)
throws NumberFormatException
{
if (value == null) return 0.0;
Class c = value.getClass();
if (c.getSuperclass() == Number.class) return ((Number) value).doubleValue();
if (c == Boolean.class) return ((Boolean) value).booleanValue() ? 1 : 0;
if (c == Character.class) return ((Character) value).charValue();
String s = stringValue(value, true);
return (s.length() == 0) ? 0.0 : 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;
}
分析如下:
1.首先v1不等于v2,然后获取到v1的类型是BYTE,v2的类型是NONNUMERIC
2.然后将会以v2为主,接下来会进入switch语句
3.case到NONNUMERIC,然后发现t1是BYTE并不是NONNUMERIC,然后case到DOUBLE
4.在doubleValue方法中,首先判断类型,"“空字符串会判断s.length() == 0这时候显然返回true,然后”“字符串对等的double值就是0.0,
5.v1(0)的double值也是0.0,因此在Ognl中”"跟0是相等的
我们可以参照以下if标签的写法:
<if test="backCarType == 0 or (backCarType != null and backCarType != '')">
and m.back_car = #{backCarType,jdbcType=BIT}
if>