在mybatis中的动态sql中存在很多表达式,如if标签中常见的(username != null && username != ‘’)或者 #{id},为了解析这类标签,mybatis使用了OGNL技术,OGNL是 Object-Graph Navigation Language 的缩写,对象-图形导航语言,语法为:#{ }
(1) 表达式(Expression)
表达式是整个OGNL的核心,所有的OGNL操作都是针对表达式的解析结果进行处理的。表达式规定了此次OGNL操作到底要干什么。因此,表达式其实是一个带有语法含义的字符串,这个字符串将规定操作的类型和操作的内容。
(2) Root对象(Root Object)
OGNL的Root对象可以理解为OGNL的操作对象。当OGNL表达式规定了“干什么”以后,我们还需要指定对谁干。OGNL的Root对象实际上是一个Java对象,是所有OGNL操作的实际载体。这就意味着,如果我们有一个OGNL的表达式,那么我们实际上需要针对Root对象去进行OGNL表达式的计算并返回结果。
(3) 上下文环境(Context)
有了表达式和Root对象,我们已经可以使用OGNL的基本功能。例如,根据表达式针对OGNL中的Root对象进行“取值”或者“写值”操作。
不过,事实上,在OGNL的内部,所有的操作都会在一个特定的数据环境中运行,这个数据环境就是OGNL的上下文环境(Context)。说得再明白一些,就是这个上下文环境(Context)将规定OGNL的操作在哪里干。
使用OGNL需要如下的依赖,但事实上我们并不需要显示引入该依赖,因为mybatis已经通过依赖传递的方式将其引入:
<dependency>
<groupId>ognlgroupId>
<artifactId>ognlartifactId>
<version>3.3.3version>
dependency>
(1) 对Root对象(Root Object)的访问
针对OGNL的Root对象的对象树的访问是通过使用“点号”将对象的引用串联起来实现的。通过这种方式,OGNL实际上将一个树形的对象结构转化成了一个链式结构的字符串结构来表达语义。
@Test
public void testOGNL() throws OgnlException {
// 定义一个参数
Account account = new Account(1, "tom", 2000);
// 解析表达式
Object username = Ognl.getValue("username", account);
log.info("username: {}", username); // username: tom
// 定义一个参数
Employee employee = new Employee(1, "tom", new Dept(1, "技术部门"));
// 解析表达式,这种表达式在标签中的test中使用经常使用
Object deptName = Ognl.getValue("dept.name != null && dept.name != ''", employee);
log.info("deptName: {}", deptName); // deptName: true
}
(2) 对上下文环境(Context)的访问
由于OGNL的上下文是一个Map结构,在OGNL进行计算时可以事先在上下文环境中设置一些参数,并让OGNL将这些参数带入进行计算。有时候也需要对这些上下文环境中的参数进行访问,访问这些参数时,需要通过#符号加上链式表达式来进行,从而表示与访问Root对象(Root Object)的区别。
@Test
public void testOgnlContext() throws OgnlException {
// 定义一个map上下文
Account account = new Account(1, "tom", 2000);
Account account2 = new Account(2, "jerry", 999);
Map<String,Object> map = new HashMap<>(4);
map.put("account1",account);
map.put("account2",account2);
Object username = Ognl.getValue("#account2.username",map,new Object() );
System.out.println(username);
}
(3) 对静态变量的访问
在OGNL中,对于静态变量或者静态方法的访问,需要通过@[class]@[field|method]的表达式语法来进行访问:
@Test
public void testOgnlStatic() throws OgnlException {
Object enable = Ognl.getValue("@com.dcy.util.DBUtilBindThread@threadLocal",new Object() );
System.out.println(enable);
}
(4) 方法调用
在OGNL中调用方法,可以直接通过类似Java的方法调用方式进行,也就是通过点号加方法名称完成方法调用,甚至可以传递参数。
@Test
public void testOGNLInvoke() throws Exception {
// 定义一个map上下文
Account account = new Account();
account.setUsername("Maloney");
Map<String,Object> map = new HashMap<>(4);
map.put("account",account);
// 调用string的substring()方法
Object username = Ognl.getValue("#account.username.substring(1,5)",map,new Object() );
System.out.println(username); // alon
}