缓存顺序(缓存原理):
1.先看二级缓存中有没有,有就返回,没有进行第二步;
2.再看一级缓存中有没有,有就返回,没有就进行第三步;
3.查询数据库,并把这次查询的结果保存在一级缓存(此时是一个新的sqlSession的一级缓存已默认开启了)。
一级缓存:
一级缓存也叫本地缓存(SqlSession级别的缓存):
与数据库同一次会话期间查询到的数据会放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。
注:一级缓存默认是开启的,只在一次sqlSession中有效,也就是拿到连接getSqlSession()到关闭连接sqlSession.close()这个区间段!
测试步骤:
1.开启日志!
2.测试在一个Session中通过查询相同的id来查询两次记录。
@Test
public void queryUserById() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
System.out.println("=================");
User user2 = userMapper.queryUserById(1);
System.out.println(user2);
System.out.println(user == user2);
//关闭sqlSession
sqlSession.close();
}
3.查看日志输出(输出true,一级缓存只在查询相同东西时有用。一级缓存相当于一个Map,从Map中取数据时Map有用,不取数据时Map就没用了。)
缓存失效的情况:
1.映射语句文件中的所有 select 语句的结果将会被缓存(查询不同的东西会刷新缓存使原缓存失效)。
例,查询两次不同id的用户(输出false):
@Test
public void queryUserById() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
System.out.println("=================");
User user2 = userMapper.queryUserById(2);
System.out.println(user2);
System.out.println(user == user2);
//关闭sqlSession
sqlSession.close();
}
2.映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存(增删改操作,可能会改变原来的数据,所以必定会刷新缓存,使原缓存失效!)。
例,更新用户(更新四号用户的信息,但一号用户的缓存也被刷新了。)
@Test
public void queryUserById() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
userMapper.updateUser(new User(4,"哈哈","123333"));
System.out.println("=================");
User user2 = userMapper.queryUserById(1);
System.out.println(user2);
System.out.println(user == user2);
//关闭sqlSession
sqlSession.close();
}
3.在同一个Mapper.xml文件中同一个接口下使用不同的Mapper对象进行查询。
@Test
public void queryUserById() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
SqlSession sqlSession2 = MyBatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
sqlSession.close();
User user2 = userMapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
//关闭sqlSession
sqlSession2.close();
}
4.手动清理缓存(一级缓存默认是开启的,只在一次sqlSession中有效,也就是拿到连接getSqlSession()到关闭连接sqlSession.close()这个区间段!)。
@Test
public void queryUserById() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
//userMapper.updateUser(new User(4,"哈哈","123333"));
//手动清理缓存
sqlSession.clearCache();
System.out.println("=================");
User user2 = userMapper.queryUserById(1);
System.out.println(user2);
System.out.println(user == user2);
//关闭sqlSession
sqlSession.close();
}
5.缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
6.缓存不会定时进行刷新(也就是说,没有刷新间隔)。
二级缓存:
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。
基于namespace级别的缓存,一个名称空间,对应一个二级缓存。
工作机制:
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
新的会话查询信息,就可以从二级缓存中获取内容;
不同的mapper查出的数据会放在自己对应的缓存(map)中。
步骤:
1.开启全局缓存
2.在使用二级缓存的Mapper.xml文件中开启二级缓存。
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
3.测试(使用不同的UserMapper对象进行查询相同id的东西,在第一个查询的sqlSession关闭后,一级缓存就没了,但由于已开启了二级缓存,所以第一次查询的内容被放在了二级缓存中,这时再用第二个UserMapper对象从二级缓存中进行查询,与上一次查询的东西相同,输出结果为true;此时若在一级缓存已关掉且第二次查询的id与第一次查询的id不同的情况下,第二次查询的东西在二级缓存中是没有的,只能去数据库拿,此时去数据库拿取并且再开启一个新的sqlSession的一级缓存。)
@Test
public void queryUserById() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
SqlSession sqlSession2 = MyBatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
sqlSession.close();
User user2 = userMapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
//关闭sqlSession
sqlSession2.close();
}
当在xml文件中仅使用语句开启二级缓存时遇到的实体类未序列化异常问题的解决方案:将实体类序列化。
package com.kuang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class User implements Serializable {
private int id;
private String name;
private String pwd;
}
注:二级缓存只针对同一个namespace(即同一个接口)下管用。
小结:
1.只要开启了二级缓存,在同一个接口下就有效。
2.所有的数据都会先放在一级缓存中;
只有当会话提交,或者关闭的时候,才会提交到二级缓存中!