Mybatis之动态SQL

623 阅读4分钟

什么是动态SQL

动态sql是mybatis的核心,主要是对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活的拼接、组装。

通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。

Mybatis提供了几下几种动态SQL元素:

  • if
  • choose
  • foreach
  • where / set
  • trim

IF元素

传统语句

select * from table_name where (11)  

使用mybatis 之后

 <select id="queryByName" parameterType="User" resultType="User">  
     SELECT  refid="columns">  
     FROM sys_user  
     WHERE is_valid = 1  
       test="userName != null">AND user_name like '%' #{userName} '%'  
  </select>

这条语句查询用户表,如果没有传入userName参数,那么就查询出所有is_Valid=1的记录;反之,如果传入了userName参数,则查询is_Valid=1且userName包含传入值的记录。

如果没有where条件会怎么样?

 <select id="queryByName" parameterType="User" resultType="User">  
     SELECT <include refid="columns"></include>  
     FROM sys_user  
     WHERE        
     <if test="userName != null">user_name like '%' #{userName} '%'</if>  
</select>  

这条映射语句是存在陷阱的。不知道大家有没有看出来?就是当IF条件不成立的时候,这条SQL语句就会被转换为

SELECT user_name, user_password, nick_name, email, user_type_id, is_valid, created_time FROM sys_user WHERE  

这条查询语句在数据库中是无法执行的!!

那么我们要如何修改才能够避免这种情况发生呢?有两种方法:

  1. 一个就是像第一段查询语句那样,让IF元素只是WHERE条件子句的一部分;

  2. 另一个就是使用Mybaits提供的WHERE元素。这个后面会讲到。

CHOOSE元素

有时候不是想用到所有条件语句,而是只从其中选择一个。针对这种情况Mybatis提供了类似Java中的switch语句的choose元素。如下示例   

  <select id="queryByName" parameterType="User" resultType="User">  
      SELECT <include refid="columns"></include>  
      FROM sys_user  
      WHERE user_type_id = 1  
      <choose>  
          <when test="userName != null">user_name like '%' #{userName} '%'</when>  
         <when test="nickName != null">nick_name like '%' #{nickName} '%'</when>  
          <otherwise>is_valid = 1</otherwise>  
      </choose>  
  </select>

 

FOREACH元素

这个元素的使用场景是在需要对一个集合进行遍历的时候使用,如批量删除、批量插入等语句。

下面就以批量删除为例,来讲一个这个元素的使用。   

  <!-- 根据传入的Id值列表,删除多条记录 -->  
  <delete id="deleteBatch" parameterType="java.util.List">  
      DELETE FROM sys_user WHERE user_id in  
      <foreach collection="list" item="item" index="index" open="(" close=")" separator=",">  
         #{item}  
      </foreach>  
 </delete>

我们知道Mybatis进行SQL映射时,传入参数只能有一个,如果想传入多个参数,只能使用Java的List或是Array进行封装后再传入。上面的语句就是将要删除的多条记录的Id值放在了List对象中传入。

foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串(上例中的open和close属性)以及在迭代中间放置分隔符(separator属性)。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

我们可以将一个 List 实例或者数组作为参数对象传给 MyBatis,当我们这么做的时候,MyBatis 会自动将它包装在一个 Map 中并以名称为键。List 实例将会以“list”作为键,而数组实例的键将是“array”。

WHERE / SET元素

在前面讲解IF元素时,我们已经提到了WHERE元素,使用这个元素可以避免在查询语句中出现只有WHERE关键字而没有作何查询条件的情况出现。

 <SELECT  <include refid="columns"></include>  
     FROM sys_user  
      <where>  
          <if test="userName != null">user_name LIKE '%' #{userName} '%'</if>  
          <if test="nickName != null"> OR nick_name LIKE '%' #{userName} '%'</if>  
      </where>  
 </select>

Mybatis会判断只有在WHERE元素中至少有一个条件成立时,才会在查询语句中添加WHERE关键字。

在上述SQL语句中,如果第一个条件不成立,而第二个条件成立时,是不是会在WHERE语句中多个OR关键字呀?可以告诉大家,完全不心担心这个问题,Mybatis早已考虑到了,她会将多余的AND或是OR关键字自动剔除掉(所谓多余,紧跟在WHERE关键字后的第一个AND或是OR)where可以自动去掉条件中的第一个and

SET元素和WHERE元素类似,只是她是使用在数据更新语句中而已。  

  <!-- 更新用户信息,并写回到数据表中 -->  
  <update id="udpateUser" parameterType="User">  
     UPDATE sys_user  
      <set>  
         <if test="userName != null">user_name = #{userName},</if>  
         <if test="userPassword != null">user_password = #{userPassword},</if>  
         <if test="nickName != null">nick_name = #{nickName},</if>  
         <if test="userTypeId != null">user_type_id = #{userTypeId},</if>  
         <if test="isValid != null">is_valid = #{isValid}</if>  
      </set>  
      WHERE user_id = #{userId}  
 </update>

MyBatis针对Mysql数据的批量插入:

使用Mysql数据库的朋友都知道,Mysql的insert语句支持一次写多行数据的,我们就是利用这个语句,实现一次性写入多条数据。

我们以批量增加用户为例,将数据封装在一个List对象中,这样在映射文件里,我们就可以这样写:   image.png

需要注意的是,不要忘记为foreach元素指定separator属性;另外,在循环内部引用对象属性时,一定要加上前缀的。

此处需要注意的是,方法一只适用于mysql数据SQL语法,方法二是通过临时表的形式,基本支持所有的数据库,单对数据库新能损耗相对第一种要大一些

定义SQL片段,共享代码

将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其它的statement中就可以引用sql片段。方便程序员进行开发。

<sql id="query_user_where">
    <if test="userCustom!=null">
        <if test="userCustom.sex!=null and userCustom.sex!=''">
            and user.sex = #{userCustom.sex}
        </if>
        <if test="userCustom.username!=null and userCustom.username!=''">
            and user.username LIKE '%${userCustom.username}%'
        </if>
    </if>
</sql>

引用SQL代码片段