我以前不知道Mybatis可以使用OGNL的语法

635 阅读1分钟

这是我参与更文挑战的第12天

用于解析静态方法

org.apache.ibatis.scripting.xmltags.TextSqlNode.BindingTokenParser#handleToken
org.apache.ibatis.scripting.xmltags.OgnlCache#getValue
org.apache.ibatis.scripting.xmltags.OgnlCache#parseExpression解析表达式
org.apache.ibatis.ognl.Ognl#parseExpression
org.apache.ibatis.ognl.OgnlParser#staticReference
org.apache.ibatis.ognl.OgnlParser#staticMethodCall
org.apache.ibatis.ognl.OgnlRuntime#callStaticMethod

在SQL映射语句中可以支持引入以下几种方式:

<select id="getUserById" resultMap="BaseResultMap">
       select * from user
       <if test="id != null">
           <where>
                 name = #{name}
                 and id =${id}
                 and id = ${user.id}
                 and id = ${@@abs(-12345678)}
                 and id = ${@@parseInt("654")}
                 and id='${@cn.followtry.mybatis.bean.User@name()}'
                 and id='${new cn.followtry.mybatis.bean.User()}'
                 and id=${@cn.followtry.mybatis.bean.User@haha}
                 and id='${@cn.followtry.mybatis.bean.User@arr[1]}'
                 and id='${@cn.followtry.mybatis.bean.User@list[1]}'
                 and id='${@cn.followtry.mybatis.bean.User@map.get("123")}'
                 and id='${@cn.followtry.mybatis.bean.CodeTypeEnum@THREE.ordinal()}'
             </where>
         </if>
       limit 100
</select>
  • 变量:id =${id}

  • 属性:id = ${user.id}

  • 静态方法(public):id='${@cn.followtry.mybatis.bean.User@name()}'

  • 静态属性(public):id=${@cn.followtry.mybatis.bean.User@aaa}

  • 数组索引:id='${@cn.followtry.mybatis.bean.User@arr[1]}'

  • 集合:'${@cn.followtry.mybatis.bean.User@list[1]}'

  • Map:

    • id='${@cn.followtry.mybatis.bean.User@map.get("123")}'
    • id='${@cn.followtry.mybatis.bean.User@map}'
    • Enum:id=${@cn.followtry.mybatis.bean.CodeTypeEnum@THREE.ordinal()}
  • 构造方法:id='${new cn.followtry.mybatis.bean.User()}'

  • java.lang.Math方法:id = ${@@abs(-12345678)} 可以省略class的编写,方法的默认class是java.lang.Math

${}语法中通过两个@字符,前者定位到Java类,后者定位到类中的方法或属性,这里只列出的其中一部分,对于Mybatis支持的${}语法,可以参见OGNL语法手册。


  • <bind>参数的调用可以通过#{}${} 方式获取,#{}可以防止注入:<bind>value值会使用OGNL计算
<bind name="username_bind" value='@java.util.UUID@randomUUID().toString().replace("-", "")' />
  • 使用OGNL实现单表的分表功能

分表这个功能是通用Mapper中的新功能,允许在运行的时候指定一个表名,通过指定的表名对表进行操作。这个功能实现就是使用了OGNL。

首先并不是所有的表都需要该功能,因此定义了一个接口,当参数(接口方法只有实体类一个参数)对象继承该接口的时候,就允许使用动态表名。

public interface IDynamicTableName {
    /**
     * 获取动态表名 - 只要有返回值,不是null和'',就会用返回值作为表名
     * @return
     */
    String getDynamicTableName();
}

然后在XML中写表名的时候使用:

<if test="@tk.mybatis.mapper.util.OGNL@isDynamicParameter(_parameter) 
            and dynamicTableName != null 
            and dynamicTableName != ''">
    ${dynamicTableName}
</if>
<if test="@tk.mybatis.mapper.util.OGNL@isNotDynamicParameter(_parameter) 
            or dynamicTableName == null 
            or dynamicTableName == ''">
    defaultTableName
</if>

由于我需要判断_parameter是否继承了IDynamicTableName接口,简单的写法已经无法实现,所以使用了静态方法,这两个方法如下:

/**
 * 判断参数是否支持动态表名
 *
 * @param parameter
 * @return true支持,false不支持
 */
public static boolean isDynamicParameter(Object parameter) {
    if (parameter != null && parameter instanceof IDynamicTableName) {
        return true;
    }
    return false;
}

/**
 * 判断参数是否b支持动态表名
 *
 * @param parameter
 * @return true不支持,false支持
 */
public static boolean isNotDynamicParameter(Object parameter) {
    return !isDynamicParameter(parameter);
}

根据<if>判断的结果来选择使用那个表名。另外注意XML判断中有一个dynamicTableName,这个参数是根据getDynamicTableName方法得到的,MyBatis使用属性对应的getter方法来获取值,不是根据field来获取值。