MyBatis之查询缓存
什么是查询缓存?
将从数据库查询的数据存储到内存中缓存起来,这样就不用从数据库中查询数据而从缓存中查询,提高查询的速度,减少对数据库的访问。
mybatis提供查询缓存包括一级缓存、和二级缓存。
1.一级缓存和二级缓存原理
一级缓存是针对每一个sqlSession进行缓存。每个sqlSession对象中使用Map存储一级缓存数据,sqlSession对象销毁其中一级缓存数据不存在了。sqlSession与SqlSession之间的一级缓存互相不影响。
map中存储了sql执行查询的结果集(java对象)。
二级缓存是针对每个mapper相同 的namespace进行缓存。每个SqlSession都要调用mapper下的sql语句,在mapper级别设置了二级缓存的数据结构map,每个mapper对应一个map数据结构,map中存储了二级缓存的数据,存储了sql执行查询的结果集(java对象)。
每个SqlSession都可以访问到二级缓存中的数据,sqlsession对象销毁mapper中的二级缓存数据仍然存在。
一级缓存运行原理:
第一次查询先去缓存中找是否有缓存数据,发现没有,查询数据库,将查询到的数据写入sqlsession的一级缓存区域。
第二次查询先去缓存中找是否有缓存数据,发现有,直接从缓存区域中取出数据返回。
如果 执行sqlsession的添加、修改、删除等操作,会执行commit,最终会清空缓存。
二级缓存原理:
不同的sqlsession都要调用mapper下的sql语句发起数据库请求。
sqlsession1执行UserMapper下的查询用户请求先从二级缓存中查找有没有数据,如果没有就从数据库中查询,并且将查询到数据存储二级缓存中。
sqlsession2执行UserMapper下的同一个查询用户请求,先从二级缓存中查找有没有数据,如果有就从二级缓存中查询数据,返回。
如果有一个sqlsession3执行UserMapper下添加、修改、删除语句,执行commit操作后,将UserMapper下的所有缓存数据全部清空。
2.一级缓存测试
当两次查询相同 的sql时,第二次查询要缓存中查询,而不从数据库查询。
注意:
1、相同 的sql定义:相同 mapper namespace下的相同 的statement下的sql,且sql的输入参数必须一致。
2、两次查询使用相同 的sqlsession对象。
3.二级缓存测试
当两次查询相同 的sql时,第二次查询要缓存中查询,而不从数据库查询。
注意:
1、相同 的sql定义:相同 mapper namespace下的相同 的statement下的sql,且sql的输入参数必须一致。
2、两次查询可以使用不同 的sqlsession对象。
3.1.开启二级缓存
mybatis对一级缓存是默认支持,对二级缓存需要开启。
打开总开关:
在核心配置文件SqlMapConfig.xml中加入
<!--二级缓存的开起-->
<setting name="cacheEnabled" value="true"/>
打开mapper的二级缓存开关:
3.2.对象序列化
mybatis支持使用第三方缓存框架实现二级缓存数据的存储,要求对象必须实现序列化。
3.3.测试代码
每次查询都会去先查找二级缓存中是否有数据:
通过源码跟踪,二级缓存的数据结构,是一个全局的对象,不受sqlsession的影响,mybatis程序运行起来二级缓存的数据结构就创建出来,直到mybatis程序停止二级缓存数据结构销毁:
二级缓存在一个全局对象中即configruation中:
详细map数据结构如下:
测试代码:
/*二级缓存的测试*/
@Test
public void testCache2() throws Exception {
/*二级缓存是跨sqlSession的是基于mapper级别的缓存,sqlSession可以使用多个对象,多个对象必须要访问同一个mapper下的sql语句*/
//获取session
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
SqlSession sqlSession4 = sqlSessionFactory.openSession();
//生成代理对象
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
UserMapper userMapper4 = sqlSession4.getMapper(UserMapper.class);
//第一次查询
User user = userMapper1.findUserById(10);
System.out.println(user);
//查询用户列表
List<User> userList = userMapper4.findUserByUsername("张");
System.out.println(userList);
//关闭sqlSession1,此时将数据写入二级缓存中
sqlSession1.close();;
sqlSession4.close();
//中间执行commit,清空缓存,将userMapper下所有缓存数据清空
// user.setUsername("赵琳");
// userMapper3.updateUser(user);
// sqlSession3.commit();
// sqlSession3.close();
//第二次查询
user = userMapper2.findUserById(10);
System.out.println(user);
//查询用户列表
List<User> userList2 = userMapper2.findUserByUsername("张");
System.out.println(userList2);
sqlSession2.close();
}
4.二级缓存深入学习
4.1.设置statement是否二级缓存
可以针对每一个statement进行测试是否二级缓存,默认是进行二级缓存。
useCache=true表示要二级缓存,一般要对查询进行二级缓存。
useCache=false,查询该 statement不进行二级缓存,针对那些查询非常频繁的语句,信息的变化性较高,可以将useCache设置false不进行二级缓存。
4.2.刷新缓存
针对insert、update、delete这些statement设置flushCache=”true” 表示执行commit就清空二级缓存,如果设置flushCache=”false”,执行操作也不清空缓存。
一般情况下需要设置flushCache=”true”,为了避免查询出脏数据。
4.3.其它的参数
4.4.mybatis整合ehcache
mybaits是一个持久框架,对缓存数据处理没有第三方专门做缓存框架优秀。
mybatis提供一个Cache接口供第三方框架整合。
建议使用第三方缓存框架和mybatis整合,比如:ehcache、redis、memcache。
mybatis提供二级缓存Cache接口,如下:
它的默认实现类:
4.4.1.第一步:引入ehcache缓存的依赖包
4.4.2.第二步,配置ehcache的配置文件
4.4.3.第三步,开启ehcache缓存
实现让echcache托管mybaits二级缓存数据的存取。
修改mapper.xml文件,在cache中指定EhcacheCache。
根据需求调整缓存参数:
<cache type="org.mybatis.caches.ehcache.EhcacheCache" >
<property name="timeToIdleSeconds" value="3600"/>
<property name="timeToLiveSeconds" value="3600"/>
<!-- 同ehcache参数maxElementsInMemory -->
<property name="maxEntriesLocalHeap" value="1000"/>
<!-- 同ehcache参数maxElementsOnDisk -->
<property name="maxEntriesLocalDisk" value="10000000"/>
<property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>
4.5.mybatis二级缓存应用场景
对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。
4.6.mybatis二级缓存局限性
mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。