OGNL Object-Graph Navigation Language

55 篇文章 0 订阅
19 篇文章 0 订阅

简介

OGNL Object-Graph Navigation Language 的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者调用对象的方法,能够遍历整个对象的结构图,实现对象属性类型的转换等功能。

须知

1 OGNL表达式的计算是围绕OGNL上下文进行的

OGNL 上下文 实际上就是一个 Map对象 ,由 ognl.OgnlContext 类表示。它里面可以 存放很多个JavaBean对象 。它有一个上下文根对象。
上下文中的 根对象 可以 直接使用名来访问 或 直接使用它的属性名 访问它的属性值。否则要加前缀 #key

2 Struts2的标签库都是使用OGNL表达式来访问ActionContext中的对象数据的。

<s:propertyvalue="xxx"/>

3 Struts2ActionContext 设置为OGNL上下文 ,并将值栈作为OGNL的根对象放置到ActionContext中。

4 值栈(ValueStack)
可以在值栈中放入、删除、查询对象。访问值栈中的对象不用“#”
Struts2 总是把当前Action实例放置在栈顶 。所以在 OGNL 中引用Action中的属性也可以省略“#”

5 调用ActionContextput(key,value) 放入的数据,需要使用# 访问。

3个符号:#、%、$

#、% 和 $ 符号在OGNL表达式中经常出现

# 号

# 符号的用途一般有三种。

访问非根对象属性,例如 #session.msg 表达式,由于Struts 2中值栈 被视为根对象,所以访问其他非根对象时,需要加#前缀。
实际上,# 相当于 ActionContext. getContext()
#session.msg 表达式相当于 ActionContext.getContext().getSession(). getAttribute("msg")

用于过滤和投影(projecting)集合,如
persons.{?#this.age>25},persons.{?#this.name=='pla1'}.{age}[0]。

用来构造Map,例如示例中的 #{'foo1':'bar1', 'foo2':'bar2'}

#可以取出堆栈上下文中的存放的对象

在这里插入图片描述

%

%符号的用途是在标志的属性为字符串类型时,计算OGNL表达式的值,这个类似js中的eval,很暴力。

$

$ 符号主要有两个方面的用途。

国际化资源文件中,引用OGNL表达式,例如国际化资源文件中的代码:reg.agerange=国际化资源信息:年龄必须在 ${min} 同 ${max} 之间

总结OGNL的使用方法

OGNL直接支持java中的方法只支持JDK中的方法,不支持其他JAR包中的方法

访问属性

名字属性获取:<s:property value="user.username"/><br>

地址属性获取:<s:property value="user.address.addr"/><br>

访问方法

调用值栈中对象的普通方法:<s:property value="user.get()"/><br>

访问静态属性和方法

调用Action中的静态方法:<s:property value="@struts.action.LoginAction@get()"/>

调用JDK中的类的静态方法:<s:property value="@java.lang.Math@floor(44.56)"/><br>

调用JDK中的类的静态方法(同上)<s:property value="@floor(44.56)"/><br>

调用JDK中的类的静态方法:<s:property value="@java.util.Calendar@getInstance()"/><br>

调用普通类中的静态属性:<s:property value="@struts.vo.Address@TIPS"/><br>

访问构造方法

调用普通类的构造方法: <s:property value="new struts.vo.Student('李晓红' , '美女' , 3 , 25).username"/>

访问数组

获取List: <s:property value="testList"/><br>

获取 List 中的某一个元素(可以使用类似于数组中的下标获取List中的内容):
<s:property value="testList[0]"/><br>

获取 Set:<s:property value="testSet"/><br>

获取 Set 中的某一个元素(Set由于没有顺序,所以不能使用下标获取数据):
<s:property value="testSet[0]"/><br> 

获取 Map: <s:property value="testMap"/><br>

获取 Map 中所有的键: <s:property value="testMap.keys"/><br>

获取 Map 中所有的值: <s:property value="testMap.values"/><br>

获取 Map 中的某一个元素(可以使用类似于数组中的下标获取List中的内容):
<s:property value="testMap['m1']"/><br>

获取 List 的大小:<s:property value="testSet.size"/><br>

访问集合 – 投影、选择

? [0]  或者 ^       第一个
$                   最后一个
利用选择获取List中成绩及格的对象: <s:property value="stus.{?#this.grade>=60}"/><br>

利用选择获取List中成绩及格的对象的username:
<s:property value="stus.{?#this.grade>=60}.{username}"/><br>

利用选择获取List中成绩及格的第一个对象的username:
<s:property value="stus.{?#this.grade>=60}.{username}[0]"/><br>
<s:property value="stus.{^#this.grade>=60}.{username}"/><br>

利用选择获取List中成绩及格的最后一个对象的username:
<s:property value="stus.{$#this.grade>=60}.{username}"/><br>

利用选择获取List中成绩及格的第一个对象然后求大小:
<s:property value="stus.{^#this.grade>=600}.{username}.size"/><br>

集合的伪属性

OGNL能够引用集合的一些特殊的属性 , 这些属性并不是 JavaBeans 模式, 例如size(),length()等等. 当表达式引用这些属性时, OGNL会调用相应的方法, 这就是伪属性

在这里插入图片描述

Ognl 类方法源码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
 
import com.hotent.core.util.BeanUtils;
 
public class Ognl {
    public Ognl() {
    }
 
    public static boolean isEmpty(Object o) throws IllegalArgumentException {
        return BeanUtils.isEmpty(o);
    }
 
    public static boolean isNotEmpty(Object o) {
        return !isEmpty(o);
    }
 
    public static boolean isNotEmpty(Long o) {
        return !isEmpty(o);
    }
 
    public static boolean isNumber(Object o) {
        return BeanUtils.isNumber(o);
    }
}

mybaits 中

# 和 $

${} 为 在预编的时候会直接被变量替换,但是存在被注入的问题原样输出,你传什么,sql里就填入什么,比如有引号它也会原样填到sql里
#{} 在预编的时候会被解析为?,占位符会使用 PreparedStatement,变量处用 ? 代替,当被变量替换的时候会加上 ‘ ’ 单引号,表明不允许加单引号(但是反引号 `` 是可以的)

在能使用 #{} 尽量使用它吧,可以防止sql注入

动态 SQL 中 OGNL

在这里插入图片描述
在这里插入图片描述

MyBatis中用于实现动态SQL的元素主要有:

if
choose(when,otherwise)
trim
where
set
foreach
if

if 就是简单的条件判断,利用if语句我们可以实现某些简单的条件选择

<select id="dynamicIfTest" parameterType="Blog" resultType="Blog">  
    select * from t_blog where 11 = 1  
    <if test="title != null">  
        and title = #{title}  
    </if>  
    <if test="content != null">  
        and content = #{content}  
    </if>  
    <if test="owner != null">  
        and owner = #{owner}  
    </if>  
</select>  

在使用<if>时,注意在对”int”型对象判断时不仅要判断是否为“null”,还得判断是否为“0”

public class Content implements Serializable {
    private int id;
    private int commandId;
    private String content;
}
Content content = new Content();
content.setCommandId(Integer.parseInt(id));

//此时的Content对象是这样的,id被默认赋值为0了
Content content = new Content(0, commandId, null);


//此时若sql语句如下
<if test="id != null">
    AND id = #{id}
</if>
<if test="commandId != null">
    AND command_id = #{commandId}
</if>
//这两条sql语句都将被执行(而我们的本意是执行后面一条)

//解决办法,更改sql语句如下
<if test="id != null and id !=0">
    AND id = #{id}
</if>
<if test="commandId != null and commandId !=0">
    AND command_id = #{commandId}
</if>
OGNL直接支持java中的方法(只支持JDK中的方法,不支持其他JAR包中的方法)。

例: <if test="Command != null and !&quot;&quot;.equals(Command.trim())">and command=#{Command}</if>

给参数赋值:#{command}是由mybaits处理的,遇到#{},mybaits会自动替换为?
choose

choose 元素的作用就相当于JAVA中的switch语句,基本上跟JSTL中的choose的作用和用法是一样的,通常都是与when和otherwise搭配的

<select id="dynamicChooseTest" parameterType="Blog" resultType="Blog">  
    select * from t_blog where 11 = 1   
    <choose>  
        <when test="title != null">  
            and title = #{title}  
        </when>  
        <when test="content != null">  
            and content = #{content}  
        </when>  
        <otherwise>  
            and owner = "owner1"  
        </otherwise>  
    </choose>  
</select>  
当 when 中有条件满足的时候,就会跳出 choose
即所有的 when 和 otherwise 条件中,只有一个会输出,当所有的我很条件都不满足的时候就输出otherwise中的内容。

上面例子 当 title!=null 的时候就输出 and titlte = #{title},不再往下判断条件
当 title 为空且 content!=null 的时候就输出 and content = #{content}
当所有条件都不满足的时候就输出 otherwise 中的内容
where

where语句的作用主要是简化SQL语句中where中的条件判断的

<select id="dynamicWhereTest" parameterType="Blog" resultType="Blog">  
    select * from t_blog   
    <where>  
        <if test="title != null">  
            title = #{title}  
        </if>  
        <if test="content != null">  
            and content = #{content}  
        </if>  
        <if test="owner != null">  
            and owner = #{owner}  
        </if>  
    </where>  
</select>  
where元素的作用是会在写入where元素的地方输出一个where

另外一个好处是你不需要考虑 where 元素里面的条件输出是什么样子的,MyBatis会智能的帮你处理,如果所有的条件都不满足那么 MyBatis 就会查出所有的记录

如果输出后是 and 开头的,MyBatis会把第一个and忽略,当然如果是or开头的,MyBatis也会把它忽略
此外,在where元素中你不需要考虑空格的问题,MyBatis会智能的帮你加上。

上述例子中,如果 title=null, 而content != null,那么输出的整个语句会是 
select * from t_blog where content = #{content},而不是select * from t_blog where and content = #{content},因为MyBatis会智能的把首个and 或 or 给忽略。
trim

trim元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是 prefix 和 suffix ;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是 prefixOverrides 和 suffixOverrides ;我们也可以利用trim来代替where元素的功能

<select id="dynamicTrimTest" parameterType="Blog" resultType="Blog">  
    select * from t_blog   
    <trim prefix="where" prefixOverrides="and | or">  
        <if test="title != null">  
            title = #{title}  
        </if>  
        <if test="content != null">  
            and content = #{content}  
        </if>  
        <if test="owner != null">  
            or owner = #{owner}  
        </if>  
    </trim>  
</select>  
set

set 元素主要是用在更新操作的时候,它的主要功能和where元素其实是差不多的,主要是在包含的语句前输出一个set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果set包含的内容为空的话则会出错。有了set元素我们就可以动态的更新那些修改了的字段

<update id="dynamicSetTest" parameterType="Blog">  
    update t_blog  
    <set>  
        <if test="title != null">  
            title = #{title},  
        </if>  
        <if test="content != null">  
            content = #{content},  
        </if>  
        <if test="owner != null">  
            owner = #{owner}  
        </if>  
    </set>  
    where id = #{id}  
</update>  
上述代码中,如果set中一个条件都不满足,即set中包含的内容为空的时候就会报错
foreach

foreach 的主要用在构建 in 条件中,它可以在SQL语句中进行迭代一个集合。foreach 元素的属性主要有item,index,collection,open,separator,close

item 表示集合中 每一个元素进行迭代时的别名
index 指定一个名字,用于表示在迭代过程中每次迭代到的位置
open 表示该语句以什么开始
separator 表示在每次进行迭代之间以什么符号作为分隔符
close表示以什么结束
collection必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:

collection 只能取 list/array/_parameter 三个值

如果传入的是 单参数 且参数类型是一个 List 的时候,collection 属性值为 list
如果传入的是 单参数 且参数类型是一个 array数组 的时候,collection 的属性值为 array
如果传入的参数是 多个的时候,我们就需要把它们 封装成一个Map了 

当然单参数也可以 封装成map,实际上如果你在 传入参数的时候 ,在 MyBatis 里面也是会把它封装成一个 Map 的,map 的 key 就是参数名
所以这个时候 collection属性值 就是传入的 List 或 array对象 在自己封装的 map 里面的 key
<select id="dynamicForeachTest" resultType="Blog">  
    select * from t_blog where id in  
    <foreach collection="list" index="index" item="item" open="(" separator="," close=")">  
        #{item}  
    </foreach>  
</select>

对应mapper 
public List<Blog> dynamicForeachTest(List<Integer> ids); 
<select id="dynamicForeach2Test" resultType="Blog">  
    select * from t_blog where id in  
    <foreach collection="array" index="index" item="item" open="(" separator="," close=")">  
        #{item}  
    </foreach>  
</select> 

对应mapper
public List<Blog> dynamicForeach2Test(int[] ids);  
<select id="dynamicForeach3Test" resultType="Blog">  
    select * from t_blog where title like "%"#{title}"%" and id in  
    <foreach collection="ids" index="index" item="item" open="(" separator="," close=")">  
        #{item}  
    </foreach>  
</select>  

上述 collection 的值为 ids,是传入的参数 Map 的 key,对应的 Mapper 代码:
public List<Blog> dynamicForeach3Test(Map<String, Object> params);  

注意
foreach标签遍历的集合元素 类型是 Map.Entry类型时
index属性指定的变量代表对应的Map.Entry的key
item 属性指定的变量代表对应的Map.Entry的value
此时如果对应的集合是 Map.entrySet,则对应的 collection 属性用collection

foreach 在进行遍历的时候
如果 传入的参数是 List 类型 ,则其 collection 属性的值可以是 list 或collection
如果 传入的参数是 Set 类型,则 collection 属性的值 只能用 collection
<select id="dynamicForeachTest" resultType="Blog">  
    select * from t_blog where id in  
    <!-- 遍历的对象是Map.Entry时,index代表对应的key,item代表对应的value -->  
    <foreach collection="collection" index="key" item="value" open="(" separator="," close=")">  
        #{key}, #{value}  
    </foreach>  
</select> 

mapper 
public List<Blog> dynamicForeachTest(Set<Map.Entry<Integer, Integer>> ids);  
bind

功能是在当前OGNL上下文中创建一个变量并绑定一个值。有了它以后我们以前的模糊查询就可以改成这个样子

<select id="fuzzyQuery" resultType="Blog" parameterType="java.lang.String">    
    <!-- bind 标签用于创建新的变量 -->  
	<bind name="titleLike" value="'%'+_parameter+'%'"/>  
    select * from t_blog where title like #{titleLike}    
</select>  
<bind>  的value值会使用OGNL计算
bind 的参数调用 只能 用 $ 获取
对<bind参数的调用可以通过 #{} 或 ${} 方式获取,#{} 可以防止注入
通用Mapper中支持一种UUID的主键,在通用Mapper中的实现就是使用了<bind>标签,这个标签调用了一个静态方法,大概方法如下:

<bind name="username_bind" 
      value='@java.util.UUID@randomUUID().toString().replace("-", "")' />

下面 test 的值会使用 OGNL 计算结果

<select id="xxx" ...>
    select id,name,... from country
    <where>
        <if test="name != null and name != ''">
            name like concat('%', #{name}, '%')
        </if>
    </where>
</select>

${param} 参数中 OGNL

<select id="xxx" ...>
    select id,name,... from country
    <where>
        <if test="name != null and name != ''">
            name like '${'%' + name + '%'}'
        </if>
    </where>
</select>

这里注意写的是 ${'%' + name + '%'},而不是 %${name}% ,这两种方式的结果一样,但是处理过程不一样

在 MyBatis 中处理 ${} 的时候,只是使用OGNL计算这个结果值,然后替换SQL中对应的 ${xxx} OGNL 处理的只是 ${这里的表达式}

这里表达式可以是 OGNL支持的所有表达式,可以写的很复杂,可以调用静态方法返回值,也可以调用静态的属性值

<select id="getAll" parameterType="java.util.Map" resultMap="TaskEntity">
	SELECT task.*,run.subject subject,run.processName processName
	FROM ACT_RU_TASK task left join BPM_PRO_RUN run
	on task.PROC_INST_ID_=run.actInstId
	where 1=1
	<if test="@Ognl@isNotEmpty(name)"> AND task.name_ LIKE #{name} </if>
	<if test="@Ognl@isNotEmpty(subject)"> AND run.subject LIKE #{subject} </if>
	<if test="@Ognl@isNotEmpty(processName)"> AND run.processName LIKE #{processName} </if>
	<if test="@Ognl@isEmpty(orderField)">
		order by task.CREATE_TIME_ desc
	</if>
	<if test="@Ognl@isNotEmpty(orderField)">
		order by ${orderField} ${orderSeq}
	</if>		
</select>
<select id="getRecentQuestionTitle" parameterType="java.lang.String" resultType="java.lang.String">  
      select title from song_question where questionState = #{value}   
      <if test="@Ognl@isSolve(value[0],0)">  
      	order by questionTime desc   
      </if>  
      <if test="@Ognl@isSolve(value[0],1)">  
      	order by answerTime desc   
      </if>                
      limit 0,1  
</select>  

MyBatis常用OGNL表达式

官网

e1 or e2
e1 and e2
e1 == e2,e1 eq e2
e1 != e2,e1 neq e2
e1 lt e2:小于
e1 lte e2:小于等于,其他gt(大于),gte(大于等于)
e1 in e2
e1 not in e2
e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
!e,not e:非,求反
e.method(args)调用对象方法
e.property对象属性值
e1[ e2 ]按索引取值,List,数组和Map
@class@method(args)调用类的静态方法
@class@field调用类的静态字段值

工作中

使用枚举Enum判断

<if test="dtEnum == @com.xxx.xxx.TestTypeEnum@HOUR">
  DATE_FORMAT(TM,'%Y-%m-%d %H') as keyStr,
</if>

TestTypeEnum定义如下:

HOUR("hour"),
DAY("day"),
MONTH("month"),
YEAR("year");

where and 条件,多个条件 or

<if test="param.xxxTypes != null and param.xxxTypes.size() > 0 ">
 and (
 <foreach collection="param.xxxTypes" index="index" item="item" separator="or"> 
     <if test= "item.equals(@com.xxx.common.xxxTypeEnum@枚举值)">
         xxx is null
     </if>
     <if test= "!item.equals(@com.xxx.common.xxxTypeEnum@枚举值)">
         json_length(xxx, CONCAT('$.',#{item,typeHandler=com.xxx.core.dao.typehandler.xxxTypeEnumHandler})) != 0
     </if>
 </foreach>
 )

foreach 嵌套使用 if 标签对象取值问题

foreach获取到的 item 是一个 json 对象

<foreach collection="advanceSearchList" item="item" index="index" >
     <if test="item.searchType == 10 ">
         and abc like CONCAT('%', #{item.searchText}, '%')
     </if>
 </foreach>
<select id="getFieldsValue" parameterType="java.util.Map" resultType="java.util.HashMap">
    SELECT
    <foreach collection="colList" item="col" index="index" separator=",">
        <if test="optionList[index] != 'null'">
            ${col}.dic_value as ${col}
        </if>
        <if test="optionList[index] == 'null'">
            ${col}
        </if>
    </foreach>

    FROM
    ${tableName} t
    <foreach collection="optionList" item="option" index="index">
        <if test="option != 'null'">
            left join t_admin_dic_values ${colList[index]} ON t.${colList[index]}=${colList[index]}.id
        </if>
    </foreach>

    WHERE
    t.id IN
    <foreach collection="recordList" item="item" separator="," open="(" close=")">
        #{item}
    </foreach>
</select>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值