十二、动态SQL
什么是动态SQL:动态SQL是指 根据不同的条件生成不同的SQL语句
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
if
choose (when, otherwise)
trim (where, set)
foreach
搭建环境
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
创建一个基础工程
1、导包
2、编写配置文件
3、编写实体类
@Data
public class Blog {
private int id;
private String title;
private String author;
private Date createTime;
private int views;
}
4、编写实体类对应的Mapper接口 和 Mapper.xml文件
@SuppressWarnings("all")
抑制警告
1、IF
接口类
public interface BlogMapper {
// 查询博客
List<Blog> queryBlogIF(Map map);
}
Mapper.xml
where 1=1
是为了解决and
的问题,后面会使用
<where>
代替,会自动取消后续的and
<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>
测试类
@Test
public void queryBlogIF(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Java如此简单");
map.put("author", "狂神说2");
List<Blog> blog = mapper.queryBlogIF(map);
for(Blog blog1 : blog ){
System.out.println(blog1);
}
sqlSession.close();
}
2、choose (when, otherwise)
而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的
switch
语句。传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员挑选的 Blog)
<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>
3、trim (where, set)
WHERE
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,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>
通过自定义 trim 元素来定制 where 元素的功能
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
SET
set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
与 set 元素等价的自定义 trim 元素
<trim prefix="SET" suffixOverrides=",">
...
</trim>
所谓的动态 SQL ,本质上还是SQL语句,只是我们可以在SQL 层面 ,去执行一个逻辑代码。
4、SQL片段
有的时候我们可能需要将一些功能抽取出来,公用。
步骤
使用SQL标签抽取公共部分
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
在需要的地方使用Include 标签引用即可
<select id="queryBlogIF" parameterType="map" resultType="blog">
select * from blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
注意事项:
- 最好基于单表来定义
SQL
片段 - 不要存在
where
标签
forEach
<!-- 我们现在可以传一个万能的map,这个map中可以存在一个集合-->
<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 queryBlogForeach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap hashMap = new HashMap();
ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
hashMap.put("ids",ids);
mapper.queryBlogforeach(hashMap);
sqlSession.close();
}
动态SQL就是在拼接SQL语句,我们只要保证SQL 正确性,按照SQL的格式,去排列组合就可以了。
十三、缓存
1、介绍
查询 == > 连接数据库,消耗资源
一次查询的结果,给他暂存一个可以直接 取到的地方——内存:缓存
我们再次查询相同的数据时的时候,可以直接走缓存,就不用走数据库了。
什么是缓存
- 存在内存中的临时数据
- 将用户经常查询到的数据放到缓存(内存)中,用户直接去查询数据就不用在磁盘上(关系型数据库的数据文件)查询,直接从缓存中查询,从而提高了查询效率,解决了高并发系统的性能问题。
为什么要使用缓存
- 减少数据库的交互此时,减少系统开销,提高系统效率
什么样的数据能使用缓存
- 经常查询并且不会经常的改变的数据
2、一级缓存
- 一级缓存也叫本地缓存——
SqlSession
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存中拿,没有必要再去数据库查询。
测试步骤
- 开启日志
- 测试在一个 Sesion 中查询两次相同的记录
- 查看日志输出
**如下可以看到,查询同一条数据的时候,只需要打开一次 JDBC。 **
缓存失效的情况
- 增删改操作可能会改变 原来的数据,所以必定会刷新缓存!
- 查询不同的东西
- 查询不同的mapper.xml
- 手动清理缓存
sqlSession.clearCache();
小结
- 一级缓存默认是开启的,只在一次
SQLSession
中有效,也就是拿到连接到关闭连接内有效【一级缓存可以看成Map】 - 页面的每次刷新相当于 新建
SqlSession
3、二级缓存
- 二级缓存也叫做全局缓存,一级缓存作用域太低,所以诞生了二级缓存
- 基于namespace 级别的缓存,一个名称空间,对应一个二级缓存。
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果关闭了当前会话,这个会话对应的一级缓存也就没有了;但是我们打开二级缓存后,会话关闭了,一级缓存中的数据保存在二级缓存中;
- 新的会话查询数据,就可以从二级缓存中获取内容;
- 不同的mapper 查出的数据会放在自己对应的缓存(map)中
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<!--在当前 Mapper.xml 中使用 二级缓存-->
<cache/>
<!--可以关闭单个的缓存-->
<select id="queryUserById" resultType="user" useCache="false">
select * from user where id = #{id}
</select>
二级缓存参数配置
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
步骤
- 开启全局缓存【默认是开启的】
<setting name="cacheEnabled" value="true"/>
- 在要使用二级缓存的Mapper中开启
<!--在当前 Mapper.xml 中使用 二级缓存-->
<cache/>
也可以自定义参数
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
- 测试
问题:我需要将实体类 序列化,否则报错:
Caused by: java.io.NotSerializableException: com.lsw.pojo.User
// implements Serializable
public class User implements Serializable {
private int id;
private String name;
private String pwd;
}
小结:
- 只要开启了二级缓存,在同一个Mapper下就会有效
- 所有的数据都会先放在 一级缓存中
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中
4、mybatis缓存原理
缓存顺序
- 先看二级缓存中有没有数据
- 有数据——>直接取出数据
- 没有数据——>进入一级缓存中查看
- 再看一级缓存中有没有数据
- 有数据——取出数据
- 没有数据——>查找数据库
- 查找数据库
5、自定义缓存—ehcache
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存。
要在 程序中使用——导包
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
主流的缓存
Redis数据库来做缓存,非关系型数据库 K-V 键值对
mybatis 初步的学习就到这里了,如果你也想详细的学习 mybatis 框架,请到这里 狂神说Java