MyBatis缓存机制
/**
* mybatis提供了两种缓存
* 一级缓存:(本地缓存): sqlSession级别缓存,一级缓存是一直开启的,无法关闭
* 与数据库同一次会话(同一个session)期间查询到的数据会放在本地缓存中
* 以后如果需要获取相同的数据,直接从缓存中拿,没必要再取数据库查询
*
* 一级缓存失效的情况下(没有使用到当前一级缓存的情况,效果就是:还需要再向数据库发送sql查询)
* 1.sqlSession变了,一级缓存即失效(重新发送sql查询)
* 2.sqlSession相同,但查询条件改变,一级缓存失效(重新发送sql查询)
* 3.sqlSession相同,两次查询之间执行了增删改操作,一级缓存失效(这次增删改可能对当前数据有影响)
* 4.sqlSession相同,手动清除了一级缓存(缓存清空)
*
* 二级缓存:(全局缓存) 基于namespace级别的缓存,一个namespace对应一个二级缓存
* 工作机制:
* 1.一个会话查询一条数据,这个数据就会被放置在当前会话的一级缓存中
* 2.如果会话关闭,则该会话对应的一级缓存会被保存到二级缓存中,新会话查询信息,就可以参照二级缓存
* 3.sqlSession中不同mapper的查询结果放在自己(namespace)对应的缓存中
* 如:sqlSession=====EmployeeMapper==>Employee
* DepartmentMapper==>Department
* 不同namespace查出的数据会放在自己对应的缓存中(map)
*
* 查询的数据默认存放在一级缓存中
* 只有会话提交或关闭后,才会放入二级缓存
*
* 二级缓存使用:
* 1>,配置文件中开启全局二级缓存 <setting>标签中开启</>cacheEnabled
* 2>,去mapper.xml中配置使用二级缓存:通过使用<cache></cache>标签开启二级缓存
* 3>.我们的pojo需要实现序列化接口
*
* 和缓存有关的设置/属性:
* 1>cacheEnabled=true false:关闭缓存(二级缓存关闭)(一级缓存一直有效)
* 2>每个select标签都有useCache='' true/false(一级缓存依然有效)(二级缓存关闭)
* 3>每个增删改标签都有属性:flushCache 默认为true,所以增删改操作执行完成后会清除缓存
* 一级缓存清除,二级缓存清空
* 4>select 标签也有flushCache属性,默认为false,如果设置为true,则每次查询完成都会清空缓存
* 5>sqSession.clearCache,只是清除当前session的一级缓存,二级缓存依然有效
* 6>localCacheScope: session/statement 本地缓存作用域,(一级缓存session),当前会话的所有查询
* statement:可以禁用一级缓存
*/
@Test
public void testFirstLevelCache() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Department department = mapper.selectDeptById(1);
System.out.println(department);
sqlSession.clearCache(); //手动清除一级缓存,再次查询将重新发送sql查数据库
Department department1=mapper.selectDeptById(1);
// 同一个session会话中查相同数据,mybatis不会发sql去数据库查询,直接从mybatis的一级缓存中取
System.out.println(department==department1); //这里打印true
SqlSession sqlSession2=sqlSessionFactory.openSession();
DeptMapper mapper1 = sqlSession2.getMapper(DeptMapper.class);
Department department2 = mapper1.selectDeptById(1);
// 不同的sqlsession,一级缓存是不同的,
System.out.println(department==department2); //这里打印false
sqlSession2.close();
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}
@Test
public void testSecondLevelCache() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession2=sqlSessionFactory.openSession();
try {
DepartCacheMapper mapper = sqlSession.getMapper(DepartCacheMapper.class);
Department department = mapper.selectDeptById(1);
// 会话关闭,如果配置了二级缓存,则查询结果会放在二级缓存中
sqlSession.close();
// 不同的session查询相同的数据,首先参照二级缓存,因为查询结果已经放置在了二级缓存中,这里没有发送新的
// 查询sql
DepartCacheMapper mapper1 = sqlSession2.getMapper(DepartCacheMapper.class);
Department department1 = mapper1.selectDeptById(1);
sqlSession2.close();
}finally {
}
}
mapper接口
public interface DepartCacheMapper {
Department selectDeptById(Integer id);
}
SQL映射文件
<!--使用了该标签表明开启mybatis的二级缓存
eviction:缓存回收策略:
LRU: -最近最少使用,移除最长时间不被使用的对象
FIFO:-先进先出,按对象进入缓存的顺序来移除他们
SOFT:-软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK:-弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
默认是LRU
flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读
true:只读,mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改
mybatis为了加快获取速度,直接会将数据在缓存中的引用交给用户,不安全,但速度快
false:非只读:mybatis觉得获取的数据可能会被修改,因此会利用序列化/反序列化技术克隆一份给你,安全,但速度相对慢
size:缓存存放多少元素
type:指定自定义缓存的全类名,需要实现Cache接口
-->
<!--<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"/>-->
<cache></cache>
<select id="selectDeptById" resultType="com.gf.selfdemo.mybatis.bean.Department">
select * from department where id=#{id}
</select>