动态 SQL,是指通过 MyBatis 提供的各种标签对条件作出判断以实现动态拼接 SQL 语句。 这里的条件判
断使用的表达式为 OGNL 表达式。 常用的动态 SQL 标签有 <if>、<where>、<choose/>、<foreach> 等。
MyBatis 的动态 SQL 语句,与 JSTL 中的语句非常相似。
动态 SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的 SQL 语句不同。若将每种可能的情况均逐一列出,对所有条件进行排列组合,将会出现大量的 SQL 语句。此时,可使用动态 SQL 来解决这样的问题。
使用动态 SQL ,需要 Java 对象作为方法参数。
在 mapper 的动态 SQL 中若出现大于号(>)、小于号(<)、大于等于号(>=),小于等于号(<=)等符号,最好将其转换为实体符号。否则, XML 可能会出现解析出错问题。特别是对于小于号(<),在 XML 中是绝不能出现的。否则解析 mapper 文件会出错。
实体符号表:
原始符号 | 实体符号 |
---|---|
< | < |
> | > |
>= | >= |
<= | <= |
1、if
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。
对于 <if> 标签的执行,当 test 的值为 true 时,会将其包含的 SQL 片段拼接到其所在的 SQL 语句中。
语法: <if test="条件表达式"> sql 语句片段 </if>
。
使用实例:查询名字为 #{name} 并且年龄大于15岁的学生的信息,查询的前提条件:传入的参数 name 不为空,age>15。
- 接口方法:
List<Student> selectStudentIf(Student student);
- mapper 文件
<select id="selectStudentIf" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<!-- 小技巧 --->
where 1=1
<!-- <if:test="是使用方法参数 Java 对象的属性值作为判断条件"> -->
<!-- #{} 也是使用方法参数 Java 对象的·属性值作为 sql 语句的参数 -->
<if test="name != null and name !='' ">
and name = #{name}
</if>
<if test="age > 15 ">
or age > #{age}
</if>
</select>
3、测试方法
@Test
public void test(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
// 传入的对象是任意类型的,只要符合业务需求即可,此处使用 Student 的对象是因为懒
Student params = new Student();
params.setName("李四");
params.setAge(15);
List<Student> students = studentDao.selectStudentsIf(student);
students.forEach(System.out::println);
}
<if/> 标签的中存在一个比较麻烦的地方:需要在 where 后手工添加 1=1 的子句。因为,若 where 后的所有 <if/> 条件均为 false,而 where 后若又没有 1=1 子句,则 SQL 中就会只剩下一个空的 where,则SQL 出错。所以,在 where 后,需要添加永为真的子句 1=1,以防止这种情况的发生。但当数据量很大时,会严重影响查询效率。
2、where
如果使用 <where/> 标签, 在其后有查询条件时,可以自动添加上 where 子句;没有查询条件时,则不会添加 where 子句。
注意:第一个 <if/> 标签中的 SQL 片断,可写可不写 and 或 or,因为 MyBatis 会将多余的 and 或 or 去掉;但之后的 <if/> 中 SQL 片断的 and 或 or,必须要求写上,否则 SQL 语句将拼接出错。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
<!-- 此处也可以写 and 或 or -->
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
3、foreach
<foreach/> 标签用于实现对于方法参数是数组或集合中的元素的遍历,主要用在 sql 的 in 语句中。
比如说,SQL中查询 id 是1001、1002、1003的学生的信息:
select * from student where id in (1001,1002,1003);
很容易想到,我们可以将这三个数据添加到一个集合中,将这个集合作为方法参数传给 mapper 文件,再从集合中遍历元素拼接为 完整的 sql 语句。
现在先来模拟一下 MyBatis 对于该语句的拼接:
@Test
public void testfor(){
List<Integer> list = new ArrayList<>();
list.add(1001);
list.add(1002);
list.add(1003);
//String sql="select * from student where id in (1001,1002,1003)";
String sql="select * from student where id in";
StringBuilder builder = new StringBuilder("");
//循环开始,字符串尾追加 (
builder.append("(");
for(Integer i:list){
// 字符串尾追加 元素,
builder.append(i).append(",");
}
builder.deleteCharAt(builder.length()-1);
//循环结束,添加 )
builder.append(")");
sql = sql + builder.toString();
System.out.println("sql=="+sql);
}
<foreach> 的规范语法:
<foreach collection="" item="自定义名称" open="" close="" separator="">
#{item的自定义名称}
</foreach>
属性介绍:
- collection:表示接口方法参数的类型,也即是表示要遍历的集合的类型
- 数组,赋值为 array
- 集合,赋值为 list
- item:表示当前遍历到的集合成员,自定义名称
- open:表示循环开始的字符
- close:表示循环结束的字符
- separator:集合成员之间的分隔符
由之前的模拟,就可以很容易得出各个成员变量的值:
- collection="list" 或 collection="array"
- item="自定义名称"
- open="("
- close=")"
- seperator=","
3.1、遍历 List<简单类型>
使用实例:查询 id 是 1002,1005,1006的学生的信息
- 接口方法
List<Student> selectStudentForList(List<Integer> idList);
- mapper 文件
<select id="selectStudentForList" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")" item="stuid" separator=",">
<!-- 遍历简单类型,占位符使用item的值即可 -->
#{stuid}
</foreach>
</if>
</select>
- 测试方法
@Test
public void testSelectForList() {
List<Integer> list = new ArrayList<>();
list.add(1002);
list.add(1005);
list.add(1006);
List<Student> studentList = studentDao.selectStudentForList(list);
studentList.forEach( stu -> System.out.println(stu));
}
3.2、遍历 List<对象类型>
使用实例:查询 id 是 1002,1005,1006的学生的信息
- 接口方法
List<Student> selectStudentForList2(List<Student> stuList);
- mapper 文件
<select id="selectStudentForList2" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")" item="stuobject" separator=",">
#{stuobject.id}
</foreach>
</if>
</select>
- 测试方法
@Test
public void testSelectForList2() {
List<Student> list = new ArrayList<>();
Student stu = new Student();
stu.setId(1002);
list.add(stu);
stu = new Student();
stu.setId(1005);
stu = new Student();
stu.setId(1006);
list.add(stu);
List<Student> studentList = studentDao.selectStudentForList2(list);
studentList.forEach( stu -> System.out.println(stu));
}
4、choose、when、otherwise标签
5、set
6、trim
7、SQL片段
<sql/> 标签用于定义 SQL 片段,以便其它 SQL 标签复用。
如果其它 SQL 标签需要复用 SQL 片段,只需要使用 <include/> 子标签进行引用即可。
SQL 片段使用步骤:
- 定义一个 SQL 片段
<sql id="自定义名称,唯一性">
可复用的 sql语句, 表名,字段等
</sql>
- 其他 SQL 标签使用 <include/> 进行引用
<select id="" resultType="">
<include refid="sql标签id的值" />
剩余的sql语句部分
</select>
使用实例:
<sql id="select*">
select id, name, age, email, from student
</sql>
<select id="selectStudentsIf" resultType="com.bjpowernode.domin,Student">
<include refid="select*" />
<where>
<if test="name != null and name!='' ">
name=#{name}
</if>
<if test="age > 18">
or age > #{age}
</if>
</where>
</select>