您的位置:首页 > 数据库

mybatis动态sql

2022-03-24 22:15 831 查看

1.动态SQL

  • 【官方声明】

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

    使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

    如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

1.1 IF关键字

1.1.1 动态SQL测试

  1. dao层 接口 => 【BlogsMapper】

    import java.util.List;
    import java.util.Map;
    
    public interface BlogsMapper {
    // 创建【插入测试数据】的方法
    int addBlog(Blogs blog);
    
    // 创建【实现动态SQL查询】的方法
    List<Blogs> getBlogsByDynamicSQL(Map<String,String> map);
    }
  2. XML映射文件 => 【BlogsMapper.xml】

    <select id="getBlogsByDynamicSQL">
    <!-- 注意: where 1 = 1 尽量不使用 -->
    select * from test.blog where 1 = 1
    <!-- <if>标签: 当向数据库发送的请求中,【title】字段不为空时,则添加【title】字段的查询过滤条件 -->
    <if test="title != null">
    and title = #{title}
    </if>
    <!-- <if>标签: 当向数据库发送的请求中,【author】字段不为空时,添加【author】字段的查询过滤条件 -->
    <if test="author != null">
    and author = #{author}
    </if>
    </select>
  3. 测试实现类 => 【DaoTest】

    @Test
    public void getBlogsByDynamicSQL(){
    
    MyBatisUtils mybatis = new MyBatisUtils();
    SqlSession sqlSession = mybatis.getSqlSession();
    BlogsMapper mapper = sqlSession.getMapper(BlogsMapper.class);
    
    List<Blogs> blogs = mapper.getBlogsByDynaticSQL();
    for (Blogs blog : blogs){
    System.out.print(blog);
    }
    
    sqlSession.close();
    }

1.2 where、set,Foreach

foreach用法

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

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

<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
<where>
<foreach item="item" index="index" collection="list"
open="ID in (" separator="," close=")" nullable="true">
#{item}
</foreach>
</where>
</select>
  • IF【官方实例】

    失败案例

    前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。

    <select id="findActiveBlogLike"
    resultType="Blog">
    SELECT * FROM BLOG
    WHERE<if test="state != null">
    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>
    </select>
  • 如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

    SELECT * FROM BLOG
    WHERE
  • 这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

    SELECT * FROM BLOG
    WHEREAND title like ‘someTitle’
    
  • 成功案例

      这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:

      <select id="findActiveBlogLike"
      resultType="Blog">
      SELECT * FROM BLOG
      <where>
      <if test="state != null">
      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>
  • 【官方声明】

      <where>标签:

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

      如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

      //此案例实际上就是where这个标签的工作原理,在拼接sql前方/开头添加where,并去掉拼接语句整体最前方的and/or 单词
      <trim prefix="WHERE" prefixOverrides="AND |OR ">
      <!-- 【prefixOverrides】属性:忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。-->
      </trim>
  • <set>标签:

      用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。

      <update id="updateAuthorIfNecessary">
      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>

    1.2.1 测试

    1. Dao层接口添加实现方法 => 【BlogsMapper】

      int updateBlogInfoBySet(Map map);
    2. XML映射文件 => 【BlogsMapper.xml】

      <update id="updateBlogInfoBySet" parameterType="blogs">
      update test.blog
      <set>
      <if test="title != null"> title = #{title}</if>
      <if test="author != null"> author = #{author}</if>
      <if test="create_time != null"> create_time = #{createTime}</if>
      <if test="views != null"> views = #{views></if>
      </set>
      </update>
    3. 测试实现类 => 【DaoTest】

      @Test
      public void dynamicSqlUpdateBySet(){
      MyBatisUtils myBatisUtils = new MyBatisUtils();
      SqlSession sqlSession = myBatisUtils.getSqlSession();
      BlogsMapper mapper = sqlSession.getMapper(BlogsMapper.class);
      
      HashMap<String,String> map = new HashMap<String, String>();
      map.put("title","updatedTitle");
      map.put("author","updatedAuthor");
      map.put("createTime", String.valueOf(new Date()));
      map.put("views","1");
      map.put("id","5bde3e48b521443bb40524988456a668");
      
      int i = mapper.updateBlogInfoBySet(map);
      if (i > 0 ){
      System.out.println("Update Succeed!");
      }
      sqlSession.close();
      }

    14.3 choose、when、otherwise

    • 【官方声明】:有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

      例子

      <select id="findActiveBlogLike"
      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>

    1.3.1 测试

    1.3.1.1 环境搭建

    1. dao层接口添加方法 => 【BlogsMapper】

      List<Blogs> queryBlogsByChoose(Map map);
    2. xml映射文件 => 【BlogsMapper.xml】

      //choose标签类似于Java中的switch
      <select id="queryBlogsByChoose" resultType="blogs" parameterType="map">
      select * from test.blog
      <!-- <choose>标签: 选择性返回
      |-   <when>标签: 当其内部条件成立时返回
      |-   <otherwise>标签: 当所有条件都不满足时执行
      -->
      <sql id="allchoose">
      <where>
      <choose>
      <when test="title!=null">
      title=#{title}
      </when>
      <when test="author!=null">
      and author=#{author}
      </when>
      <otherwise>
      and views=5000
      </otherwise>
      </choose>
      </where>
      </sql>
      
      <select id="queryBlogChoose" parameterType="map" resultType="blog">
      select * from mybatis.blog
      <include refid="allchoose"></include>
      </select>
    3. 测试实现类 => 【DaoTest】 (按测试情况进行配置 )

    14.3.1.2 测试结果

    • 单个条件成立

      测试实现类 => 【DaoTest】

      @Test
      public void dynamicSqlChoose(){
      MyBatisUtils myBatisUtils = new MyBatisUtils();
      SqlSession sqlSession = myBatisUtils.getSqlSession();
      BlogsMapper mapper = sqlSession.getMapper(BlogsMapper.class);
      
      HashMap<String,String> map = new HashMap<String, String>();
      //创建单条件成立的集合
      map.put("title","MyBatis");
      System.out.println(mapper.queryBlogsByChoose(map));
      
      sqlSession.close();
      }
  • 多个条件成立

      测试实现类 => 【DaoTest】

      @Test
      public void dynamicSqlChoose(){
      MyBatisUtils myBatisUtils = new MyBatisUtils();
      SqlSession sqlSession = myBatisUtils.getSqlSession();
      BlogsMapper mapper = sqlSession.getMapper(BlogsMapper.class);
      
      HashMap<String,String> map = new HashMap<String, String>();
      //创建多个条件满足的集合
      map.put("title","MyBatis");
      map.put("author","Camelot");
      System.out.println(mapper.queryBlogsByChoose(map));
      
      sqlSession.close();
      }
  • <when>都为false,<otherwise>为true

      测试实现类 => 【DaoTest】

      @Test
      public void dynamicSqlChoose(){
      MyBatisUtils myBatisUtils = new MyBatisUtils();
      SqlSession sqlSession = myBatisUtils.getSqlSession();
      BlogsMapper mapper = sqlSession.getMapper(BlogsMapper.class);
      
      //创建空集合传入
      HashMap<String,String> map = new HashMap<String, String>();
      System.out.println(mapper.queryBlogsByChoose(map));
      
      sqlSession.close();
      }
  • 结论

    <choose>标签会在多个条件都满足的情况下,仅会返回第一个传参的返回值。

    但当其他条件都不满足时,可以添加<otherwise>标签,用于返回一个固有值。

  • 14.4 Trim

    • Trim可以自定义SQL语句中的规范,当<where>标签、<set>标签不满足时,可以使用Trim自定义。

    14.4.1 Trim自定义测试

    • 使用Trim复写<where>、<set>规则

    14.4.1.1 <trim>实现<where>

    1. Dao层接口 => 【BlogsMapper】

      List<Blogs> queryBlogsByTrim(Map<String,String> map)
    2. XML映射器 => 【BlogsMapper.xml】

      <select id="queryBlogsByTrim" parameterType="blogs">
      select * from test.blog
      <trim prefix="WHERE" prefixOverride="AND |OR ">//prefix去前缀
      <if test="titleMap != null"> AND title = #{titleMap}</if>
      <if test="authorMap != null"> OR author = #{authorMap}</if>
      </trim>
      </select>
    3. 测试实现类 => 【DaoTest】

      @Test
      public void dynamicSqlSelectByTrim(){
      MyBatisUtils mybatis = new MyBatisUtils;
      SqlSession sqlSession = mybatis.getSqlSession();
      BlogsMapper mapper = sqlSession.getMapper(BlogsMapper.class);
      
      HashMap<String,String> map = new HashMap<String,String>();
      map.put("titleMap","MyBatis");
      map.put("authorMap","Altria");
      
      for (Blogs blog : mapper.queryBlogsByTrim(map)) {
      System.out.println(blog);
      }
      
      sqlSession.close();
      }

    1.4.1.2 <trim>实现<set>

    1. Dao层接口 => 【BlogsMapper】

      int updateBlogInfoByTrim(Map<String,String> map)
    2. XML映射文件 => 【BlogsMapper.xml】

      <update id="updateBlogInfoByTrim" parameterType="map">
      update test.blog
      <trim prefix="SET" suffixOverride=",">//suffix去后缀
      <if test="titleMap != null"> title = #{titleMap},</if>
      <if test="authorMap != null"> author = #{authorMap},</if>
      </trim>
      where id = #{idMap}
      </update>
    3. 测试实现类 => 【DaoTest】

      @Test
      public void dynamicSqlUpdateByTrim(){
      
      MyBatisUtils mybatis = new MyBatisUtils();
      SqlSession sqlSession = mybatis.getSqlSession();
      BlogsMapper mapper = mybatis.getMapper(BlogsMapper.class);
      
      Map<String,String> map = new HashMap<String,String>();
      map.put("authorMap","Altria");
      map.put("titleMap","Spring Framework Updated");
      map.put("idMap","5aa45402bc764755b3ae406be6b27d33");
      
      int i = mapper.updateBlogInfoByTrim(map);
      if( i > 0 ){
      System.out.println("Update Succeed!");
      }
      }
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: