MyBatis缓存机制

MyBatis系统中默认定义了两级缓存。
一级缓存和二级缓存:
1、默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称本地缓存)
2、二级缓存需要手动开启和配置。(namespace级别全局缓存)
3、MyBatis定义了缓存接口Cache,可以通过实现Cache接口自定义二级缓存

一级缓存

与数据库同一次会话期间查询到的数据会存放在本地缓存中;
以后如果需要获取相同的数据,会从缓存中查找,不会访问数据库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testFirstLevelCache()throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession = null;
try{
sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee empById1 = mapper.getEmpById(1);
System.out.println(empById1);
// 再次查询相同的数据
Employee empById2 = mapper.getEmpById(1);
System.out.println(empById2);
System.out.println(empById1==empById2);// true
}finally {
if(sqlSession!=null){
sqlSession.close();
}
}
}

可以看见在两次查询中,sql语句只向数据库发送了一次,并且第二次查询得到的对象与第一次完全相同,说明第二次查询访问的是内存中的缓存。

一级缓存失效的情况:
1、不同的SqlSession对应不同的一级缓存;
2、同一个SqlSession但是查询条件不同;
3、同一个SqlSession两次查询期间执行了任何一次增删改操作;
4、同一个SqlSession两次查询期间手动清空了缓存。sqlSession.clearCache()

二级缓存

工作机制:
1、一个会话,查询一条数据,这个数据就会被保存在当前会话的一级缓存中;
2、如果会话关闭,一级缓存中的数据就会保存到二级缓存中。新的会话就会参照二级缓存。
3、不同的namespace查出的数据会放在自己对应的缓存中(map)
      sqlSession——–EmployeeMapper——-Employee
                         ——–DepartmentMapper——-Department
使用:
1、开启全局二级缓存

1
2
<!--  开启全局缓存-->
<setting name="cacheEnabled" value="true"/>

2、在sql映射文件mapper.xml中配置使用二级缓存
只需在mapper标签的第一行添加:

1
2
3
4
5
6
7
<!-- cache参数:
eviction 缓存回收策略 默认LRU 最近最少使用
flushInterval 缓存刷新间隔 默认不刷新
readOnly 是否只读 默认flase
size 缓存大小
type 自定义缓存全类名(实现Cache接口)-->
<cache></cache>

3、JavaBean对象需要实现序列化Serializable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test
public void testSecondLevelCache() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession1 = null;
SqlSession sqlSession2 = null;
try {
sqlSession1 = sqlSessionFactory.openSession();
sqlSession2 = sqlSessionFactory.openSession();
EmployeeMapper mapper1 = sqlSession1.getMapper(EmployeeMapper.class);
EmployeeMapper mapper2 = sqlSession2.getMapper(EmployeeMapper.class);
// 使用不同的sqlSession 查询相同的数据
Employee empById1 = mapper1.getEmpById(1);
System.out.println(empById1);
// 关闭会话,数据才会存入二级缓存 从一级缓存转移到二级缓存
if (sqlSession1 != null) {
sqlSession1.close();
}
Employee empById2 = mapper2.getEmpById(1);
System.out.println(empById2);
System.out.println(empById1 == empById2);// false empById1是一级缓存里面的 empById2是从二级缓存中获取的 故地址值不相同
} finally {
if (sqlSession2 != null) {
sqlSession2.close();
}
}
}

关闭缓存

1、cacheEnabled=false:关闭二级缓存,一级缓存还可以使用
2、每一个select标签都有useCache = “true”(默认)
      useCache = “false”:关闭二级缓存,一级缓存还可以使用
3、每一个增删改标签默认flushCache=”true”,查询标签默认flushCache=”false”
      增删改操作执行完后就会清空缓存
4、sqlSession.clearCache():只是清空当前sqlSession的一级缓存

缓存原理图