12、动态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、编写实体类
- 4、编写实体类对应的Mapper接口和Mapper.xml文件
1、
2、
3、导lombok包
![
。](https://my-bucket-pics.oss-cn-qingdao.aliyuncs.com/img/20201213155852.png)
4、实体类
@Data
public class Blog {
private int id;
private String title;
private String author;
private Date createTime;
private int views;
}
5、插入数据
mapper接口
public interface BlogMapper {
//插入数据
int addBolg(Blog blog);
}
xml配置
<!--configuration核心配置文件-->
<mapper namespace="com.nine.dao.BlogMapper">
<insert id="addBlog" parameterType="blog">
insert into mybatis.blog (id,title,author,create_time,views)
values (#{id},#{title},#{author},#{createTime},#{views});
</insert>
</mapper>
6、编写测试类
IF语句
Mapper接口
//if条件查询
List<Blog> queryBlog(Map map);
xml配置文件
<select id="queryBlog" parameterType="map" resultType="blog">
select * from mybatis.blog where 1=1
<if test="title!=null">
and title = #{title}
</if>
<if test="author!=null">
and author = #{author}
</if>
</select>
测试
@Test
public void test3(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
//map.put("title","学习Spring");
List<Blog> blogs = mapper.queryBlog(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
首次查询,未对map进行添加
Preparing: select * from mybatis.blog where 1=1
==> Parameters:
<== Columns: id, title, author, create_time, views
<== Row: 9072dc4a181c4c0d8c2540940b587c6b, 学习mybatis, nine, 2020-12-13 13:31:11.0, 9999
<== Row: ab28072d9af6486f8f0ae1ce58a94e99, mybatis第二部, nine, 2020-12-13 13:31:11.0, 9999
<== Row: 0db743d5b1654b989f1a5cf7ba721877, 学习Spring, nine, 2020-12-13 13:31:11.0, 9999
<== Row: 481d794e121d4310af0c02405a3618b6, 学习数据结构, nine, 2020-12-13 13:31:11.0, 9999
<== Total: 4
Blog(id=9072dc4a181c4c0d8c2540940b587c6b, title=学习mybatis, author=nine, createTime=Sun Dec 13 13:31:11 CST 2020, views=9999)
Blog(id=ab28072d9af6486f8f0ae1ce58a94e99, title=mybatis第二部, author=nine, createTime=Sun Dec 13 13:31:11 CST 2020, views=9999)
Blog(id=0db743d5b1654b989f1a5cf7ba721877, title=学习Spring, author=nine, createTime=Sun Dec 13 13:31:11 CST 2020, views=9999)
Blog(id=481d794e121d4310af0c02405a3618b6, title=学习数据结构, author=nine, createTime=Sun Dec 13 13:31:11 CST 2020, views=9999)
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@64c87930]
Returned connection 1690859824 to pool.
对map的title进行筛选
Preparing: select * from mybatis.blog where 1=1 and title = ?
==> Parameters: 学习Spring(String)
<== Columns: id, title, author, create_time, views
<== Row: 0db743d5b1654b989f1a5cf7ba721877, 学习Spring, nine, 2020-12-13 13:31:11.0, 9999
<== Total: 1
Blog(id=0db743d5b1654b989f1a5cf7ba721877, title=学习Spring, author=nine, createTime=Sun Dec 13 13:31:11 CST 2020, views=9999)
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@64c87930]
Returned connection 1690859824 to pool.
用where对sql进行改进
<select id="queryBlog" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<if test="title!=null">
and title = #{title}
</if>
<if test="author!=null">
and author = #{author}
</if>
</where>
</select>
choose语句 (when, otherwise)
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.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>
trim语句(where,set)
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
<where>
<if test="title!=null">
and title = #{title}
</if>
<if test="author!=null">
and author = #{author}
</if>
</where>
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title !=null">
title = #{title},
</if>
<if test="author !=null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="" prefixOverrides="" suffix="" suffixOverrides="">
</trim>
所谓的动态SQL,本质还是SQL语句,只是我们我们可以在SQL层面,去执行一个逻辑代码
SQL片段
有的时候,我们可能会将一些功能的部分抽取出来,方便复用!
1、使用SQL标签抽取公共的部分
<sql id="if-title-author">
<if test="title !=null">
title = #{title},
</if>
<if test="author !=null">
author = #{author}
</if>
</sql>
2、在需要使用的地方使用include标签即可
<select id="queryBlog" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
注意事项:
-
最好基于单表来定义SQL片段!
-
不要存在where标签
Foreach
select * from user where 1=1 and
<foreach item="item" index="index" collection="list"
open="(" separator="or" close=")">
#{item}
</foreach>
(id=1 or id=2 or id=3)
动态SQL就是在拼接语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了。
建议:
- 先在Mysql中写出完整的SQL,再在对应的去修改成为我们的动态SQL实现通用即可
13、缓存
13.1 简介
1.什么是缓存【Cache】?
- 存在内存中临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
2.为什么使用缓存?
- 减少和数据库交互的次数,减少系统开销,提高系统效率。
3.什么样的数据能够使用缓存?
- 经常查询并且不经常改变的数据。
13.2 Mybatis缓存
- Mybatis包含一个非常强大的查询缓存特性,它可以非常方便地订制和配置缓存,缓存可以极大提升开发效率。
- Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存
13.3 一级缓存
- 一级缓存也叫本地缓存:SqlSession
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
测试步骤:
1、开启日志
2、测试在一个Session中查询俩次(相同的)记录
3、查看日志输出
测试代码:
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User idUser = mapper.getIdUser(1);
System.out.println(idUser);
System.out.println("----------");
User idUser1 = mapper.getIdUser(1);
System.out.println(idUser1);
System.out.println(idUser==idUser1);
sqlSession.close();
}
缓存失效的情况:
1、查询不同的东西
2、增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
3、查询不同的Mapper.xml
4、手动清理缓存
小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段
13.4 二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存中;
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
步骤:
1、开启全局缓存
<!--显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
2、在要使用二级缓存的Mapper中开启
<!--在当前Mapper.xml中使用二级缓存-->
<cache/>
也可以自定义参数
<!--在当前Mapper.xml中使用二级缓存-->
<!--FIFO:先进先出-->
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnle"
/>
小结:
- 只要开启了二级缓存,在用一个Mapper下就有效;
- 所有的数据都会先放在一级缓存中;
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中!
13.5 缓存原理
用户–》二级缓存—》一级缓存—》数据库
13.6 自定义缓存-ehcache
Ehcache是一种广泛使用的开源Java分布式缓存,主要面向通用缓存
用到Ehcache要先导包
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>