Mybatis-动态SQL

动态SQL

什么是动态SQL: 根据不同的条件生成不同的SQL语句

1. 搭建环境

1. 准备数据库

create table `blog`(
`id` varchar(50) not null comment '博客id',
`title` varchar(100) not null comment '博客标题',
`author` varchar(30) not null comment '博客作者',
`create_time` datetime not null comment '创建时间',
`views` int(30) not null comment '浏览量'
)engine=innodb default charset=utf8;

2. 创建一个基础工程

  1. 导包

  2. 编写配置文件

  3. 编写实体类

    package com.wang.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.Date;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Blog {
        private int id;
        private String title;
        private String author;
        //日期用util包中的
        private Date createTime;
        private int views;
    }
    
  4. 编写实体类对应Mapper接口和对应的xml文件

  5. 插入数据

    1. 创建一个工具类,利用UUID作为数据库的id

      package com.wang.utils;
      
      import java.util.UUID;
      
      public class IdUtils {
      
          public static String getId() {
              return UUID.randomUUID().toString().replaceAll("-", "");
          }
      
      }
      
    2. 编写插入数据的接口及对应的mapper

      package com.wang.dao;
      
      import com.wang.pojo.Blog;
      
      public interface BlogMapper {
      
          //插入数据
          int addBlog(Blog blog);
      }
      
      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.wang.dao.BlogMapper">
      
          <insert id="addBlog" parameterType="Blog">
              insert into blog(id, title, author, create_time, views)
              values (#{id}, #{title}, #{author}, #{createTime}, #{views});
          </insert>
      
      </mapper>
      
    3. 插入数据

      package com.wang.dao;
      
      import com.wang.pojo.Blog;
      import com.wang.utils.IdUtils;
      import com.wang.utils.MybatisUtils;
      import org.apache.ibatis.session.SqlSession;
      import org.junit.Test;
      
      import java.util.Date;
      
      public class MyTest {
      
          @Test
          public void TestAddBlog() {
              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(9999);
      
              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("微服务");
              mapper.addBlog(blog);
      
              //增删改查要开启事务
              sqlSession.commit();
      
              sqlSession.close();
          }
      
      }
      

2. IF

1. 编写接口

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

2. 编写mapper文件

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

3. 测试

@Test
public void TestQueryBlogIF() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();

    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("author", "狂神说");
    List<Blog> blogs = mapper.queryBlogIF(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }

    sqlSession.close();
}

3. choose(when, otherwise)

类似Java中的switch语句, when相当于case, otherwise相当于default

<select id="queryBlogChoose" parameterType="map" resultType="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>

4. tirm(where, set)

1. where

由于使用where语句,如果第一个if不使用,则会在where后直接跟and,从而导致SQL语句报错

在之前的if标签中,使用了where 1 = 1来避免这种情况,但是这样写时不正规的

因此我们下面使用where标签来解决此问题

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

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

2. set

使用set标签解决update只更新部分字段导致出现无关的逗号导致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>

3. trim

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

1. 与where等价的trim

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

2. 与set等价的trim

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

5. Foreach

    <!--我们现在传递一个万能的map, 这个map中可以存在一个集合
        foreach中的collection标签为遍历的集合,item为集合中的元素
        open为每一个遍历元素的对应语句的头开头,close为结尾
        separator为其分隔符
    -->
    <select id="queryBlogForeach" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>
@Test
public void TestQueryBlogForeach() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();

    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    ArrayList<Integer> ids = new ArrayList<>();
    for (int i = 1; i < 4; i++) {
        ids.add(i);
    }
    map.put("ids", ids);
    List<Blog> blogs = mapper.queryBlogForeach(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }

    sqlSession.close();
}

6. SQL片段

有的时候,我们会将一些公共的部分抽取出来,方便复用

1. 使用sql标签抽取公共的部分

<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

2. 在需要使用的地方使用include标签引用即可

<select id="queryBlogIF" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <include refid="if-title-author">
        </include>
    </where>
</select>

3. 注意事项

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

7. 动态SQL的本质

所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码

8. 总结

  • 动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了
  • 建议:
    • 先在MySQL中写出完整的SQL语句
    • 再对应的去修改成为我们的动态SQL实现通用即可
posted @ 2020-09-01 16:51  山人西来  阅读(179)  评论(0编辑  收藏  举报