简介:
MyBatis系统中默认定义了两级缓存。 一级缓存和二级缓存。
1、默认情况下,只有一级缓存(SqlSession级别的缓存, 也称为本地缓存)开启。
2、二级缓存需要手动开启和配置,它是基于namespace级别的缓存。
3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。
一、一级缓存
一级缓存(local cache), 即本地缓存,,作用域默认为sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空。
本地缓存不能被关闭,但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域。
在mybatis3.1之后, 可以配置本地缓存的作用域.。在 mybatis.xml 中配置:
1、一级缓存演示。使用当前一级缓存,不需要再向数据库发出sql语句进行查询
/**
* 一级缓存:(本地缓存):sqlSession级别的缓存。一级缓存是一直开启的;
* 与数据库同一次会话期间查询到的数据会放在本地缓存中。
* 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;
*/
@Test
public void testFirstLevelCache() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
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();
}
}
2、一级缓存失效。没有使用当前一级缓存,需要再次向数据库发出sql进行查询。
同一次会话期间只要查询过的数据都会保存在当 前SqlSession的一个Map中:
key:hashCode+查询的SqlId+编写的sql查询语句+参数
/**
* 一级缓存失效的四种情况:
* 1、sqlSession不同。
* 2、sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)
* 3、sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
* 4、sqlSession相同,手动清除了一级缓存(缓存清空)
*/
@Test
public void testFirstLevelCache() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);
//1、sqlSession不同
SqlSession openSession2 = sqlSessionFactory.openSession();
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
Employee emp02 = mapper2.getEmpById(1);
System.out.println(emp02);
System.out.println(emp01==emp02);
//2、sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)
Employee emp03 = mapper.getEmpById(3);
System.out.println(emp03);
System.out.println(emp01==emp03);
//3、sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
mapper.addEmp(new Employee(null,"lizq1217","@qq.com","1"));
Employee emp04 = mapper.getEmpById(1);
System.out.println(emp04);
System.out.println(emp01==emp04);
//4、sqlSession相同,手动清除了一级缓存(缓存清空)
openSession.clearCache();
Employee emp05 = mapper.getEmpById(1);
System.out.println(emp05);
System.out.println(emp01==emp05);
}finally{
openSession.close();
}
}
二、二级缓存
二级缓存(second level cache),全局作用域缓存。
二级缓存默认不开启,需要手动配置。
MyBatis提供二级缓存的接口以及实现,缓存实现要求 POJO实现Serializable接口。
二级缓存在 SqlSession 关闭或提交之后才会生效。
使用步骤:
1、在全局配置文件mybatis-config.xml中开启二级缓存<setting name= "cacheEnabled" value="true"/>
。
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2、在需要使用二级缓存的映射文件处使用cache配置缓存<cache />
。
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>
<!--
eviction:缓存的回收策略:
• LRU – 最近最少使用的:移除最长时间不被使用的对象。
• FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
• SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
• WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
• 默认的是 LRU。
flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读:
true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
false:非只读:mybatis觉得获取的数据可能会被修改。
mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
size:缓存存放多少元素;
type="":指定自定义缓存的全类名;
实现Cache接口即可;
-->
3、注意:POJO需要实现Serializable接口。
public class Employee implements Serializable {
private static final long serialVersionUID = 3381523110389232482L;
测试:
/**
* 二级缓存:(全局缓存):基于namespace级别的缓存:一个namespace对应一个二级缓存:
* 工作机制:
* 1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
* 2、如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;
* 3、不同namespace查出的数据会放在自己对应的缓存中(map)
* 效果:数据会从二级缓存中获取
* 查出的数据都会被默认先放在一级缓存中。
* 只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
* 使用:
* 1)、开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>
* 2)、去mapper.xml中配置使用二级缓存:<cache></cache>
* 3)、我们的POJO需要实现序列化接口
* @throws IOException
*/
@Test
public void testSecondLevelCache() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession openSession = sqlSessionFactory.openSession();
SqlSession openSession2 = sqlSessionFactory.openSession();
try{
//1、
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);
openSession.close();
//第二次查询是从二级缓存中拿到的数据,并没有发送新的sql
Employee emp02 = mapper2.getEmpById(1);
System.out.println(emp02);
openSession2.close();
}finally{
}
}
三、缓存的设置
1、全局配置文件mybatis-config.xml中
cacheEnabled=true:开启二级缓存。默认是true。
cacheEnabled=false:关闭二级缓存。不影响一级缓存,一级缓存一直可用。
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2、映射文件mapper.xml的查询标签
useCache=“true”:使用二级缓存。默认是true。
useCache=“false”:不使用缓存。一级缓存依然可以使用,二级缓存不能使用。
flushCache=“false”:不会清除一级缓存和二级缓存。默认是false。
flushCache=“true”:每次查询之后都会清除一级缓存和二级缓存;缓存是没有被使用的;
<!-- public Employee getEmpById(Integer id); -->
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee" useCache="true" flushCache="false">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
3、映射文件mapper.xml的增删改标签
flushCache=“true”:增删改执行完成后就会清除一级缓存和二级缓存。默认是true。
flushCache=“false”:不会清除一级缓存和二级缓存。
<!-- public Long addEmp(Employee employee); -->
<insert id="addEmp" parameterType="com.atguigu.mybatis.bean.Employee" flushCache="true">
insert into tbl_employee(last_name,email,gender)
values(#{lastName},#{email},#{gender})
</insert>
4、sqlSession.clearCache();只是清楚当前session的一级缓存;
5、localCacheScope:本地缓存作用域:一级缓存SESSION;当前会话的所有数据保存在会话缓存中;默认是SESSION。
STATEMENT:可以禁用一级缓存;
四、缓存原理图解:
1、一级缓存:SqlSession从数据库查询出数据后,将数据缓存在各自的SqlSession中。
2、二级缓存:当SqlSession关闭,即会话关闭后,一级缓存的数据才会缓存在各自namespace值的二级缓存中。
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper">
3、查询缓存的顺序:二级缓存 ——> 一级缓存 ——> 数据库。