Mybatis if标签非String类型参数校验逻辑

话不多说,先上用法~

在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 表达式时,应该使用 ltgt 表示小于和大于。而且在判断为空的时候直接判断不等于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)表达式的规则。

首先小伙伴们可以通过这个网址去看一下带有中文注释的MyBatis源代码:GitHub - tuguangquan/mybatis: mybatis源码中文注释

 既然是探索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标签表达式的判断逻辑

猜你喜欢

转载自blog.csdn.net/weixin_42505381/article/details/130029769