转载: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 ListqueryBlogIF(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 Mapmap = 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 ListqueryBlogChoose(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 Mapmap = 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表数据:
BlogMapper.java:
1 ListqueryBlogForeach(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 Listids = 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 }