如果我们有复杂的业务,我们需要更复杂的SQL语句,往往需要拼接,而拼接SQL语句,稍微不注意,由于引号、空格等缺失都会导致错误,而Mybatis 提供的动态SQL 使得拼接SQL语句变得容易。
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素
1、if 标签
if 标签和我们java 里面的if语句作用是一样的,下面代码里的test 是判断表达式,判断后面的条件时否成立
<select id="getStu" parameterType="com.tulun.bean.Student" resultType="com.tulun.bean.Student">
select * from student
where
<if test="id != null">
id = #{id}
</if>
<if test="name != null && name != """>
and name = #{name}
</if>
</select>
@Test
public void test(){
StudentDynamicSql mapper = session.getMapper(StudentDynamicSql.class);
Student stu = mapper.getStu(new Student(1, "", "123456"));
System.out.println(stu);
}
从结果来看,第二个if 标签里的语句是不成立的,最后我们生成的SQL语句也没有第二个条件。
2、where 标签
还是上面的例子,我们这次设置id 为null 来看看会发生什么?
@Test
public void test(){
StudentDynamicSql mapper = session.getMapper(StudentDynamicSql.class);
Student stu = mapper.getStu(new Student(null, "wjm", "123456"));
System.out.println(stu);
}
它抛出了一串异常并且打印出如下日志,当第一个条件不成立的时候,这个拼接而成的语句是由问题的。这里不得不说一下标签了。
标签就是我们SQL语句中的where,如果用sql语句的where,那么当第一个条件不满足时,那么后面的条件前都会加一个and,SQL语句就会出现错误,如果后面生成对的语句是以and 或or 开头的,那么标签会自动得到取掉开头的and,or。
我们修改一下映射文件,还是将id 设为null,我们看一下会发生什么?
<select id="getStu" parameterType="com.tulun.bean.Student" resultType="com.tulun.bean.Student">
select * from student
<where>
<if test="id != null">
id = #{id}
</if>
<if test="name != null && name != """>
and name = #{name}
</if>
</where>
</select>
生成的 SQL 语句没问题了, 语句帮我们处理掉了前面的and
3、trim 标签
where 不能处理掉后面的and 或者其他的元素,在这里mybatis 还提供了另一个标签, 标签里面提供了这么几个元素
元素 | 功能 |
---|---|
prefix | 给拼接后的字符串增加一个前缀,可以添加where、set等任何值 |
suffix | 给拼接后的字符增加一个后缀 |
prefixOverrides | 前缀覆盖,标签体里的前面多余的字符去掉 |
suffixOverride | 后缀覆盖,标签体里的后面多余的字符去掉 |
通过 标签,原来的代码我们可以写成这个样子
<select id="getStu" parameterType="com.tulun.bean.Student" resultType="com.tulun.bean.Student">
select * from student
<trim prefix="where" suffixOverrides="and">
<if test="id != null">
id = #{id} and
</if>
<if test="name != null && name != """>
name = #{name}
</if>
</trim>
</select>
4、choose 标签
choose:(when,otherwise)分支选择,相当于带了break的switch case
when:相当于case
otherwise:相当于default
只会进入一个,如果条件都不满足就进入otherwise,那么上面的例子我们可以再变一下。
<select id="getStu" parameterType="com.tulun.bean.Student" resultType="com.tulun.bean.Student">
select * from student
<trim prefix="where" suffixOverrides="and">
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="name != null && name != """>
name = #{name}
</when>
<otherwise>
pssward = #{passward}
</otherwise>
</choose>
</trim>
</select>
@Test
public void test(){
StudentDynamicSql mapper = session.getMapper(StudentDynamicSql.class);
Student stu = mapper.getStu(new Student(null, "", "wjmpwd"));
System.out.println(stu);
}
看一看发生了什么。
在 的两个标签条件都不满足的情况下,进入了otherwise 标签。
5、foreach 标签
动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。
foreach 标签里的几种参数
参数 | 功能 |
---|---|
collection | 指定要遍历的集合 |
List | List 类型的参数会特殊处理封装在map中,map的key就叫list |
item | 将当前遍历出的元素赋给指定的变量 |
separator | 每个元素之间的分隔符 |
open | 遍历出所有结果拼接一个开始的字符 |
close | 遍历出所有结果拼接一个结束的字符 |
现在我们重新写一个接口,通过id 批量查找学生信息
/**
* 批量获取学生信息
* @param list
* @return
*/
public List<Student> getStus(List<Integer> list);
<select id="getStus" parameterType="com.tulun.bean.Student" resultType="com.tulun.bean.Student">
select * from student
<trim prefix="where">
id in
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</trim>
</select>
@Test
public void test(){
StudentDynamicSql mapper = session.getMapper(StudentDynamicSql.class);
/**
* 批量获取学生信息
*/
List<Student> stus = mapper.getStus(Arrays.asList(1, 2, 3, 4));
for (Student stu:stus){
System.out.println(stu);
}
}
最后生成的SQL语句是 select * from student where id in ( ? , ? , ? , ? )。
我们分析一下,
- collection :就是我们方法参数的那个list
- item:每次循环将取出的数据放入这个变量中
- open:循环开开始拼接的字符串,在我们这个SQL语句中就是“(”
- close:循环结束拼接的字符串,在我们这个SQL语句中就是“)”
- separator:就是遍历过程中每个元素之间的分割符
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。collection是传入的可迭代对象参数。当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
6、两个内置参数
① _parameter: 代表整个参数
- 单个参数:_parameter 就是这个参数
- 多个参数:参数会被封装成map,_parameter就代表这个map。当多个参数时,可以通过_parameter.get(0)来获取参数
获取单个学生那个例子,我们继续修改它
<select id="getStu" parameterType="com.tulun.bean.Student" resultType="com.tulun.bean.Student">
select * from student
<if test="_parameter != null">
<trim prefix="where">
<choose>
<when test="id != null">
id = #{_parameter.id}
</when>
<when test="name != null && name != """>
id = #{_parameter.name}
</when>
<otherwise >
pssward = #{_parameter.passward}
</otherwise>
</choose>
</trim>
</if>
</select>
② _databaesId:如果配置了这个_databaesIdProvider标签,_databaesId 就是代表当前数据库的别名
7、bind
绑定一个表达式给一个变量,可以用这个变量来代替这个表达式
我们增加一个这样的接口
public Student getStu2();
<select id="getStu2" resultType="com.tulun.bean.Student">
<bind name="n" value="'wjm'"></bind>
select * from student where name = #{n}
</select>
我们将"wjm" 这个表达式绑定给了n这个变量,表示在后面我们可以通过n这个变量来代替wjm,看看结果吧!
8、sql 标签和include标签
① sql标签:抽取可重用的sql片段,方便后面使用
② include:引用已经抽取的sql 查询,还可以定义一些property ,sql标签内部就能使用自定义的属性,使用${property}引用
我们来看一个小例子,修改一下我们刚刚写的那个方法。
<sql id="l">
id,name,pssward
</sql>
<select id="getStu2" resultType="com.tulun.bean.Student">
<bind name="n" value="'wjm'"></bind>
select
<include refid="l"/>
from student where name = #{n}
</select>
看看结果
总结
其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。