MyBatis学习-5
本次学习我将为大家讲解一下MyBatis的缓存,它可以在我们实际开发使用时为我们节省很大的时间,类似JDBC的缓存池。
一级缓存
一级缓存,(本地缓存),sqlSession级别的缓存,一级缓存是一直开起的,与数据库同一次会话期间查询到的数据会放在本地缓存中。以后我们如果需要相同的数据,直接从缓存中拿,没必要去查询数据库; 下面我们通过使用一个sqlSession来获取两次相同的数据看看他们到底是不是同一个内容
@Test
public void test09() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
// SqlSession openSession2 = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp1 = mapper.getEmpById(1);
// EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
System.out.println(emp1);
//xxx
Employee emp2 = mapper.getEmpById(1);
System.out.println(emp2);
System.out.println(emp1 == emp2);
}finally {
openSession.close();
}
}
我们发现输出的结果是true说明两次getMappper返回的是同一个数据,再到我们的控制台去看,它说DEBUG [main] - ==> Preparing: select id,last_name Lastname,email,gender from tbl_employee where id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
Employee{id=1, Lastname='lvyonghao', email='snake_lvyonghao@163.com', gender='1', department=null}
Employee{id=1, Lastname='lvyonghao', email='snake_lvyonghao@163.com', gender='1', department=null}
证明了我们确实只做了一次查询。
下面我们讨论一级缓存失效的情况
- sqlSession不同
如果我再新建一个sqlSession同样的查询语句他会不会再次从缓存池中拿出来呢?
@Test public void test09() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); SqlSession openSession2 = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp1 = mapper.getEmpById(1); EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class); System.out.println(emp1); //xxx Employee emp2 = mapper2.getEmpById(1); System.out.println(emp2); System.out.println(emp1 == emp2); }finally { openSession.close(); } }
运行的结果是false,并且我们在控制台中也可以看到它确实发送了两次sql,说明这个时候sqlSession缓存失效了。
- sqlSession相同,查询条件不同
@Test public void test09() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); // SqlSession openSession2 = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp1 = mapper.getEmpById(1); // EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class); System.out.println(emp1); //xxx Employee emp2 = mapper.getEmpById(3); System.out.println(emp2); System.out.println(emp1 == emp2); }finally { openSession.close(); }
结果是必然的,false,查询条件不同必然没有启用一级缓存。
- sqlSession相同,两次查询期间执行了增删改语句
@Test public void test09() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); SqlSession openSession2 = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp1 = mapper.getEmpById(1); EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class); System.out.println(emp1); //xxx mapper.addEmp(new Employee(null,"huwenhao","hunwenhao@outlook.com","1")); System.out.println("数据添加成功"); Employee emp2 = mapper.getEmpById(1); System.out.println(emp2); System.out.println(emp1 == emp2); }finally { openSession.close(); } }
结果是false,因为我们的增删改操作可能会对当前数据有影响。
-
sqlSession相同,手动清除了一级缓存 ``` @Test public void test09() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession(); SqlSession openSession2 = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp1 = mapper.getEmpById(1); EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class); System.out.println(emp1); //xxx openSession.clearCache();
// mapper.addEmp(new Employee(null,”huwenhao”,”hunwenhao@outlook.com”,”1”)); // System.out.println(“数据添加成功”);
Employee emp2 = mapper2.getEmpById(1);
System.out.println(emp2);
System.out.println(emp1 == emp2);
}finally {
openSession.close();
}
} ``` 当我们把一级缓存清理后,结果是false,这个我就不用多说什么了。 实际上sqlSession级别的缓存其实就是一个Map ---
二级缓存
二级缓存也称为全局缓存,它是基于namespace级别的缓存,对应的就是我们XML的namespace,一个namespace对应一个二级缓存。
- 一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存。
- 如果会话关闭,一级缓存中的数据会被保存到二级缓存中,新的会话查询信息就可以参照二级缓存中的内容
- 不同的namespace查出的数据会放在在自己对应的缓存中(Map)
下面我们就实践一下吧
- 首先二级缓存是自动开启的,我们不需要手动开启二级缓存配置,当然你也可以显示的配置设个属性,来到我们MyBatis的配置文件当中的setting标签当中,设置cacheEnabled为true。
- 我们去Mapper.xml当中配置二级缓存,我们只需要在文件当中添加
<cache></cachae>
标签就可以了,当然他也有几个属性我们也来介绍一下:- evication:缓存回收策略
LRU : 最近少使用的,移除长时间不使用的对象(默认) FIFD : 先进先出,按对象进入缓存的顺序移除他们 SOFT :软引用,移除基于垃圾回收器状态和软引用规则的对象。 WEAK : 弱引用,更积极的移除给予垃圾收集器状态和弱引用规则的对象。
- flushInterval:刷新间隔时间(设置为毫秒数)
- readOnly:是否只读设置为true和false
- size :设置缓存中放置多少个元素
- type=“”:自定义缓存的全类名,实现Cache接口即可
- evication:缓存回收策略
- 我们的POJO需要实现序列化接口Serializable
@Test
public void test10() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
SqlSession openSession2 = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);
openSession.close();
Employee emp02 = mapper2.getEmpById(1);
System.out.println(emp02);
}finally {
openSession.close();
}
}
我们在日志中可以看到这么一句话Cache Hit Ratio [com.snake_lvyonghao.MyBatis.Dao.EmployeeMapper]: 0.5
意思就是在缓存中命中,也就意味着我们从二级缓存中找到了。