Mybatis 缓存
一、一级缓存
测试一
- 测试:
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//第一次查询
List<User> users = sqlSession.selectList("com.example.mapper.UserMapper.findAll");
users.forEach(user -> {
System.out.println(user);
});
System.out.println("=============================");
//第二次查询
List<User> users2 = sqlSession.selectList("com.example.mapper.UserMapper.findAll");
users2.forEach(user -> {
System.out.println(user);
});
}
- 查看sql日志
13:23:58,390 DEBUG findAll:159 - ==> Preparing: select * from user
13:23:58,419 DEBUG findAll:159 - ==> Parameters:
13:23:58,443 DEBUG findAll:159 - <== Total: 5
User{id=1, username='lucy'}
User{id=2, username='tom'}
User{id=3, username=''}
User{id=5, username='555'}
User{id=6, username='666'}
=============================
User{id=1, username='lucy'}
User{id=2, username='tom'}
User{id=3, username=''}
User{id=5, username='555'}
User{id=6, username='666'}
测试二
1.代码:
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//第一次查询
List<User> users = sqlSession.selectList("com.example.mapper.UserMapper.findAll");
users.forEach(user -> {
System.out.println(user);
});
修改其中一个用户信息的username
User user = new User();
user.setId(3);
user.setUsername("ceshi");
sqlSession.update("com.example.mapper.UserMapper.update", user);
System.out.println("=============================");
//第二次查询
List<User> users2 = sqlSession.selectList("com.example.mapper.UserMapper.findAll");
users2.forEach(user2 -> {
System.out.println(user2);
});
}
2.查看sql日志
13:34:32,869 DEBUG findAll:159 - ==> Preparing: select * from user
13:34:32,895 DEBUG findAll:159 - ==> Parameters:
13:34:32,921 DEBUG findAll:159 - <== Total: 5
User{id=1, username='lucy'}
User{id=2, username='tom'}
User{id=3, username=''}
User{id=5, username='555'}
User{id=6, username='666'}
13:34:32,924 DEBUG update:159 - ==> Preparing: update user set username=? where id=?
13:34:32,925 DEBUG update:159 - ==> Parameters: ceshi(String), 3(Integer)
13:34:32,934 DEBUG update:159 - <== Updates: 1
=============================
13:34:32,934 DEBUG findAll:159 - ==> Preparing: select * from user
13:34:32,934 DEBUG findAll:159 - ==> Parameters:
13:34:32,936 DEBUG findAll:159 - <== Total: 5
User{id=1, username='lucy'}
User{id=2, username='tom'}
User{id=3, username='ceshi'}
User{id=5, username='555'}
User{id=6, username='666'}
结论
1.第一次查询所有用户的信息,先去缓存中查找,如果缓存中没有,查询数据库,将查询的结果放在一级缓存中。
2.如果没有进行增删改操作,第二次查询时,会直接查询一级缓存,得到结果,不去查询数据库。
3.如果有进行增删改操作,会清空一级缓存,避免脏读。
一级缓存源码解析
- 查看org.apache.ibatis.session.SqlSession中的查询方法,以selectList为例
- 查看selectList的实现类,默认实现类为DefaultSqlSession
- 依次跟踪
- 查看Executor实现类 BaseExecutor中的query方法
- query方法中,会根据当前MappedStatement、参数、分页、结果集去创建一个缓存key,然后继续查看重载方法
- 查看查询方法
- 查看查询数据库方法
####### 一级缓存存储结构
二级缓存
二级缓存的原理和一级缓存原理一样,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去
缓存中取。但是一级缓存是基于sqlSession的,而二级缓存是基于mapper文件的namespace的,也 就
是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace 相
同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中
如何开启二级缓存
- mybatis核心配置文件 默认是开启的 可以不配置
<!--开启全局的二级缓存配置-->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
- mapper.xml
- 针对整个mapper 添加
<cache></cache>
标签开启缓存<cache>标签属性 eviction:缓存的回收策略 LRU - 最近最少使用,移除最长时间不被使用的对象 FIFO - 先进先出,按对象进入缓存的顺序来移除它们 SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象 WEAK - 弱引用,更积极地移除基于垃圾收集器和弱引用规则的对象 默认的是LRU
- 针对整个mapper 添加
flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读
true:只读:mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
mybatis为了加快获取数据,直接就会将数据在缓存中的引用交给用户 。不安全,速度快
false:读写(默认):mybatis觉得获取的数据可能会被修改
mybatis会利用序列化&反序列化的技术克隆一份新的数据给你。安全,速度相对慢
size:缓存存放多少个元素
type:指定自定义缓存的全类名(实现Cache接口即可) 默认实现类PerpetualCache
 2. 针对单个标签 在变迁中添加属性
useCache="true" ```开启缓存
3. 注意事项
开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操
作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中,如果我们要再取
这个缓存的话,就需要反序列化了。所以mybatis中的pojo都去实现Serializable接口
测试一:
测试代码:
@Test
public void test5() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession1 = factory.openSession();
SqlSession sqlSession2 = factory.openSession();
User user1 = sqlSession1.selectOne("com.example.mapper.UserMapper.findById", 1);
System.out.println(user1);
sqlSession1.commit();//必须要commit
User user2 = sqlSession2.selectOne("com.example.mapper.UserMapper.findById", 1);
System.out.println(user2);
}
查看sql日志
//第一次查询 先查询二级缓存, 缓存中不存在,查询数据库
10:03:22,572 DEBUG UserMapper:62 - Cache Hit Ratio [com.example.mapper.UserMapper]: 0.0
10:03:22,577 DEBUG JdbcTransaction:137 - Opening JDBC Connection
10:03:22,711 DEBUG PooledDataSource:406 - Created connection 2021707251.
10:03:22,711 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7880cdf3]
10:03:22,713 DEBUG findById:159 - ==> Preparing: select * from user where id = ?
10:03:22,735 DEBUG findById:159 - ==> Parameters: 1(Integer)
10:03:22,760 DEBUG findById:159 - <== Total: 1
//查询结果:
User{id=1, username='lucy'}
//第二次查询 0.5表示 两次查询,在缓存中查询到一次
10:03:22,769 DEBUG UserMapper:62 - Cache Hit Ratio [com.example.mapper.UserMapper]: 0.5
User{id=1, username='lucy'}
测试二:
测试代码:
@Test
public void test5() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession1 = factory.openSession();
SqlSession sqlSession2 = factory.openSession();
User user1 = sqlSession1.selectOne("com.example.mapper.UserMapper.findById", 1);
System.out.println(user1);
sqlSession1.commit();
User user = new User();
user.setId(1);
user.setUsername("jack");
// 增删改会清空二级缓存
sqlSession1.update("com.example.mapper.UserMapper.update",user);
sqlSession1.commit();
User user2 = sqlSession2.selectOne("com.example.mapper.UserMapper.findById", 1);
System.out.println(user2);
}
查看sql日志
10:15:21,727 DEBUG PooledDataSource:406 - Created connection 2021707251.
10:15:21,727 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7880cdf3]
10:15:21,729 DEBUG findById:159 - ==> Preparing: select * from user where id = ?
10:15:21,754 DEBUG findById:159 - ==> Parameters: 1(Integer)
10:15:21,775 DEBUG findById:159 - <== Total: 1
User{id=1, username='lucy'}
10:15:21,781 DEBUG update:159 - ==> Preparing: update user set username=? where id=?
10:15:21,782 DEBUG update:159 - ==> Parameters: jack(String), 1(Integer)
10:15:21,784 DEBUG update:159 - <== Updates: 1
10:15:21,784 DEBUG JdbcTransaction:70 - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7880cdf3]
10:15:21,796 DEBUG UserMapper:62 - Cache Hit Ratio [com.example.mapper.UserMapper]: 0.0
10:15:21,796 DEBUG JdbcTransaction:137 - Opening JDBC Connection
10:15:21,800 DEBUG PooledDataSource:406 - Created connection 1847008471.
10:15:21,800 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6e171cd7]
10:15:21,800 DEBUG findById:159 - ==> Preparing: select * from user where id = ?
10:15:21,801 DEBUG findById:159 - ==> Parameters: 1(Integer)
10:15:21,802 DEBUG findById:159 - <== Total: 1
User{id=1, username='jack'}
二级缓存源码解析
- 查看org.apache.ibatis.session.SqlSession中的查询方法,以selectList为例
- 查看Executor实现类 CacheExecutor中的query方法 CacheExecutor是BaseExecutor的装饰类
- query
注意事项
1.sqlSession 调用close或者commit后,不会清空二级缓存。 2.二级缓存只有在commit之后才会生效 3.如果使用默认的缓存,实体需要实现序列化接口(Serializable) 4.二级缓存在分布式下会存在脏读问题