MyBatis 缓存机制
1. 概述
1.1 缓存可以极大的提升查询效率,MyBatis 系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存(SqlSession 级别的缓存,也称为本地缓存)开启;
- 二级缓存(基于namespace 级别的缓存, 也称为全局缓存)需要手动开启和配置;
- 为了提高扩展性,MyBatis 定义了缓存接口 Cache, 可以通过实现Cache 接口来自定义二级缓存;
2. 一级缓存
- 一级缓存: 与数据库同一次会话期间,查询到的数据会放在本地缓存中;以后,如果需要获取相同的数据,直接从缓存中获取;
- 一级缓存就是SqlSession级别的一个Map;
- 一级缓存失效的情况(即获取相同的数据,需要发送两次sql语句)
- SqlSession 不同时,一级缓存失效;
- SqlSession 相同,查询条件不同;
- SqlSession 相同,但是两次查询之间,执行了增删改操作;
- SqlSession 相同,手动清除了一级缓存;(
clearCache()
方法)
// EmployeeMapper.java
public class EmployeeMapper{
public Employee getEmpById(Integer id);
}
// EmployeeMapper.xml
<resultMap type="cn.itcast.mybatis.bean.Employee" id="MyEmp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
</resultMap>
<select id="getEmpById" resultMap="MyEmp">
select * from tbl_employee where id=#{id}
</select>
// 测试类
public class MyTest{
@Test
public void test01 throw IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
// 一级缓存
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);
Employee emp02 = mapper.getEmpById(1);
System.out.println(emp02);
System.out.println(emp01==emp02);
}finally{
openSession.close();
}
}
}
3. 二级缓存
- 基于namespace级别的缓存,一个namespace对应一个二级缓存;
- 查询出的数据,都会默认放在一级缓存中,只有会话提交或关闭之后,一级缓存中的数据才会转移到二级缓存;
- 工作机制:
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果会话关闭,一级缓存中的数据会被保存到二级缓存中; 新的会话查询信息,就可以参照二级缓存中的内容;
- 例如,使用EmployeeMapper对象,查询到的Employee对象, 和使用DepartmentMapper对象,查询到
的Department对象分别存放在不同的map中;即不同namespace查询出的数据,会放在自己对应的缓存(map);
- 使用步骤:
- 在
mybatis-config.xml
中开启:<setting name="cacheEnabled" value="true"/>
; - 在
xxxMapper.xml
中配置使用二级缓存:<cache></cache>
,常用属性:eviction
: 缓存的回收策略;(LRU, FIFO, SOFT, WEAK)flushInterval
: 缓存刷新间隔; 即缓存多长时间清空一次,默认不清空;readOnly
: 缓存是否只读;size
: 缓存存放多少元素;type
: 指定自定义缓存的全类名;
- POJO 需要实现序列化接口;
- 在
- 和缓存相关的设置/属性:
cacheEnabled=false
: 关闭的是二级缓存,一级缓存仍可用;- select 标签中的
useCache=false
: 关闭的也是二级缓存,一级缓存依然可以使用; - 每个增删改标签都存在
flushCache=true
,默认为true; 表示增删改执行完成后,就会清空一级缓存和二级缓存; - 查询标签的
fluseCache
的属性默认为 false; sqlSession.clearCache()
: 只清空当前session中的一级缓存;
// 测试类
public class MyTest{
@Test
public void test01 throw 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);
// 两次会话,查询内容相同; 二级缓存开启的情况下,只会发送一条SQL语句
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);
openSession.close();
Employee emp02 = mapper2.getEmpById(1);
System.out.println(emp02);
openSession2.close();
}finally{
}
}
}
4. 整合第三方缓存(ehcache)
- 导入jar包:
ehcache-core-2.6.8
,slf4j-api
,slf4j-log4j
,mybatis-ehcache
; - 配置 EmployeeMapper.xml
- 在类路径下新建
ehcache.xml
文件
// EmployeeMapper.xml
<mapper namespace="cn.itcast.mybatis.dao.EmployeeMapper">
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
<resultMap type="cn.itcast.mybatis.bean.Employee" id="MyEmp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
</resultMap>
<select id="getEmpById" resultMap="MyEmp">
select * from tbl_employee where id=#{id}
</select>
</mapper>
// DepartmentMapper.xml
<mapper namespace="cn.itcast.mybatis.dao.DepartmentMapper">
<!-- 引用缓存: namespace,指定和哪个名称空间下的缓存一样 -->
<cache-ref namespace="cn.itcast.mybatis.dao.EmployeeMapper"/>
<resultMap type="cn.itcast.mybatis.bean.Employee" id="MyEmp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
</resultMap>
<select id="getEmpById" resultMap="MyEmp">
select * from tbl_employee where id=#{id}
</select>
</mapper>
参考资料