动态SQL

mybatis在为我们书写SQL语句的时候提供了很大的方便,它允许我们通过一系列标签灵活的操控我们的SQL语句。下面介绍这些标签以及它们的使用方法

相关标签的介绍

  • if
  • choose
    • when
    • otherwise
  • where
  • trim
  • set
  • foreach
  • bind
  • sql

下面介绍每个标签的使用方法

 if

顾名思义,if标签用于判断条件的正确与否,如果正确会执行if标签体里面的SQL语句,如果条件不成立,则不会执行

if标签属性介绍

  • test:用于存放判断条件,这与JSTL表达式很相似,但是里面用的却是OGNL表达式,而非EL表达式

例:

<select id="getEmpsByConditionIf" resultType="com.jinxin.bean.Employee">
  SELECT * FROM tb1_employee
  WHERE
    <!-- test:判断表达式,与JSTL不同,这里面的不是el表达式而是OGNL表达式
         OGNL表达式中的值是从传来的参数中获取的
    -->
    <if test="id!=null">
        id=#{id}
    </if>
    <if test="lastName!=null and lastName!=''">
        AND last_name LIKE #{lastName}
    </if>
    <if test="email!=null and email.trim()!=''">
        AND email=#{email}
    </if>
    <if test="gender==0 or gender==1">
        AND gender=#{gender}
    </if>
</select>

where

上面使用if标签先进行条件判断。例如,如果传来的id不为空,则会执行 id = #{id} 的操作,否则不会执行,其他的项亦然,但是这有一个问题,如果第一个id的判断不成立,那么就不会显示 id = #{id} ,那么整个SQL最终必然是 WHERE 后面直接就跟了 AND,所以会出错

那么如何解决这个问题呢?有两种方式

1、既然错误的根源是因为第一个判断不成立而使得后面的SQL会多出一个AND,那么干脆在所有的判断之前加一个绝对成立的判断,例如 1 = 1 ,这样在后面所有的判断中的SQL之前都带上 AND即可。但是这种方式未免太不优雅了,放弃

2、既然上面的方法因为不够优雅而受诟病,那么有没有更为优雅的解决方案呢?于是便有了where标签,where标签能够将前面多余SQL自动去掉

例:

<select id="getEmpsByConditionIf" resultType="com.jinxin.bean.Employee">
  SELECT * FROM tb1_employee
    <where>
        <if test="id!=null">
            id=#{id}
        </if>
        <if test="lastName!=null and lastName!=''">
            AND last_name LIKE #{lastName}
        </if>
        <if test="email!=null and email.trim()!=''">
            AND email=#{email}
        </if>
        <if test="gender==0 or gender==1">
            AND gender=#{gender}
        </if>
    </where>
</select>

trim

上面的where元素虽然能够解决SQL前面多余AND的问题,但是如果后面有多余的AND或者其他的字符怎么办?此时where无能为力,但是不要慌,还有一个更加好用的标签——trim,trim不但能够去掉它所包含的SQL前面多余的字符,还能去掉其后面多余的字符,不但如此,它还能在前后加上想要的字符,那么看看怎样使用吧

trim标签属性介绍:

  • prefix:前缀,给trim包含的SQL前面加上一个前缀
  • prefixOverrides:前缀覆盖,去掉前面的指定字符,例如AND 或 OR
  • suffix:后缀,给trim包含的SQL加一个后缀
  • suffixOverrides:后缀覆盖,去掉后面指定的字符,例如逗号,AND、OR什么的

例:

<select id="getEmpsByConditionIf" resultType="com.jinxin.bean.Employee">
  SELECT * FROM tb1_employee
    <trim prefix="WHERE" suffixOverrides="AND | OR">
        <if test="id!=null">
            id=#{id}
        </if>
        <if test="lastName!=null and lastName!=''">
            AND last_name LIKE #{lastName}
        </if>
        <if test="email!=null and email.trim()!=''">
            AND email=#{email}
        </if>
        <if test="gender==0 or gender==1">
            AND gender=#{gender}
        </if>
    </trim>
</select>

choose

choose有类似于switch的效果,但是又有些不同,switch的条件是在switch里面给定的,然后去case里面匹配,这一点跟前面使用resultMap封装结果集的时候使用的discriminator更加相似。而choose的判断却是在每一个when标签里面判断,而且判断的条件不唯一

choose下有两个字标签:一个是when,一个是otherwise

when:

  • test:用来放判断条件

otherwise:如果前面所有的when都没有通过,就会执行这里面的sql

例:

<select id="getEmpsByChoose" resultType="com.jinxin.bean.Employee">
  SELECT * FROM tb1_employee
  <where>
      <!-- 如果带了id据按照id查询,如果带了lastName就按照lastName查询 -->
      <choose>
          <when test="id!=null">
              id=#{id}
          </when>
          <when test="lastName!=null">
              last_name LIKE #{lastName}
          </when>
          <when test="email!=null">
              email=#{email}
          </when>
          <otherwise>
              1=1
          </otherwise>
      </choose>
  </where>
</select>

set

where一样,set是用于封装修改条件的(where用于封装查询条件)

where是为了解决and多余的问题,同样set是为了解决更新过程中逗号多余的问题

例:

<update id="updateEmp">
    UPDATE tb1_employee
    <set>
        <if test="last_name!=null">
            last_name=#{lastName},
        </if>
        <if test="email!=null">
            email=#{email},
        </if>
        <if test="gender!=null">
            gender=#{gender}
        </if>
    </set>
    WHERE id=#{id}
</update>

foreach

foreach,见名知意,是用来做循环的,对于传来的参数是集合或者数组的情况下,就可以使用foreach处理。

如下,想要筛选出id属于某个集合的数据

mapper:mapper里面的方法会传入一个参数,这个参数是一个List,里面保存了一个id的集合

List<Employee> getEmpByForeach(List<Integer> ids);

 然后就可以通过foreach拼装条件,首先对foreach的属性进行介绍

  • collection:指定要遍历的集合,在前面mapper映射文件那篇博客中有提到过,list类型的参数会被封装在map里面,使用list或者collection取出即可
  • item:代表当前循环的元素
  • separator:指定每个元素中间的分割符,也就是循环之间的分割符
  • open:开始拼装前要加的字符,相当于加一个前缀
  • close:结束拼装前要加的字符,相当于加一个后缀
  • index:索引,在遍历List的时候就是索引值,在遍历map的时候就是map的key,value在上面提到的item里面

例:

<select id="getEmpByForeach" resultType="com.jinxin.bean.Employee">
    SELECT * FROM tb1_employee WHERE id IN
    <!--
        collection: 指定要遍历的集合,list类型的参数会做特殊处理封装在map中,map的key就叫做list
        item:当前遍历的元素
        separator:指定每个元素之间的分割符
        open:拼装结果开始的字符
        close:拼装结果结束的字符
        index:索引,遍历List的时候就是索引值,遍历map的时候index表示的就是map的key,item表示的是值
        #{变量名} 就能取出当前遍历的元素
    -->
    <foreach collection="list" item="item_id" separator="," open="(" close=")">
        #{item_id}
    </foreach>
</select>

foreach批量插入

上面介绍了foreach基本的用法,那么使用foreach还能做什么呢?有一个需求特别适合用foreach实现。

现在有一个集合,里面保存的是元素是某张表的实体类,现在有很多数据,我们希望一次性批量插入

首先应该明确,使用SQL应该怎样做批量插入?如下:

INSERT INTO tb1_employee(last_name, email, gender, d_id)
VALUES ("tina", "tina@163.com", '0', 1), ("frank", "frank@163.com", '1', 2)

可见,上面是通过在VALUES后面以一个扩招为一个单位进行批量插入的

那么已经非常明确了,我们需要做的就是使用foreach去循环后面的这几个括号进行批量插入

<insert id="addEmps">
    INSERT INTO tb1_employee(last_name, email, gender, d_id)
    VALUES
    <foreach collection="emps" item="emp" separator=",">
      (#{emp.lastName}, #{emp.email}, #{emp.gender}, #{emp.department.id})
    </foreach>
</insert>

其实除了上面在value后面跟上多个插入的数据以外,还有一个想法,能否每条数据直插入一条记录,然后执行多次。但是mysql默认不支持一次执行多个sql语句,如果要一次执行多条语句,需要去url上开启执行多条语句: jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true 

这样后mysql就可以一次执行多条SQL语句了

<insert id="addEmps">
    <foreach collection="emps" item="emp" separator=";">
        INSERT INTO tb1_employee(last_name, email, gender, d_id)
        VALUES
        (#{emp.lastName}, #{emp.email}, #{emp.gender}, #{emp.department.id})
    </foreach>
</insert>

oracle批量插入

oracle中是不支持在VALUES跟多条记录进行保存的,那么上面针对mysql的批量保存方法就不适用于oracle了,那么在oracle中应该怎样批量保存呢

1、可以使用PLSQL插入

BEGIN
    INSERT INTO tb1_employee(id, last_name, email)
    VALUES(employee_seq.nextval, ‘Sam’, ‘Sam@163.com’);
    
    INSERT INTO tb1_employee(id, last_name, email)
    VALUES(employee_seq.nextval, ‘Jim’, ‘Jim@163.com’);
END;

2、利用中间表

INSERT INTO tb1_employee(id, last_name, email)
    SELECT employee_seq.nextval, lastName, email FROM(
        SELECT ‘test’ lastName, ‘test@163.com’ email FROM dual
        union
        SELECT ‘test1’ lastName, ‘test1@163.com’ email FROM dual
        union
        SELECT ‘test2’ lastName, ‘test2@163.com’ email FROM dual
)

两个内置参数

除了方法中传递的参数可以被用来判断,取值以外。mybatis还提供了两个内置参数

_parameter

_parameter代表了整个参数

  • 单个参数:_parameter就是这个参数

  • 多个参数:参数会被封装成一个map_parameter就是代表这个map

_databaseId

_databaseId:如果在全局配置文件中配置了databaseIdProvider标签。_databaseId就是代表当前数据库的别名(oraclemysqlSqlite)

bind

有时如果我们传来的参数过长,或者通过 属性.属性 的方式链的太长了,而这个变量又大量使用,那么可以使用bind给它取一个别名,方便使用

bind属性说明:

  • name:别名
  • value:要替换的变量

例:

<select id=”getEmpByLastName” resultType=”Employee”>
    <bind name=”_lastName” value=”’%’ + lastName + ‘%’” />
    SELECT * FROM tb1_employee WHERE last_name LIKE #{_lastName}
</select>

sql

使用sql可以抽取可以重用的SQL片段,方便后面引用

例:

<sql id="insertColumn">
    employee_id, last_name, email
</sql>

<select id="addEmp" databaseId="oracle">
    <!-- include:引用外部定义的sql -->
    INSERT INTO tb1_employee(<include refid="insertColumn">
</include>) VALUES ...
</select>

 

posted @ 2018-11-11 17:01  Jin同学  阅读(171)  评论(0)    收藏  举报