在Java中除掉String类型对象之后剩下的对象类型大致可以归类为以下三类:
- 基本数字类型(int、long、float、double等)
- 布尔类型(boolean)
- 其他类型(包含基本类型的包装类、自定义类型)
对于第一类基本数字类型,在Mybatis的if标签中可以使用大于、小于、等于等运算符进行条件判断。例如:
<!-- 示例:根据 age 参数生成动态 SQL -->
<select id="getUserByAge" parameterType="int" resultType="com.example.User">
SELECT * FROM user
WHERE
<if test="age != null and age gt 0">
age = #{age}
</if>
</select>
注:<if>
标签中的表达式是基于 OGNL 的,而 OGNL 中的比较运算符是 lt
表示小于,gt
表示大于。因此,在 <if>
标签中使用 OGNL 表达式时,应该使用 lt
和 gt
表示小于和大于。而且在判断为空的时候直接判断不等于null即可。
对于第二类布尔类型,可以直接使用true、false进行条件判断,例如:
<!-- 示例:根据 isVip 参数生成动态 SQL -->
<select id="getVipUsers" parameterType="boolean" resultType="com.example.User">
SELECT * FROM user
WHERE
<if test="isVip">
is_vip = 1
</if>
</select>
注:上述test的ognl表达式也可以写成isVip==true来判断
对于第三类其他类型,则可以通过Java对象的属性或者方法进行条件判断,例如:
<!-- 示例:根据 User 对象的 gender 属性生成动态 SQL -->
<select id="getUserByGender" parameterType="com.example.User" resultType="com.example.User">
SELECT * FROM user
WHERE
<if test="gender != null and gender.equals('male')">
gender = 'male'
</if>
</select>
注:ognl表达式可以将表达式中的java对象方法解析出来,并且加以使用。比如:集合对象、Hash对象等复杂对象。对于对象中非静态方法可以直接使用比如:collection.size()。如果是静态方法、静态属性、自定义工具类方法,则需要进行@全路径类名@属性/方法名进行调用
===========================礼貌的分割线====================================
上面都是具体的用法,下面则涉及到一些Mybatis如何解析OGNL(Object-Graph Navigation Language)表达式的规则。
既然是探索if节点的处理逻辑,直接翻到
下面的IfSqlNode代码文件:
/*
* Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.scripting.xmltags;
/**
* @author Clinton Begin
*/
/**
* if SQL节点
*
*/
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();
}
@Override
public boolean apply(DynamicContext context) {
//如果满足条件,则apply,并返回true
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
}
}
可以发现实例化if节点之后可以通过调用apply方法来进行节点处理。此方法在每个节点代码文件中都有,而if节点通过调用evaluator.evaluateBoolean来实现节点中表达式的判断。
- 我们查看一下evaluator.evaluateBoolean是怎么去进行判断的
//表达式求布尔值,比如username == 'cbegin'
public boolean evaluateBoolean(String expression, Object parameterObject) {
//非常简单,就是调用ognl
Object value = OgnlCache.getValue(expression, parameterObject);
if (value instanceof Boolean) {
//如果是Boolean
return (Boolean) value;
}
if (value instanceof Number) {
//如果是Number,判断不为0
return !new BigDecimal(String.valueOf(value)).equals(BigDecimal.ZERO);
}
//否则判断不为null
return value != null;
}
从代码能够知道是通过OgnlCache.getValue来判断返回值的类型来确定boolean结果
- OgnlCache.getValue逻辑则是调用Ognl表达式来解析节点表达式,Cache则是为了避免Ognl的性能问题
// 缓存
private static final Map<String, Object> expressionCache = new ConcurrentHashMap<String, Object>();
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 e) {
throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e);
}
}
private static Object parseExpression(String expression) throws OgnlException {
Object node = expressionCache.get(expression);
if (node == null) {
//大致意思就是OgnlParser.topLevelExpression很慢,所以加个缓存,放到ConcurrentHashMap里面
node = Ognl.parseExpression(expression);
expressionCache.put(expression, node);
}
return node;
}
- Ognl Jar包源码,最终通过不断迭代node节点来获取表达式的确定值
// 创建默认上下文
public static Map createDefaultContext(Object root, ClassResolver classResolver) {
return addDefaultContext(root, null, classResolver, null, new OgnlContext(classResolver, null, null));
}
// 增加默认上下文
public static Map addDefaultContext(Object root, MemberAccess memberAccess, ClassResolver classResolver, TypeConverter converter, Map context) {
OgnlContext result;
if (context instanceof OgnlContext) {
result = (OgnlContext)context;
} else {
result = new OgnlContext(memberAccess, classResolver, converter, context);
}
result.setRoot(root);
return result;
}
// 获取Ognl值
public static Object getValue(Object tree, Map context, Object root) throws OgnlException {
return getValue(tree, context, root, (Class)null);
}
// 实际逻辑
public static Object getValue(Object tree, Map context, Object root, Class resultType) throws OgnlException {
Object result;
OgnlContext ognlContext = (OgnlContext)addDefaultContext(root, context);
Node node = (Node)tree;
if (node.getAccessor() != null) {
result = node.getAccessor().get(ognlContext, root);
} else {
result = node.getValue(ognlContext, root);
}
if (resultType != null)
result = getTypeConverter(context).convertValue(context, root, null, null, result, resultType);
return result;
}
从上述逻辑链大概能抽象出一条mybatis如何通过Ongl表达式来进行if标签表达式的判断逻辑