回顾一下,我们之前在 Mybatis 的 mapper.xml 映射文件里写 SQL 查询单个学生记录的时候是这样写的:
<select id="get" parameterType="java.lang.Long" resultType="Student">
select id, name, sex from t_student where id = #{id}
</select>
其中传进来的参数 #{id} 就是使用的 OGNL 表达式。
Mybatis 官方文档中「XML 映射文件」模块里边,有解析到:
说当我们使用 #{id} 类型参数符号的时候,其实就是告诉 Mybatis 创建一个预处理语句参数,通过 JDBC,这样的一个参数在 SQL 中会由一个 "?" 来标识,并传递到一个新的预处理语句中。
也就是说当我们使用 #{XX} OGNL 表达式的时候, 它会先帮我们生成一条带占位符的 SQL 语句,然后在底层帮我们设置这个参数:ps.setInt(1, id);
那,OGNL 是什么呢?
OGNL 是 Object-Graph Navigation Language 的缩写,对象-图行导航语言,语法为:#{ }。
是不是有点懵,不知道这是个啥?
OGNL 作用是在对象和视图之间做数据的交互,可以存取对象的属性和调用对象的方法,通过表达式可以迭代出整个对象的结构图。
举个栗子:
有一个学生对象 student,属性分别有 id = 10,name = '学生1' 和 课程对象 course,其中 course 对象中属性有:分数 score = 88,排名 rank = 5。
对象关系树结构如下:
student
id:10
name:学生1
course:
score:88
rank:5
当上下文(环境)中的对象为 student 的时候,也就是在 Mybatis 中查询时传入的参数对象为 student 的时候:
通过 OGNL 表达式直接获取上下文中对象的属性值,比如:
#{id} —> 10,相对于当前上下文对象.getId(),即 student.getId() 。
#{name} —> 学生1。
#{course.score} —> 88,相当于 student.getCourse().getScore()。
所以,通过 OGNL 表达式,可以迭代出整个对象的结构图。
回到前面说的查询单个学生信息的 SQL:
<select id="get" parameterType="java.lang.Long" resultType="Student">
select id, name, sex from t_student where id = #{id}
</select>
如果通过 namespace + id 的方式来查询的话,如:
Student student = sqlSession.selectOne("com.mxz.mybatis.mapper.StudentMapper.get", 1L);
可以看到,当前上下文的对象并没有 id 这个属性,传入的参数为 1L(简单类型),但是运行结果是正确的。
于是,
如果当前上下文对象是简单类型对象(基本类型+String),#{X} 表达式直接取出参数值,而和花括号中的名称没有任何关系,即 X 可为任意内容,因为 Mybatis 可以自动判断出来。
如果当前上下文对象是 JavaBean 对象,通过 #{X} 表达式来获取值,X 为属性名称。
如果当前上下文对象是 Map 对象,通过 #{key} 来获取值。
也就是说,
当我们查询单个学生信息,传入的参数为 1L 的时候(简单类型),映射文件中对应 SQL 的 OGNL 表达式不管是写成 #{id},还是 #{ooxx},查询结果是一样的。
而如果我们传入的是一个 student 对象的话,如:
Student key = new Student();
key.setId(1L);
Student student = sqlSession.selectOne("com.mxz.mybatis.mapper.StudentMapper.get", key);
那么我们要取到 id 的值的话,就必须写成 #{id},否则乱写则会报错。
tips:查看官方文档是学习一门新语言最有效的方法。
系列预告:Mybatis 系列 6:MybatisUtil 提取,作用域(scope)和生命周期。
欢迎关注公众号「阿泽学长」。