Mybatis动态sql全面详解

156 阅读4分钟

文章目录

写在前面

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

CDATA

在使用MyBatis过程中,有时我们的SQL是写在XML 映射文件中,如果写的SQL中有一些特殊的字符的话,在解析XML文件的时候会被当做XML自身元素,但我们不希望如此操作,所以我们要使用<![CDATA[ ]]>来解决。

<select id="gtBlogs" resultType="Blog">
  <![CDATA[
    select * from t_blog where id < #{blogId}
  ]]>
</select>

sql+include

<sql id="extraSql">
	`id`, `name`, `remark`, `create_by`, `create_at`, `update_by`, `update_at`
</sql>

<select id="selectOne" resultType="Employee">
    select <include refid="extraSql"></include> from employee where emp_id = #{empId}
</select>

sql标签可以抽取sql片段,然后使用include标签将指定id的sql片段插入。

if

if标签:主要用于基本的if判断\

OGNL 的表达式

JSP是用OGNL表达式进行解析的,mybatis的xml文件也是是用OGNL表达式解析。

其中 test 的表达式是基于OGNL 的表达式,语法规则也是OGNL的语法规则。
OGNL官方表达式手册:commons.apache.org/proper/comm…

注:在test中无法使用<= 等符号可以使用 lte 代替
< :lt
<=:lte
>:gt
>=:gte

<! -- trim()可以表示去除首位空格 -- >
<if test='order != null and "DESC".equalsIgnoreCase(order.trim())'>
  xxxx
</if>

if用于where

可以动态拼接sql条件。

<select id="findOne" resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = 'ACTIVE'
  <if test="title != null and title != ''">
    AND title = #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
  <if test="age ==null or age ==''">  
      AND AGE = #{student.age}
  </if>
</select>

if用于set

可以按需更新需要个更新的字段。

<update id="updateSqlSet">
    update demo_user
        <set>
            <if test="name !=null"> name=#{name}, </if>
            <if test="age !=null">  age = #{age}, </if>
            <if test="sex !=null">  sex = #{sex} </if>
        </set>
        where id = #{id}
</update>

choose、when、otherwise

有点类似java中的switch,也有点像if-else

<select id="findOne" resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

where

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

<select id="findOne" resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
        AND 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>

set

set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

<update id="updateOne">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

trim

trim可以替代where和set。

trim的属性:

  • prefix:指定要动态添加的前缀
  • suffix:指定要动态添加的后缀
  • prefixOverrides:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
  • suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值

trim替换where

<select id="selectDatas" resultType="Employee">
    select * from employee
    <trim prefix="WHERE" prefixOverrides="AND|OR">
        <if test="empId != null">
            AND emp_id>#{empId}
        </if>
        <if test="empSalary != null">
            AND emp_salary>#{empSalary}
        </if>
    </trim>
</select>

trim替换set

<update id="updateOne">
  update Author
    <trim prefix="SET" suffixOverrides=",">
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </trim>
  where id=#{id}
</update>

bind

bind 元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。

<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>

foreach

foreach常用于对集合进行遍历(尤其是在构建 IN 条件语句的时候)。

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。

你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

  • collection属性: 表示要遍历的对象,如果要遍历的参数使用@Param注解取名了就使用该名字,如果没有取名List,或者collection。
  • item属性: 表示遍历出来的元素,我们到时候要拼接SQL语句就得使用这个元素: 如果遍历出来的元素是POJO对象, 那么我们就通过 #{遍历出来的元素.POJO的属性} 获取数据;如果遍历出来的元素是简单类型的数据,那么我们就使用 #{遍历出来的元素} 获取这个简单类型数据
  • separator属性: 遍历出来的元素之间的分隔符
  • open属性: 在遍历出来的第一个元素之前添加前缀
  • close属性: 在遍历出来的最后一个元素之后添加后缀

script

要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。

    @Update({"<script>",
      "update Author",
      "  <set>",
      "    <if test='username != null'>username=#{username},</if>",
      "    <if test='password != null'>password=#{password},</if>",
      "    <if test='email != null'>email=#{email},</if>",
      "    <if test='bio != null'>bio=#{bio}</if>",
      "  </set>",
      "where id=#{id}",
      "</script>"})
    void updateAuthorValues(Author author);

也可以在Dao的java中直接使用注解标注sql语句

@Select("select * from t_job t where t.id = #{id}")
JobModel getById(Long id);

多数据库支持

如果配置了 databaseIdProvider,你就可以在动态代码中使用名为 “_databaseId” 的变量来为不同的数据库构建特定的语句。

<insert id="insert">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    <if test="_databaseId == 'oracle'">
      select seq_users.nextval from dual
    </if>
    <if test="_databaseId == 'db2'">
      select nextval for seq_users from sysibm.sysdummy1"
    </if>
  </selectKey>
  insert into users values (#{id}, #{name})
</insert>