10、动态SQL

10.1 什么是动态SQL

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

官网描述:

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。

动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。


  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。

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

10.2 案例实现

搭建环境

新建一个数据库表:blog

字段:id,title,author,create_time,views

  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

1、创建Mybatis基础工程

2、实体类编写

  1. import java.util.Date;
  2. public class Blog {
  3. private String id;
  4. private String title;
  5. private String author;
  6. private Date createTime;
  7. private int views;
  8. //set
  9. //get
  10. }

3、编写Mapper接口及xml文件

  1. public interface BlogMapper {
  2. }
  3. <?xml version="1.0" encoding="UTF-8" ?>
  4. <!DOCTYPE mapper
  5. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  6. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  7. <mapper namespace="com.chen.mapper.BlogMapper">
  8. </mapper>

4、mybatis核心配置文件,下划线驼峰自动转换

  1. <settings>
  2. <setting name="mapUnderscoreToCamelCase" value="true"/>
  3. <setting name="logImpl" value="STDOUT_LOGGING"/>
  4. </settings>
  5. <!--注册Mapper.xml-->
  6. <mappers>
  7. <mapper resource="mapper/BlogMapper.xml"/>
  8. </mappers>

5、插入初始数据

编写接口

  1. //新增一个博客
  2. int addBlog(Blog blog);

sql配置文件

  1. <insert id="addBlog" parameterType="blog">
  2. insert into blog (id, title, author, create_time, views)
  3. values (#{id},#{title},#{author},#{createTime},#{views});
  4. </insert>

初始化博客方法

  1. @Test
  2. public void addInitBlog(){
  3. SqlSession session = MybatisUtils.getSession();
  4. BlogMapper mapper = session.getMapper(BlogMapper.class);
  5. Blog blog = new Blog();
  6. blog.setId(IDUtil.genId());
  7. blog.setTitle("Mybatis");
  8. blog.setAuthor("黑马程序员");
  9. blog.setCreateTime(new Date());
  10. blog.setViews(20);
  11. mapper.addBlog(blog);
  12. blog.setId(IDUtil.genId());
  13. blog.setTitle("Java");
  14. mapper.addBlog(blog);
  15. blog.setId(IDUtil.genId());
  16. blog.setTitle("Spring");
  17. mapper.addBlog(blog);
  18. blog.setId(IDUtil.genId());
  19. blog.setTitle("微服务");
  20. mapper.addBlog(blog);
  21. session.close();
  22. }

初始化数据完毕

10.2.1 if 语句

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

1、编写接口类

  1. //需求1
  2. List<Blog> queryBlogIf(Map map);

2、编写SQL语句

  1. <!--需求1:
  2. 根据作者名字和博客名字来查询博客!
  3. 如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
  4. select * from blog where title = #{title} and author = #{author}
  5. -->
  6. <select id="queryBlogIf" parameterType="map" resultType="blog">
  7. select * from blog where
  8. <if test="title != null">
  9. title = #{title}
  10. </if>
  11. <if test="author != null">
  12. and author = #{author}
  13. </if>
  14. </select>

3、测试

  1. @Test
  2. public void testQueryBlogIf(){
  3. SqlSession session = MybatisUtils.getSession();
  4. BlogMapper mapper = session.getMapper(BlogMapper.class);
  5. HashMap<String, String> map = new HashMap<String, String>();
  6. map.put("title","Mybatis");
  7. map.put("author","黑马程序员");
  8. List<Blog> blogs = mapper.queryBlogIf(map);
  9. System.out.println(blogs);
  10. session.close();
  11. }

这样写我们可以看到,如果 author 等于 null,那么查询语句为 select * from user where title=#{title},但是如果title为空呢?那么查询语句为 select * from user where and author=#{author},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句!

10.2.2 Where

修改上面的SQL语句;

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

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

10.2.3 Set

同理,上面的对于查询 SQL 语句包含 where 关键字,如果在进行更新操作的时候,含有 set 关键词,我们怎么处理呢?

1、编写接口方法

  1. int updateBlog(Map map);

2、sql配置文件

  1. <!--注意set是用的逗号隔开-->
  2. <update id="updateBlog" parameterType="map">
  3. update blog
  4. <set>
  5. <if test="title != null">
  6. title = #{title},
  7. </if>
  8. <if test="author != null">
  9. author = #{author}
  10. </if>
  11. </set>
  12. where id = #{id};
  13. </update>

3、测试

  1. @Test
  2. public void testUpdateBlog(){
  3. SqlSession session = MybatisUtils.getSession();
  4. BlogMapper mapper = session.getMapper(BlogMapper.class);
  5. HashMap<String, String> map = new HashMap<String, String>();
  6. map.put("title","动态SQL");
  7. map.put("author","秦疆");
  8. map.put("id","9d6a763f5e1347cebda43e2a32687a77");
  9. mapper.updateBlog(map);
  10. session.close();
  11. }

10.2.4 choose语句

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句

1、编写接口方法

  1. List<Blog> queryBlogChoose(Map map);

2、sql配置文件

  1. <select id="queryBlogChoose" parameterType="map" resultType="blog">
  2. select * from 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>

3、测试类

  1. @Test
  2. public void testQueryBlogChoose(){
  3. SqlSession session = MybatisUtils.getSession();
  4. BlogMapper mapper = session.getMapper(BlogMapper.class);
  5. HashMap<String, Object> map = new HashMap<String, Object>();
  6. map.put("title","Java如此简单");
  7. map.put("author","狂神说");
  8. map.put("views",9999);
  9. List<Blog> blogs = mapper.queryBlogChoose(map);
  10. System.out.println(blogs);
  11. session.close();
  12. }

10.2.5 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 blog
  3. <where>
  4. <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
  5. <include refid="if-title-author"></include>
  6. <!-- 在这里还可以引用其他的 sql 片段 -->
  7. </where>
  8. </select>

注意:

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

②、在 sql 片段中不要包括 where

10.2.6 Foreach

将数据库中前三个数据的id修改为1,2,3;

需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息

1、编写接口

  1. List<Blog> queryBlogForeach(Map map);

2、编写SQL语句

  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="id" open="and (" close=")" separator="or">
  13. id=#{id}
  14. </foreach>
  15. </where>
  16. </select>

3、测试

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

小结:其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。

11、缓存

11.1 简介

1、什么是缓存 [ Cache ]?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2、为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

3、什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

11.2 Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存

    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

11.3 一级缓存

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

11.4 一级缓存失效的四种情况

一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;

一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!

每个sqlSession中的缓存相互独立

当前缓存中,不存在这个数据

因为增删改操作可能会对当前数据产生影响

11.5 二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

11.6 使用步骤

1、开启全局缓存 【mybatis-config.xml】

  1. <setting name="cacheEnabled" value="true"/>

2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】

  1. <cache/>
  2. 官方示例=====>查看官方文档
  3. <cache
  4. eviction="FIFO"
  5. flushInterval="60000"
  6. size="512"
  7. readOnly="true"/>
  8. 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

结论

  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中
  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

MyBatis学习06(动态SQL和缓存)的更多相关文章

  1. mybatis学习 十 动态 SQL

    1.  根据方法传入的参数不同执行不同的 SQL 命令.称为动态 SQL, MyBatis 中动态 SQL 就是在 mapper.xml 中添加逻辑判断等. 2. <if>标签 <s ...

  2. mybatis学习之动态sql

    mybatis的动态sql语句很强大,在mapper映射文件中使用简单的标签即可实现该效果,下面一个个记录: 1.select查询 简单的select类似如下: <select id=" ...

  3. mybatis 学习五 动态SQL语句

    3.1 selectKey 标签 在insert语句中,在Oracle经常使用序列.在MySQL中使用函数来自动生成插入表的主键,而且需要方法能返回这个生成主键.使用myBatis的selectKey ...

  4. Mybatis学习笔记-动态SQL

    概念 根据不同环境生成不同SQL语句,摆脱SQL语句拼接的烦恼[doge] 本质:SQL语句的拼接 环境搭建 搭建数据库 CREATE TABLE `blog`( `id` VARCHAR(50) N ...

  5. MyBatis框架——动态SQL、缓存机制、逆向工程

    MyBatis框架--动态SQL.缓存机制.逆向工程 一.Dynamic SQL 为什么需要动态SQL?有时候需要根据实际传入的参数来动态的拼接SQL语句.最常用的就是:where和if标签 1.参考 ...

  6. MyBatis动态SQL和缓存

    1. 什么是动态SQL 静态SQL:静态SQL语句在程序运行前SQL语句必须是确定的,SQL语句中涉及的表的字段名必须是存在的,静态SQL的编译是在程序运行前的. 动态SQL:动态SQL语句是在程序运 ...

  7. 【mybatis深度历险系列】mybatis中的动态sql

    最近一直做项目,博文很长时间没有更新了,今天抽空,学习了一下mybatis,并且总结一下.在前面的博文中,小编主要简单的介绍了mybatis中的输入和输出映射,并且通过demo简单的介绍了输入映射和输 ...

  8. Mybatis入门之动态sql

    Mybatis入门之动态sql 通过mybatis提供的各种标签方法实现动态拼接sql. 1.if.where.sql.include标签(条件.sql片段) <sql id="sel ...

  9. mybatis 详解------动态SQL

    mybatis 详解------动态SQL   目录 1.动态SQL:if 语句 2.动态SQL:if+where 语句 3.动态SQL:if+set 语句 4.动态SQL:choose(when,o ...

  10. mybatis中的动态SQL

    在实际开发中,数据库的查询很难一蹴而就,我们往往要根据各种不同的场景拼接出不同的SQL语句,这无疑是一项复杂的工作,我们在使用mybatis时,mybatis给我们提供了动态SQL,可以让我们根据具体 ...

随机推荐

  1. csu-acm 1503: 点到圆弧的距离

    1503: 点到圆弧的距离 分析: 先判断点和圆心的连线是否在圆弧范围内,如果在,最短距离即到圆心的距离减去半径的绝对值:反之,为到端点的最短距离. 具体看注释 #include <bits/s ...

  2. Java Set HashSet

    import java.util.HashSet; import java.util.Set; /** Set存储特点:数据无序.不可重复 Set接口的实现类: HashSet:Set接口的主要实现类 ...

  3. springCloud学习05之api网关服务zuul过滤器filter

    前面学习了zuul的反向代理.负载均衡.fallback回退.这张学习写过滤器filter,做java web开发的对filter都不陌生,那就是客户端(如浏览器)发起请求的时候,都先经过过滤器fil ...

  4. HTTP返回状态码及错误大全

    http://www.kaiyuanba.cn/html/1/131/226/4258.htm HTTP 400 - 请求无效 HTTP 401.1 - 未授权:登录失败 HTTP 401.2 - 未 ...

  5. scrapy-redis的搭建 分布式爬虫 去重

    master:一.spider文件1.需要更改继承的类from scrapy_redis.spiders import RedisSpider 2.注释掉start_urls 3.在爬虫目录下新创建一 ...

  6. 「SDOI2016」数字配对

    「SDOI2016」数字配对 题目大意 传送门 题解 \(a_i\) 是 \(a_j\) 的倍数,且 \(\frac{a_i}{a_j}\) 是一个质数,则将 \(a_i,a_j\) 质因数分解后,其 ...

  7. JM操作数据库

    [前言] 为什么要去直连数据库,去操作数据库? 因为在我们做自动化的时候,或者在大批量准备数据的时候,自动化的时候有时候会生成很多条页面上,接口上无法删除的数据,那么就很有很多的测试数据遗留在系统上, ...

  8. 关于高校表白App的NABCD项目分析

    N(Need,需求) 首先,针对本校男多女少 的具体情况,为广大本校大学生提供一个更加宽广的平台: 其次,针对当前各高校均有校园表白墙的实际情况,各表白墙难以整合在一起,使得信息不够集中的现状,我们小 ...

  9. Scala学习——简介

    一.Scala简介 Scala 是 Scalable Language 的简写,是一门多范式的编程语言,设计初衷是实现可伸缩的语言并集成面向对象编程和函数式编程的各种特性. 二.Scala 环境搭建 ...

  10. java并发编程基础——线程池

    线程池 由于启动一个线程要与操作系统交互,所以系统启动一个新的线程的成本是比较高的.在这种情况下,使用线程池可以很好的提升性能,特别是程序中涉及创建大量生命周期很短暂的线程时. 与数据库连接池类似,线 ...