mybatis动态sql和缓存 哇 咔 咔 : textcolor{blue}{哇咔咔:} 哇咔咔: 古 娜 拉 黑 暗 之 神 , 请 赐 予 我 力 量 , 让 我 起 个 床 textcolor{green}{古娜拉黑暗之神,请赐予我力量,让我起个床} 古娜拉黑暗之神,请赐予我力量,让我起个床
gitee中MyBatis学习源码所用到的代码都可以在这里找到
- 十二、动态SQL
- 1. 搭建环境
- 2. 创建一个基础工程
- 2.1导包
- 2.2 编写配置文件
- 2.3 编写实体类
- 2.4 编写实体类对应的Mapper接口和Mapper.xml文件
- 2.5 生成UUID的工具类
- 2.6 测试加入数据
- 3. if
- 4. trim(where,set)
- 4.1 where
- 4.2 set
- 5. choose(when,otherwise)
- 6. foreach
- 使用
- 7. SQL片段
- 8. 总结
- 十三、缓存
- 1. 简介
- 2. Mybatis缓存
- 3. 一级缓存
- 3.1 测试步骤:
- 3.2 缓存失效得情况
- 3.3 小结
- 4. 二级缓存
- 4.1 简介
- 4.2 开启步骤
- 4.3 测试
- 4.4 小结
- 5. 缓存原理
- 6. 自定义缓存-Ehcache
mybatis中的sql
什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。
1. 搭建环境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=utf82. 创建一个基础工程 2.1导包
2.2 编写配置文件org.projectlombok lombok 1.18.20
mybaits-config.xml和db.properties
2.3 编写实体类import lombok.Data;
import java.util.Date;
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;//属性名和字段名不一致
private int views;
}
2.4 编写实体类对应的Mapper接口和Mapper.xml文件
BlogMapper和BlogMapper.xml
2.5 生成UUID的工具类
import java.util.UUID;
@SuppressWarnings("all")//抑制警告
public class IDutils {
public static String getId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
}
2.6 测试加入数据
public interface BlogMapper {
//插入数据
int addBlog(Blog blog);
}
insert into blog (id,title,author,create_time,views) values (#{id},#{title},#{author},#{createTime},#{views});
import com.hxl.dao.BlogMapper;
import com.hxl.pojo.Blog;
import com.hxl.utils.IDutils;
import com.hxl.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.Date;
public class MyTest {
@Test
public void addInitBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDutils.getId());
blog.setTitle("Mybatis");
blog.setAuthor("王木木");
blog.setCreateTime(new Date());
blog.setViews(9999);
mapper.addBlog(blog);
blog.setId(IDutils.getId());
blog.setTitle("Java");
mapper.addBlog(blog);
blog.setId(IDutils.getId());
blog.setTitle("Spring");
mapper.addBlog(blog);
blog.setId(IDutils.getId());
blog.setTitle("微服务");
mapper.addBlog(blog);
sqlSession.close();
}
}
-
出现了问题
他说是无效绑定。
这里需要绑定,但是我是绑定了的,所以又进行了下面的操作
-
在pom文件下加入下面的话既可以解决,加入后记得刷新maven
-
src/main/resources ***.xml true src/main/java ***.xml src/main/resources true org.apache.maven.plugins maven-resources-plugin UTF-8
-
-
我觉得出现上述的问题是因为我的BlogMapper.xml是放在src/main/java/com/hxl/dao下的。而不是像上面的那个放在resources下的。所以出现了那个问题。
ListqueryBlogIF(Map map);
@Test
public void queryBlogIF(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Java");
List blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
4. trim(where,set)
4.1 where
-
where元素只会在至少有一个子元素的条件返回SQL子句的情况下采取插入“WHERe”自子句,而且,若语句的开头为“AND“或”OR“,where元素也会将它们去除。
- 我们很容易看到,之前提到的if是用了一个where 1=1.其实这个是不合适的。但是现在加了个标签,它可以自动识别是不是第一个where,然后在判断是否加上and。
- where的存在会判断and是不是需要,第二个前面及以后要加上and。
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
...
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
4.2 set- set元素会动态前置SET关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的SQL语句的后面留下这些逗号
update blog where id = #{id} title = #{title}, author = #{author},
@Test
public void updateBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("id","ac040b29e3b047a09a4fe78797193597");
map.put("author","王木木");
map.put("title","Java1");
mapper.updateBlog(map);
sqlSession.close();
}
来看看与 set 元素等价的自定义 trim 元素吧:
...
注意,我们覆盖了后缀值设置,并且自定义了前缀值。
5. choose(when,otherwise)这个choose有点像switch,case。他只能运行一个。比如说前面的都不符合,那他必须有一个views要符合,否则会报错。前面的按照顺序有一个符合了,那他就跳出去了。
6. foreachselect * from user where 1=1 and使用(id=1 or id=2 or id=3)
//查询第1-2-3号记录的博客 ListqueryBlogForeach(Map map);
select * from blog id = #{id}
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
//这里因为是uuid是String,如果是1,2这种就用Integer
ArrayList ids = new ArrayList();
map.put("ids",ids);
List blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
我们还可以进行下面的测试
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList ids = new ArrayList();
ids.add("ac040b29e3b047a09a4fe78797193597");
map.put("ids",ids);
List blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList ids = new ArrayList();
ids.add("ac040b29e3b047a09a4fe78797193597");
ids.add("a46a7874d69f4d69b9572816d8f30ab7");
map.put("ids",ids);
List blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
7. SQL片段
有时候我们会将一些功能的部分抽取出来,方便复用
- 使用SQL标签抽取公共的部分。
-
在需要使用的地方使用include标签引用即可
我们可以加一个sql标签,id可以随意取,然后下面来一个include refid是引用id,和上面的id是一个。这样就可以实现复用。
title = #{title} and author = #{author} -
注意事项:
- 最好基于单表来定义SQL片段
- 不要存在where标签
所谓的动态SQL,本质还是SQL语句,只是我们可以在sql层面,去执行一个逻辑代码。
if,where,set,choose,when
动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就ok了
建议:
- 先在Mysql中写出完整的SQL,再对应的去修改成为我们的动态SQL。实现通用。
查询-->需要连接数据库,耗资源 一次查询的结果,给他暂存一个可以直接取到的地方-->内存:缓存 我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
- 什么是缓存[Cache]?
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
- 为什么要使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率
- 什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据
- Mybatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大地提升查询效率。
- Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(Sqlsession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,是基于namespace级别的缓存
- 为了提高扩展性,mybatis定义了缓存接口,我们可以通过实现Cache接口来自定义二级缓存。
- 一级缓存也叫本地缓存:SqlSession
- 与数据库同一次会话期间查询到得数据会放在本地缓存中
- 以后如果需要获取相同得数据,直接从换从中拿,没必要再去查询数据库
-
开启日志
在核心配置文件中
-
测试再一个Session中查询两次相同得记录
@Test public void queryUserByIdTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); System.out.println("========"); User user1 = mapper.queryUserById(1); System.out.println(user1); sqlSession.close(); } -
查看日志输出
-
查询不同得东西
-
增删改操作可能会改变原来得数据,所以必定会刷新缓存
- 比如说在查两个相同的用户,中间插一个修改用户的操作
-
查询不同Mapper.xml
-
手动清理缓存
-
sqlSession.clearCache();//手动清理缓存
-
一级缓存默认是开启的,只在一次sqlSession中有效,也就是拿到连接到关闭连接这个区间
一级缓存就相当于一个map
4. 二级缓存 4.1 简介 4.2 开启步骤-
开启全局缓存
虽然默认就是true但是还是需要显示的开启一下,增强可读性
在核心配置文件中
-
要启用二级缓存需要在sql映射文件中添加一行代码
也可以自定义一些参数
还可以在你想要的地方关闭缓存,只需要在sql语句标签下添加下面的代码
-
如果没有加二级缓存,查询两个sqlSession是会进入两次数据库的
@Test public void queryUserByIdTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); SqlSession sqlSession2 = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user1 = mapper2.queryUserById(1); System.out.println(user1); sqlSession.close(); sqlSession2.close(); } -
如果开启了二级缓存
@Test public void queryUserByIdTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); SqlSession sqlSession2 = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); sqlSession.close(); UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); User user1 = mapper2.queryUserById(1); System.out.println(user1); System.out.println(user == user1); sqlSession2.close(); } -
问题:
我们需要将实体类序列化,否则就会报错
Caused by:java.io.NotSerializableException:com.hxl.pojo.User
我们需要在实体类后加上implements Serializable
@Data public class User implements Serializable { private int id; private String name; private String pwd; }
- 只要开启了二级缓存,在同一个Mapper下就有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中
- 实体类要序列化
Ehcache是一种广泛使用的开源Java分布式缓存,主要面向通用缓存
要在程序中使用Ehcache,先要导包
org.mybatis.caches mybatis-ehcache 1.2.1
在resources下建立一个ehcache.xml文件
在需要的Mapper.xml下
测试



