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 <!--namespace用于绑定一个对应的Dao/Mapper接口-->
 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<Blog> queryBlogIF(Map map);

BlogMapper.xml:

1 <select id="queryBlogIF" parameterType="map" resultType="blog">
2     select * from mybatis.blog where 1=1
3     <if test="title != null">
4         and title = #{title}
5     </if>
6     <if test="author != null">
7         and author = #{author}
8     </if>
9 </select>

测试代码:

 1 @Test
 2 public void queryBlogIFTest(){
 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.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 <select id="queryBlogIF" parameterType="map" resultType="blog">
 2     select * from mybatis.blog
 3     <where>
 4         <if test="title != null">
 5             and title = #{title}
 6         </if>
 7         <if test="author != null">
 8             and author = #{author}
 9         </if>
10     </where>
11 </select>

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

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

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

BlogMapper.java:

1 List<Blog> 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<String,String> map = new HashMap<String,String>();
 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         <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
5         <include refid="if-title-author"></include>
6         <!-- 在这里还可以引用其他的 sql 片段 -->
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表数据:

BlogMapper.java:

1 List<Blog> queryBlogForeach(Map map);

BlogMapper.xml:

 1 <select id="queryBlogForeach" parameterType="map" resultType="blog">
 2     select * from blog
 3     <where>
 4         <!--
 5         collection:指定输入对象中的集合属性
 6         item:每次遍历生成的对象
 7         open:开始遍历时的拼接字符串
 8         close:结束时拼接的字符串
 9         separator:遍历对象之间需要拼接的字符串
10         select * from blog where 1=1 and (id=1 or id=2 or id=3)
11       -->
12         <foreach collection="ids"  item="blogId" open="and (" close=")" separator="or">
13             id=#{blogId}
14         </foreach>
15     </where>
16 </select>

测试代码: map.put("ids",ids); 此处的ids和 <foreach collection="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<Integer> ids = new ArrayList<Integer>();
 8         ids.add(1);
 9         ids.add(2);
10         ids.add(3);
11         map.put("ids",ids);
12 
13         List<Blog> blogs = mapper.queryBlogForeach(map);
14 
15         System.out.println(blogs);
16 
17         session.close();
18     }
原文地址:https://www.cnblogs.com/zhihaospace/p/12301873.html