首页 > 技术文章 > MyBatis(九):动态SQL

renzhongpei 2020-03-29 21:58 原文

本文是按照狂神说的教学视频学习的笔记,强力推荐,教学深入浅出一遍就懂!b站搜索狂神说或点击下面链接

https://space.bilibili.com/95256449?spm_id_from=333.788.b_765f7570696e666f.2

动态SQL(Dynamic SQL)

  • 动态 SQL 是 MyBatis 的强大特性之一。

  • 其实就是简化了拼接SQL的方式。比如实际业务中,输入了用户名时,要按用户名查询是非常常见的,动态SQL其实就是Mybatis里的方式。

if(username!=null&&!"".equals(username)){
    sb.append("and username = '"+username+"' " );
}

 

  • 在Mapper.xml文件中使用以下四种符号实现(和JSTL很像)。

    • if

    • choose (when, otherwise)

    • trim (where, set)

    • foreach

环境准备

 

 

 

拓展

  • 实体类

public class Blog {
​
    private int id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}
​

 

注意:实体类的Date是createTime,而数据库中的是create_time,字段不一样,但是可以通过主配置文件的setting,自动把驼峰规则修改为下划线---->createTime的T自动变为_t。
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

 

 

  • UUID--雪花算法自动生成pk

public class IDutils {
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }
}

 

  • Mapper.xml

    <insert id="addBlog" parameterType="blog">
        <!--注意value中传入的时间是实体类的名称,insert进数据库写的是字段名-->
        insert into mybatis.blog (id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views})
    </insert>

 

  • 测试类

    • 日期的new Date()虽然在控制台输出是,但是只要实体类和数据库的格式都是date,那么就会自动转换成数据库的格式而不需要手动转换。

 

 

 

​
    @Test
    public void addInitBlog(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog = new Blog();
        blog.setId(IDutils.getId());
        blog.setTitle("Mybatis学习");
        blog.setAuthor("张三");
        blog.setCreateTime(new Date());
        blog.setViews(222);
        System.out.println(blog.toString());
        mapper.addBlog(blog);
        blog.setId(IDutils.getId());
        blog.setTitle("Java学习");
        mapper.addBlog(blog);
        blog.setId(IDutils.getId());
        blog.setTitle("Spring学习");
        mapper.addBlog(blog);
        blog.setId(IDutils.getId());
        blog.setTitle("SpringMVC学习");
        mapper.addBlog(blog);
        sqlSession.commit();
        sqlSession.close();
    }

 

 

if

  • Mapper接口

    //查询博客
    List<Blog> queryBlogIF(Map map);

 

  • Mapper.xml

    • 增加if条件即可

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

 

  • 测试类,通过map传参

    @Test
    public void queryBlogIf(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
//        map.put("title","Mybatis学习");
        map.put("author","李四");
​
        List<Blog> blogs = mapper.queryBlogIF(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
​
        sqlSession.close();
    }
​

 

 

 

 

choose和where

  • 和if的区别是,choose中只会选择一个执行,就和switch一样

  • Mapper.xml


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

 


where:

  • 上面的例子中增加了where标签

  • 如果不使用where 1=1,可以使用官方给的标签<where>,然后第一个语句不写连接符就可以了

  • (其实第一句也加and测试下来也没问题,而且官方描述是子句开头是and或者or的,作为第一句语句时会自动去除and和or,写了更整齐,以后用多了再看吧)。

 

 

 

  • 测试类

     

        @Test
        public void queryBlogIf(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            HashMap map = new HashMap();
    //        map.put("title","Mybatis学习");
            map.put("author","李四");
    ​
            List<Blog> blogs = mapper.queryBlogChoose(map);
            for (Blog blog : blogs) {
                System.out.println(blog);
            }
            sqlSession.close();
        }

     

 

 

 

set

  • set只限于update中,和where的用法类似

  • Mapper.xml


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

 

 
  • 测试类

    @Test
    public void updateblog(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        map.put("title","Mybatis学习1212");
        map.put("id","1");
        mapper.updateBlog(map);
        sqlSession.commit();
        sqlSession.close();
    }

 

 

 


 

foreach

  • 对于in语句,可以使用foreach对集合进行遍历

  • 比如对于以下语句,如果2、4、6用map来传,xml文件里就要定义3个变量了,这时候可以用foreach

select * from blog where id in ('2','4','6')

 

  • foreach

<!--collection--传入集合的名称
    item--从集合遍历时每个元素的引用名称
    open--循环前拼接的内容
    separator--每个元素之间间隔的内容
    close--循环后拼接的内容
    index--循环时下标的序号名称,很少会使用到
-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from blog
    <where>
        id in
        <foreach collection="ids" item="idv" open="(" separator="," close=")">
            #{idv}
        </foreach>
    </where>
</select>

 

  • 测试类

    @Test
    public void queryBlogForeach(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        ArrayList<Integer> ids = new ArrayList<>();
        ids.add(2);
        ids.add(4);
        ids.add(6);
        HashMap map = new HashMap();
        map.put("ids",ids);
​
        List<Blog> blogs = mapper.queryBlogForeach(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }
​
 

 

 

 

trim

  • 这个标签其实一般不会用到,因为where、set标签一般就够用了。

  • 假如没有where 标签,使用trim也可以达到相同的效果:

  • trim有3个属性

    • prefix:标签内的语句会用这个属性开头

    • suffix:标签内的语句会用这个结尾

    • prefixOverrides:标签内的第一个语句是这个属性开头,会去掉

    • suffixOverrides:标签内最后一个语句是这个结尾,会去掉

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

 

  • 示例:其实就和where完全一样

    <select id="queryBlogForeach" parameterType="map" resultType="blog">
        select * from blog
        <trim prefix="where" prefixOverrides="and|or" >
            and id in
            <foreach collection="ids" item="idv" open="(" separator="," close=")">
                 #{idv}
            </foreach>
        </trim>
    </select>

 

SQL片段

  • 把公共的SQL抽取出来,方便复用。

  • Mapper.xml

<!--把公共部分抽取出来-->
    <sql id="ifta">
        <if test="title!=null">
            and title = #{title}
        </if>
        <if test="author!=null">
            and author = #{author}
        </if>
    </sql>
<!--通过ID可以进行调用-->
    <select id="queryBlogIF" parameterType="map" resultType="blog">
        select * from blog t
        <where>
            <include refid="ifta"/>
        </where>
    </select>

 


注意事项:

  • 最好基于单表来定义SQL片段

  • 不要存在where标签

推荐阅读