7、MyBatis动态SQL

学习资源:动力节点《2020最新MyBatis教程【IDEA版】-MyBatis从入门到精通》



动态 SQL,是指通过 MyBatis 提供的各种标签对条件作出判断以实现动态拼接 SQL 语句。 这里的条件判
断使用的表达式为 OGNL 表达式。 常用的动态 SQL 标签有 <if>、<where>、<choose/>、<foreach> 等。

MyBatis 的动态 SQL 语句,与 JSTL 中的语句非常相似。

动态 SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的 SQL 语句不同。若将每种可能的情况均逐一列出,对所有条件进行排列组合,将会出现大量的 SQL 语句。此时,可使用动态 SQL 来解决这样的问题。

使用动态 SQL ,需要 Java 对象作为方法参数。


在 mapper 的动态 SQL 中若出现大于号(>)、小于号(<)、大于等于号(>=),小于等于号(<=)等符号,最好将其转换为实体符号。否则, XML 可能会出现解析出错问题。特别是对于小于号(<),在 XML 中是绝不能出现的。否则解析 mapper 文件会出错。

实体符号表:

原始符号 实体符号
< &lt;
> &gt;
>= &gt;=
<= &lt;=

1、if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。

对于 <if> 标签的执行,当 test 的值为 true 时,会将其包含的 SQL 片段拼接到其所在的 SQL 语句中。

语法: <if test="条件表达式"> sql 语句片段 </if>

使用实例:查询名字为 #{name} 并且年龄大于15岁的学生的信息,查询的前提条件:传入的参数 name 不为空,age>15。

  1. 接口方法:
List<Student> selectStudentIf(Student student);  
  1. 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 &gt; #{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的学生的信息

  1. 接口方法
List<Student> selectStudentForList(List<Integer> idList);
  1. 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>
  1. 测试方法
@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的学生的信息

  1. 接口方法
List<Student> selectStudentForList2(List<Student> stuList);
  1. 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>
  1. 测试方法
@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 片段使用步骤:

  1. 定义一个 SQL 片段
<sql id="自定义名称,唯一性">  
    可复用的 sql语句, 表名,字段等 
</sql>
  1. 其他 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 &gt; #{age}
        </if>
    </where>
</select>
原文地址:https://www.cnblogs.com/sout-ch233/p/13608362.html