8.Mybatis的动态SQL

转载:https://blog.kuangstudy.com/index.php/archives/506/

一.动态SQL

1.介绍

什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.

官网说明:

 1 官网描述:
 2     MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
 3     虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。
 4     动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
 5     
 6     -------------------------------
 7     - if
 8     - choose (when, otherwise)
 9     - trim (where, set)
10     - foreach
11     -------------------------------
  • 我们之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。

  • 那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。

2.搭建环境

(1)新建一个数据库表:blog

1 CREATE TABLE `blog` (
2   `id` varchar(50) NOT NULL COMMENT '博客id',
3   `title` varchar(100) NOT NULL COMMENT '博客标题',
4   `author` varchar(30) NOT NULL COMMENT '博客作者',
5   `create_time` datetime NOT NULL COMMENT '创建时间',
6   `views` int(30) NOT NULL COMMENT '浏览量'
7 ) ENGINE=InnoDB DEFAULT CHARSET=utf8

(2)建立基础工程

  • 导包

  • 编写核心配置文件

  • 编写实体类

  • 编写Mapper接口文件和Mapper.xml文件

(2.1)添加工具类IDUtils.java:

 1 import org.junit.Test;
 2 
 3 import java.util.UUID;
 4 
 5 public class IDUtils {
 6 
 7     public static String getId(){
 8         return UUID.randomUUID().toString().replaceAll("-","");
 9     }
10 
11     @Test
12     public void test(){
13         System.out.println(IDUtils.getId());
14         System.out.println(IDUtils.getId());
15         System.out.println(IDUtils.getId());
16     }
17 }

(2.2)添加实体类Blog.java:

 1 import lombok.Data;
 2 
 3 import java.util.Date;
 4 
 5 @Data
 6 public class Blog {
 7 
 8     private String id;
 9     private String title; 10 private String author; 11 private Date createTime; //属性名和数据库字段名不一致,数据库字段名为create_time 需要开启驼峰命名转换 12 private int views; 13 }

(2.3)添加接口文件BlogMapper.java:

1 import edu.ustc.wzh.pojo.Blog;
2 
3 public interface BlogMapper {
4 
5     //插入数据
6     int addBlog(Blog blog);
7 }

(2.4)添加BlogMapper.xml:

 1 
 2 <mapper namespace="edu.ustc.wzh.dao.BlogMapper">
 3 
 4     <insert id="addBlog" parameterType="Blog">
 5         insert into mybatis.blog(id, title, author, create_time, views)
 6         value (#{id},#{title},#{author},#{createTime},#{views});
 7     insert>
 8 
 9 
10 mapper>

(2.5)测试代码:

 1 @Test
 2 public void addInitBlog(){
 3     SqlSession session = MybatisUtils.getSession();
 4     BlogMapper mapper = session.getMapper(BlogMapper.class);
 5 
 6     Blog blog = new Blog();
 7     blog.setId(IDUtils.getId());
 8     blog.setTitle("Mybatis如此简单");
 9     blog.setAuthor("狂神说");
10     blog.setCreateTime(new Date());
11     blog.setViews(9999);
12 
13     mapper.addBlog(blog);
14 
15     blog.setId(IDUtils.getId());
16     blog.setTitle("Java如此简单");
17     mapper.addBlog(blog);
18 
19     blog.setId(IDUtils.getId());
20     blog.setTitle("Spring如此简单");
21     mapper.addBlog(blog);
22 
23     blog.setId(IDUtils.getId());
24     blog.setTitle("微服务如此简单");
25     mapper.addBlog(blog);
26 
27     session.close();
28 }

3.动态SQL之IF语句

(3.1)IF标签:

  • 需求:根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询,都为空则查询全部博客

BlogMapper.java:

1 //根据传入的属性查询博客(例如传入作者就查作者,传入title就查title)
2 List queryBlogIF(Map map);

BlogMapper.xml:

1 

测试代码:

 1 @Test
 2 public void queryBlogIFTest(){
 3     SqlSession session = MybatisUtils.getSession();
 4     BlogMapper mapper = session.getMapper(BlogMapper.class);
 5 
 6 
 7     Map map = new HashMap();
 8     map.put("title","Mybatis如此简单");
 9 
10     List blogs = mapper.queryBlogIF(map);
11 
12     for (Blog blog : blogs) {
13 
14         System.out.println(blog);
15     }
16 
17     session.close();
18 }

(3.2)where标签:

  • 这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。

修改BlogMapper.xml:将 where 1=1 替换成了where标签

 1 

(3.3)choose(when,otherwise)标签:(类似于Java中的switch case default)

  • 如果一个都不满足查询结果为空

  • 满足了第一个when执行后就退出,不会再执行后面的when或otherwise

BlogMapper.java:

1 List queryBlogChoose(Map map);

BlogMapper.xml:

 1 <select id="queryBlogChoose" parameterType="map" resultType="blog">
 2     select * from mybatis.blog
 3     <where>
 4         <choose>
 5             <when test="title != null">
 6                 title = #{title}
 7             when>
 8             <when test="author != null">
 9                 and author = #{author}
10             when>
11             <otherwise>
12                 and views = #{views}
13             otherwise>
14         choose>
15     where>
16 select>

测试代码:

 1 @Test
 2 public void queryBlogChooseTest(){
 3     SqlSession session = MybatisUtils.getSession();
 4     BlogMapper mapper = session.getMapper(BlogMapper.class);
 5 
 6 
 7     Map<String,String> map = new HashMap<String,String>();
 8     map.put("title","Mybatis如此简单");
 9 
10     List<Blog> blogs = mapper.queryBlogChoose(map);
11 
12     for (Blog blog : blogs) {
13 
14         System.out.println(blog);
15     }
16 
17     session.close();
18 }

(3.4)set标签:一般和upate语句使用,删除多余逗号

  • 这里,set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号。(译者注:因为用的是“if”元素,若最后一个“if”没有匹配上而前面的匹配上,SQL 语句的最后就会有一个逗号遗留)

BlogMapper.java:

1 int updateBlogSet(Map map);

BlogMapper.xml:

 1 <update id="updateBlogSet" parameterType="map">
 2     update mybatis.blog
 3     <set>
 4         <if test="title != null">
 5             title = #{title},
 6         if>
 7         <if test="author != null">
 8             author = #{author}
 9         if>
10     set>
11     where id = #{id}
12 update>

测试代码:

 1 @Test
 2 public void updateBlogSetTest(){
 3     SqlSession session = MybatisUtils.getSession();
 4     BlogMapper mapper = session.getMapper(BlogMapper.class);
 5 
 6 
 7     Map map = new HashMap();
 8     map.put("title","我学习Mybatis如此简单");
 9     map.put("id","5a95b31cb1494aa68c498c5c54243439");
10 
11     int res = mapper.updateBlogSet(map);
12 
13     if (res > 0){
14         System.out.println("更新成功!");
15     }
16 
17     session.close();
18 }

(3.5)trim标签:用来定制自定义的格式

  • <trim prefix="" prefixOverrides="" suffix="" suffixOverrides="">trim>

     

  • prefix:前置标签

  • prefixOverrides:忽略的前置符号

  • suffix:后置标签

  • suffixOverrides:忽略的后置符号

例子:

where标签:前置标签为where时忽略掉开头and或or

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

set标签:前置标签为set时忽略掉结尾逗号

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

(3.6)SQL片段

  • 有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

提取公共的SQL片段:

1 <sql id="if-title-author">
2     <if test="title != null">
3         title = #{title}
4     if>
5     <if test="author != null">
6         and author = #{author}
7     if>
8 sql>

引用SQL片段:

1 <select id="queryBlogIF" parameterType="map" resultType="blog">
2     select * from mybatis.blog
3     <where>
4         
5         <include refid="if-title-author">include>
6         
7     where>
8 select>

注意:

  • 最好基于 单表来定义 sql 片段,提高片段的可重用性

  • 在 sql 片段中不要包括 where,set这种会优化SQL的语句,以防出错

(3.7)Foreach标签

  • 动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。

  • 我们需要查询Blog表中的id=1或id=2或id=3的博客信息:

    select * from blog where 1=1 and (id=1 or id=2 or id=3)

     

  • 格式:

    1 collection:指定输入对象中的集合属性
    2 item:每次遍历生成的对象
    3 open:开始遍历时的拼接字符串
    4 close:结束时拼接的字符串
    5 separator:遍历对象之间需要拼接的字符串

修改Blog表数据:

8.Mybatis的动态SQL_第1张图片

BlogMapper.java:

1 List queryBlogForeach(Map map);

BlogMapper.xml:

 1 

测试代码: map.put("ids",ids); 此处的ids和  一致

 1 @Test
 2     public void queryBlogForeachTest(){
 3         SqlSession session = MybatisUtils.getSession();
 4         BlogMapper mapper = session.getMapper(BlogMapper.class);
 5 
 6         HashMap map = new HashMap();
 7         List ids = new ArrayList();
 8         ids.add(1);
 9         ids.add(2);
10         ids.add(3);
11         map.put("ids",ids);
12 
13         List blogs = mapper.queryBlogForeach(map);
14 
15         System.out.println(blogs);
16 
17         session.close();
18     }

 

你可能感兴趣的:(8.Mybatis的动态SQL)