zoukankan      html  css  js  c++  java
  • Mybatis (四) Mybatis动态SQL

    Mybatis动态SQL

    概念

    摘自官网:https://mybatis.org/mybatis-3/zh/dynamic-sql.html

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

    ​ 虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。

    ​ 动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。

    动态sql语句标签主要包括下面这几种:

    • if
    • choose...when...otherwise
    • trim
    • foreach
    • bind

    if 标签

    当我们需要做一些判断的时候,可以使用到if标签,如下:

    <select id="findUsersByUsernamePassword" resultType="user" >
      select * from tb_user
      <where>
        <if test="username != null">
          username = #{username}
        </if>
        <if test="password != null">
          and password = #{password}
        </if>
      </where>
    </select>
    

    所对应的Mapper接口方法:

    /*根据username和password查询*/
    List<User> findUsersByUsernamePassword(User user);
    

    我们可以直接使用对象中的属性来取值。如果此时我们加了@Param("user")

    /*根据username和password查询*/
    List<User> findUsersByUsernamePassword(@Param("user") User user);
    

    对应的取值xml:

    <select id="findUsersByUsernamePassword" resultType="user" >
      select * from tb_user
      <where>
        <if test="user.username != null">
          username = #{user.username}
        </if>
        <if test="user.password != null">
          and password = #{user.password}
        </if>
      </where>
    </select>
    

    解释:上述示例中,sql拼接结果如下:

    • username 为null,password 不为null
      select * from tb_user where password = ?
    
    • username 不为null,password为null
      select * from tb_user where username = ?
    
    • username 不为null,password不为null
      select * from tb_user where username = ? and password = ?
    
    • username 为null,password为null
      select * from tb_user
    

    注意:

    <if test="id != null and userId != '' and username =='2'">
        refusalreason=#{remark,jdbcType=VARCHAR}
    </if>
    

    mybatis是用OGNL表达式来解析的,在OGNL的表达式中,'2'会被解析成字符,java是强类型的,char 和一个string 会导致不等,所以if标签中的sql不会被解析。

    • if标签里,判断相等或不相等
    单个字符需要加.toString(),如:auditidentified =='2'.toString()
    
    • if标签里,判断相等或不相等
    多个字符不需要加toString(),如:auditidentified !='2155'
    

    choose 标签

    ​ 有时我们不希望用到所有的条件,比如用户有记录有id,user_id, username这几个属性,但是查询的时候希望有优先级,比如id存在的时候,只按id查询,如果id不存在,但是user_id存在,则按照user_id查询,如果前面两者都不存在,则按照username查询。

    语句模板:

    <choose>
      	<when test = "a">A</when>
      	<when test = "b">B</when>
      	<otherwise>C</otherwise>
    </choose>
    

    语句解释:

    解释:(只会执行一个条件,也必定会有一个条件执行)
      这个语句相当Java中的 switch...case...default
      switch 	==> choose:定义语句范围
      case   	==> when  :满足当前条件 test 时执行
      default 	==> otherwise :当前面的所有when都不满足的时候,使用otherwise的语句
    
    即:当表达式a计算结果为true时,则执行语句A, choose结束
        当表达式b计算结果为true时,则执行语句B, choose结束
        当前面的when都不满足时,执行C。choose结束。
    

    示例语句如下:

    <select id="findUsersByIdOrUserIdOrUsername" resultType="user">
        select * from tb_user
        <where>
            <choose>
                <when test="id != null">
                 	 id = #{id}
                </when>
                <when test="userId != null">
                	  user_id = #{userId}
                </when>
                <otherwise>
                	  username = #{username}
                </otherwise>
            </choose>
        </where>
    </select>
    

    对应的Mapper接口方法:

    /*查询,如果有id不为空,则直接按id查询,如果id为空,则按userId查询,否则按username查询*/
    List<User> findUsersByIdOrUserIdOrUsername(
        @Param("id") Integer id,
        @Param("userId") String userId,
        @Param("username") String  username);
    

    ​ 注意的同学可以发现,这里与之前的传参方法不同。后面将会有一篇专门介绍参数传递问题。这里先不细说。

    trim 标签

    与trim相关的标签还有set和where标签,这是用来我们解决sql拼接的问题,之前自己手动凭借SQL的时候,需要非常注意 and,or和逗号问题,对应and|or,我们会在sql之前使用where 1 = 1,如下:

    select * from tb_user where 1 = 1 and ...
    

    我们看一下下面这个sql:

    <select id="findUsersByUsernamePassword" resultType="user" >
      select * from tb_user where
        <if test="username != null">
          username = #{username}
        </if>
        <if test="password != null">
          and password = #{password}
        </if>
    </select>
    

    当我username为空,password不为空时,处理之后就会生成如下sql:

    select * from tb_user where and password = ?
    

    上面这个sql就是一个错误的sql,但是mybatis中的动态sql帮我们提供了方法,下面我们看一下trim标签:

    <trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""></trim>
    <!-- 解释:
    prefix: 前缀,即在当前使用trim标签的最开头处添加指定元素。
    suffix: 后缀,即在当前使用trim标签的最结束处添加指定元素。
    prefixOverrides: 在最前面将元素指定的数据移除掉。
    suffixOverrides: 在最后面将元素指定的数据移除掉。
    -->
    

    所以我们使用trim标签来解决上面标签产生的问题,如下:

    <select id="findUsersByUsernamePassword" resultType="user" >
      select * from tb_user
      <trim prefix="where" prefixOverrides="and |or">
        <if test="username != null">
          username = #{username}
        </if>
        <if test="password != null">
          and password = #{password}
        </if>
      </trim>
    </select>
    

    注意:and后面的空格是必要添加的(官网介绍)。但是我测试的空格没有影响,为了规避问题,建议加上空格。

    mybatis鉴于上述使用环境非常常见,将其封装为where标签,以及set标签,如下:

    <select id="findUsersByUsernamePassword" resultType="user" >
      select * from tb_user
      <where>
        <if test="username != null">
          username = #{username}
        </if>
        <if test="password != null">
          and password = #{password}
        </if>
      </where>
    </select>
    
    <update id="updateUserByUser" >
      update tb_user
      <set>
        <if test="userId != null">
          user_id = #{userId},
        </if>
        <if test="username != null">
          username = #{username},
        </if>
        <if test="password != null">
          password = #{password},
        </if>
        <if test="email != null">
          email = #{email},
        </if>
        <if test="phone != null">
          phone = #{phone},
        </if>
        <if test="gender != null">
          gender = #{gender},
        </if>
        <if test="birthday != null">
          birthday = #{birthday},
        </if>
        <if test="status != null">
          status = #{status},
        </if>
        <if test="createTime != null">
          create_time = #{createTime},
        </if>
        <if test="createUser != null">
          create_user = #{createUser},
        </if>
        <if test="modifyUser != null">
          modify_user = #{modifyUser},
        </if>
        <if test="modifyTime != null">
          modify_time = #{modifyTime},
        </if>
      </set>
        where id = #{id}
    </update>
    

    foreach标签

    ​ 这个标签对我们来说从名字上,其实就知道是用来干啥的了。对,foreach标签就是用来循环迭代一些可迭代的元素,如,list,set,map,数组等。

    试想以下,当我们需要根据多个id批量查询的时候,我们使用java代码需要如何操作:

    public void test() {
      String sql = " select * from tb_user where id in ";
      List<Integer> ids = new ArrayList<>();
      ids.add(1);
      ids.add(2);
      ids.add(3);
      ids.add(4);
      String idSql = "(";
      for (int i = 0; i < ids.size(); i++) {
        if (i == ids.size() - 1) {
          idSql += ids.get(i) +")";
        }else{
          idSql += ids.get(i) + ",";
        }
      }
      sql += idSql;
      System.out.println(sql);
    }
    
    //select * from tb_user where id in (1,2,3,4);
    

    可以看出,拼接这个sql非常繁琐,但是Mybatis提供的foreach可以很简单的实现,在此之前,我们看一下foreach标签:

    <foreach collection="" index="" item="" open="" separator="" close="" ></foreach>
    

    注意:

    你可以将任何可迭代对象 如 List、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时。

    • collection 属性的书写方式,List -> list/collection,Set->collection,[]数组 -> array,Map -> map.

    • index 是当前迭代的次数,item 的值是本次迭代获取的元素。

      当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

    • open表示拼接最前的元素

    • close表示拼接在最后的元素

    • separator表示遍历的各个元素之间的分隔符。

    使用foreach实现:

    <select id="findUsersByIds" resultType="user">
      select * from tb_user
      <where>
        id in
        <foreach collection="list" item="ite" open="(" separator="," close=")">
          #{ite}
        </foreach>
      </where>
    </select>
    

    对应的mapper接口的方法:

    List<User> findUsersByIds(List<Integer> id);
    

    bind 标签

    bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文:

    <select id="findUserById" resultType="user">
      <bind name="id" value=" id + 1"/>
      select * from tb_user where id = #{id}
    </select>
    
    
    <!--
       ==>  Preparing: select * from tb_user where username like ?
       ==> Parameters: %xxx%(String)
    -->
    <select id="findUsersByLikeUsername" resultType="user">
      <bind name="name" value=" '%' +username+ '%' "/>
      select * from tb_user where username like #{name}
    </select>
    
    <!-- 模糊查询还可以这样 -->
    <select id="findUserByQueryVo" parameterType="QueryVo"
    	resultType="User">
    	select * from tb_user where username like concat('%',#{user.username},'%')
    </select>
    

    bind 标签比较简单,如上所示,可以将字符串进行拼接,也可以对传递进来的参数进行处理。

    源码地址:

    https://gitee.com/ooyhao/JavaRepo_Public/tree/master/Mybatis

    最后

    如果觉得不错的话,那就关注一下小编哦!一起交流,一起学习

    程序yuan
  • 相关阅读:
    SSH学习-struts2整合spring报错'Could not open ServletContext resource [/WEB-INF/applicationContext.xml]'
    YAML学习
    配置composer代理
    Windows下配置PHPUnit(pear已弃用,使用phpunit.phar)
    算法-第四版-练习1.3.9解答
    算法-第四版-练习1.3.10解答
    算法-第四版-练习1.3.11解答
    算法-第四版-练习1.3.12解答
    算法-第四版-练习1.3.13解答
    算法-第四版-练习1.3.14解答
  • 原文地址:https://www.cnblogs.com/ooyhao/p/11562086.html
Copyright © 2011-2022 走看看