Mybatis框架学习笔记 --- [缓存初步理解]
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis框架学习笔记 --- [缓存初步理解]相关的知识,希望对你有一定的参考价值。
mybatis3版本的文档 -->mybatis文档
模拟CPU,内存和硬盘之间的关系
CPU运行最快,这三个之间的运行速度差距可能会导致并发问题
本来,数据在硬盘存着,现在可以考虑把数据缓存到内存上;这样的话,CPU要读取数据,直接在内存上读取缓存即可;暂时减轻数据库的压力;
但是有的数据还是必须要去硬盘直接读取的,比如说要读取的这部分数据变动比较快;缓存的数据还保留着之前的数据,但是原数据已经变了;那就得去硬盘读取数据了;
使用缓存(cache)可减少数据库的压力,提高数据库的性能;缓存是使用 Map 集合缓存数据
实际就是缓解查询的压力;
就是把上次在数据库查询的对象存储到内存中去;如果下次还要查询使用;直接在缓存里面取出即可;而减少了查询的次数;减少对数据库的访问次数;
一级缓存
mybatis的话,包含着一级缓存和二级缓存;
只要不进行缓存相关的配置,mybatis的一级缓存是默认存在的;
一级缓存是sqlSession 会话级别的;即只要保证在同一个会话内的数据就会被缓存下来;直到这次会话结束关闭时,才会让缓存中的数据销毁掉;如果没有声明需要刷新,并且缓存没有超时的情况下, SqlSession 都会取出当前缓存的数据,而不会再次发送 SQL 到数据库
。
一级缓存的声明周期
- MyBatis 开启一个数据库会话后,创建一个新的
SqlSession 对象
,这个SqlSession 对象中包含一个新的Executor 对象
。Executor 对象
里面包含一个新的PerpetualCache 对象
,若此时 SqlSession调用close()方法来关闭掉这次会话
,会释放掉一级缓存 PerpetualCache 对象
,一级缓存就失效了。 - 让SqlSession
调用 clearCache()
,就会清空 PerpetualCache 对象中的缓存数据
,但是该对象仍可使用,即还能使用一级缓存功能. - 若在SqlSession 中执行任何的(
增删改sql操作
)(update(),delete(),insert()) ,同样会清空PerpetualCache 对象的数据
,但是该对象可以继续使用;即可以继续使用一级缓存功能
案例,还是用这个年龄大于某个范围的sql来作为执行案例吧
持久层接口EmployeeMapper
下的方法
//根据年龄大于某个范围进行查询;
List<Employee> getEmpByExceedAge(Integer age);
对应的映射文件定义sql
<!--根据年龄大于某个范围进行查询;-->
<select id="getEmpByExceedAge" resultType="employee">
select * from t_employee where age<![CDATA[ > ]]> #{age};
</select>
测试一级缓存;
在同一次会话下进行相同的查询; 然后再开一个会话进行同样的查询
//测试一级缓存; getEmpByExceedAge
@Test
public void getEmpByExceedAge2(){
//调用工具类;
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//获取代理对象;
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
//调用方法; 查询年龄大于20岁的员工;
List<Employee> res1 = mapper.getEmpByExceedAge(20);
List<Employee> res2 = mapper.getEmpByExceedAge(20);
System.out.println("在一次会话内取到得结果是否来自同一地址---->"+(res1 == res2));
//两次取到得结果是否来自同一地址---->true
//关闭第一次的会话sqlSession;
sqlSession.close();
//若再开一个会话2;
//调用工具类;
SqlSession sqlSession2 = MyBatisUtils.getSqlSession();
//获取代理对象;
EmployeeMapper mapper2 = sqlSession2.getMapper(EmployeeMapper.class);
//调用方法; 查询年龄大于20岁的员工;
List<Employee> res3 = mapper2.getEmpByExceedAge(20);
System.out.println("看看这两次会话取得是否来自同一地址--->"+(res2 == res3));
// 两次取到得结果是否来自同一地址--->false
//关闭第二次会话sqlSession;
sqlSession2.close();
}
执行时可发现;在同一会话下;在次查询同一个方法,且参数相同,即查询得到的对象相同;第二次查询的话,它读取的是缓存里面的数据;
但是关闭了第一次会话后;开启了第二次会话,再去查询,那个缓存的数据信息已经不存在了;所以它会去访问数据库进行查询
那么,我要是在同一会话下进行不同的查询呢;
//测试一级缓存; getEmpByExceedAge
@Test
public void getEmpByExceedAge0(){
//调用工具类;
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//获取代理对象;
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
//调用方法; 查询年龄大于20岁的员工;
List<Employee> res1 = mapper.getEmpByExceedAge(20);
//注意 ;这里查询年龄大于22岁的员工;
List<Employee> res2 = mapper.getEmpByExceedAge(22);
//这里再次查询年龄大于20岁的员工;
List<Employee> res3 = mapper.getEmpByExceedAge(20);
System.out.println("在一次会话内取到得结果是否来自同一地址---->"+(res1 == res2));
System.out.println("在一次会话内取到得结果是否来自同一地址---->"+(res1 == res3));
//两次取到得结果是否来自同一地址---->true
//关闭这次会话sqlSession;
sqlSession.close();
}
注意;在这同一次会话内;它缓存的是相同的查询得到的对象信息;
我在第一次和第二次查的不是同一个信息;所以第二次就得去数据库访问查询读取;
第三次和第一次的查询一样;所以第三次就用了第一次的缓存数据
二级缓存
二级缓存是 SqlSessionFactory 级别; 会话工厂;也就是会创建sqlSession会话的那个接口;
那么也就是说,在不同的sqlSession中,都能得到之前的缓存数据信息;即使上一次的会话已经关闭了,数据也会存入二级缓存;
那么也就是说,只要在同一个mapper下;要缓存的信息都是互通的;
但是mapper不同;自然就不能得到缓存的数据了;即二级缓存的隔离级别在于mapper
;
每次查询会先从缓存区域查找,如果找不到则从数据库查询,并将查询到数据写入缓存。Mybatis 内部存储缓存使用一个 HashMap,key 为hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的 java 对象。
sqlSession 执行 insert、update、delete 等操作 commit 提交后会清空缓存区域,防止脏读。
要使用到二级缓存的话;
首先去mybatis的核心配置文件下开启缓存;即在<settings>
下设置开启cacheEnabled
<settings>
<!--开启二级缓存,默认为true-->
<setting name="cacheEnabled" value="true"/>
</settings>
其实,本人当前使用的mybatis版本下;这个已经默认配置为true
状态了
然后第二步的话,还需要把实体类序列化;即让这些类实现序列化接口 Java.io. Serializable;
第三步,再去配置映射文件;
在 Mapper 映射文件中添加<cache />
我在这里的案例下;就在EmployeeMapper.xml
下设置一下了;
<!--设置开启二级缓存-->
<cache >
</cache>
测试使用;查询大于某个年龄的员工
//测试二级缓存;
@Test
public void getEmpByExceedAge3(){
//调用工具类;
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//获取代理对象;
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
//调用方法; 查询年龄大于20岁的员工;
List<Employee> res1 = mapper.getEmpByExceedAge(20);
List<Employee> res2 = mapper.getEmpByExceedAge(20);
//关闭第一次会话sqlSession;
sqlSession.close();
//若再开一个会话;
//调用工具类;
SqlSession sqlSession2 = MyBatisUtils.getSqlSession();
//获取代理对象;
EmployeeMapper mapper2 = sqlSession2.getMapper(EmployeeMapper.class);
//调用方法; 查询年龄大于20岁的员工;
List<Employee> res6 = mapper2.getEmpByExceedAge(20);
List<Employee> res7= mapper2.getEmpByExceedAge(20);
//关闭第二次会话sqlSession;
sqlSession2.close();
}
注意到,第二次会话根本就没去连接数据库;直接用了第一次会话的缓存数据信息
在语句中可使用useCache
来开关是否使用缓存;这个默认是true
比如,我这次让这个查询不使用缓存
<!--根据年龄大于某个范围进行查询;-->
<select id="getEmpByExceedAge" resultType="employee" useCache="false">
select * from t_employee where age<![CDATA[ > ]]> #{age};
</select>
我再去执行测试时,这时已经没有二级缓存功能了;一次会话关闭,就没有缓存的数据了
在设置开启缓存时,还能使用flushInterval=" "
来设置缓存的刷新时间;毫秒为单位;
例如这里设置刷新时间为6秒;
<!--设置缓存,设置缓存刷新时间为6秒-->
<cache flushInterval="6000">
</cache>
以上是关于Mybatis框架学习笔记 --- [缓存初步理解]的主要内容,如果未能解决你的问题,请参考以下文章
mybatis学习笔记(14)-mybatis整合ehcache
SSM框架MyBatis笔记 --- 三层架构;MyBatis框架结构;MyBatis 核心配置文件;sql 映射文件;MyBatis 使用初步(通过Maven添加依赖)