MyBatis之动态SQL的使用

Posted Mq_sir

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis之动态SQL的使用相关的知识,希望对你有一定的参考价值。

MyBatis之动态SQL的使用

什么是动态SQL:简单来说就是根据不同的的条件生成不同的sql语句

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

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

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

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

数据库准备,创建一个blok的表

create table blog(
	id int primary key comment '博客id',
	title varchar(100) not null comment '博客标题',
	author varchar(30) not null comment '博客作者',
	create_time varchar(50) not null comment '创建时间',
	views int(30) not null comment '浏览量'
) 

insert into blog values(1,'javaWeb教程','黑马程序员',now(),1000) 
insert into blog values(2,'安卓软件开发','周世凯,陈小龙',now(),1000) 
insert into blog values(3,'数据结构','清华大学出版社',now(),10000) 
insert into blog values(4,'人文基础与应用','毛灿月',now(),560)
insert into blog values(5,'java教程','小钱',now(),123456)
insert into blog values(6,'C语言','谭浩强',now(),10000)
insert into blog values(7,'C语言','小毛',now(),10000)

在这里插入图片描述

编写实体类

@Data
public class Blog {
    private int id;
    private String title;
    private String author;
    private String  create_Time;
    private int views;
}

1、if语句

编写接口

//    通过if,查询博客
    List<Blog> queryBlogIF(Map map);

编写Mapper.xml的sql语句

    <select id="queryBlogIF" parameterType="map" resultType="pojo.Blog">
        select * from blog
        <where>
            <if test="title!=null">
               and title=#{title}
            </if>
            <if test="author!=null">
                and author=#{author}
            </if>
        </where>
    </select>

如果不传入 “title”,那么所有的 Blog 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行查找并返回对应的 Blog结果

where标签会在下面介绍

测试类

    @Test
    public void queryBlogIF(){
        SqlSession sqlSession = Mybatisutil.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();
        map.put("title","C语言");
        map.put("author","谭浩强");
        List<Blog> blogs = mapper.queryBlogIF(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }

结果

在这里插入图片描述

2、choose、when、otherwise

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

编写接口

   //    通过choose,查询博客
    List<Blog> queryBlogChoose(Map map);

编写Mapper.xml的sql语句

   <select id="queryBlogChoose" parameterType="map" resultType="pojo.Blog">
        select * from blog
        <where>
            <choose>
                <when test="title!=null">
                    title=#{title}
                </when>
                <when test="author!=null">
                    and author=#{author}
                </when>
                <otherwise>
                    and views=#{views}
                </otherwise>
            </choose>
        </where>
    </select>

测试类

    //通过choose查询
    @Test
    public void queryBlogChoose(){
        SqlSession sqlSession = Mybatisutil.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();
        map.put("title","C语言");
        map.put("author","谭浩强");
        List<Blog> blogs = mapper.queryBlogChoose(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }

结果
在这里插入图片描述

当title满足要求时,即’break‘退出choose选择,就不会执行下面sql语句的拼接,和switch一样,所以这里查出来的有两条记录,当我只传入views参数时候,就会拼接 otherwise标签的语句,结果如下

在这里插入图片描述

3、trim、where、set

在我们拼接语句的时候,会有标点符号,前缀后缀符号等问题,这个时候可以用trim来解决。

如下面的sql语句,假如没有使用where标签,那么如果if语句中没有满足条件的拼接语句,这个时候我们的sql语句就是select * from blog where,这样的sql语句就是错误的,那么where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

    <select id="queryBlogIF" parameterType="map" resultType="pojo.Blog">
        select * from blog
       		where
            <if test="title!=null">
               and title=#{title}
            </if>
            <if test="author!=null">
                and author=#{author}
            </if>
    </select>

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

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

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

<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>

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

来看看与 set 元素等价的自定义 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

3.1、set修改数据

编写接口

    //使用set修改数据
    int updateBlog(Map map);

编写Mapper.xml的sql语句

<update id="updateBlog" parameterType="map">
        update blog
        <set>
            <if test="title!=null">
                 title=#{title},
            </if>
            <if test="author!=null">
                author=#{author}
            </if>
        </set>
        where id=#{id}
    </update>

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

测试类

    @Test
    public void updateBlog(){
        SqlSession sqlSession = Mybatisutil.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();
        //map.put("title","C语言");
        map.put("author","小浩强");
        map.put("id",6);
        mapper.updateBlog(map);

        sqlSession.close();
    }

结果
在这里插入图片描述

4、Foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历

编写接口

  //使用foreach查询3 4 5 条博客
    List<Blog> queryBlogForeach(Map map);

编写Mapper.xml的sql语句

    <select id="queryBlogForeach" parameterType="map" resultType="pojo.Blog">
        select * from blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id=#{id}
            </foreach>
        </where>
    </select>

测试类

    @Test
    public void queryBlogForeach(){
        SqlSession sqlSession = Mybatisutil.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();
        ArrayList<Integer> idlist = new ArrayList<>();
        idlist.add(3);
        idlist.add(4);
        idlist.add(5);
        map.put("idlist",idlist);
        List<Blog> blogs = mapper.queryBlogForeach(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }

结果:

在这里插入图片描述

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

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

5、SQL片段

我们会将公共的部分提取出来,方便复用

使用:

  1. 使用SQL标签提取公共的部分
<!--    将公共部分,需要重复使用的sql提取出来,使用sql标签-->
<sql id="if-titlt-author">
        <if test="title!=null">
            title=#{title},
        </if>
        <if test="author!=null">
            and author=#{author}
        </if>
    </sql>
  1. 在需要使用的地方使用include标签
<!--    在需要使用的地方使用include标签-->
    <select id="queryBlogIF" parameterType="map" resultType="pojo.Blog">
        select * from blog
        <where>
            <include refid="if-titlt-author"></include>
        </where>
    </select>

注意事项:

  • 最好基于单表来定义SQL片段
  • 不要存在where标签

总结:

  • 动态SQl就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列就行
  • 书写sql语句之前先在sql查询环境中测试一下,避免写错,在编写Mappre.xml的时候找BUG。

以上是关于MyBatis之动态SQL的使用的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis 学习笔记总结

MyBatis-05-笔记

mybatis动态sql片段与分页,排序,传参的使用

Mybatis -- 动态Sql 环境搭建

MyBatis动态SQL标签用法

[mybatis]动态sql_sql_抽取可重用的sql片段