mybatis缓存
1、 查询缓存结构
2、mybatis缓存
mybatis的缓存机制有两级:
(1)一级缓存:一级缓存mybatsi已近为我们自动开启,不用我们手动操作,而且我们是关闭不了的!!但是我们可以手动清除缓存。一级缓存是sqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中 有一个数据结构(HashMap)用于缓存数据。不同的sqlSession之间的
缓存数据区域(HashMap)是互不影响的。
(2)二级缓存:二级缓存需要我们手动开启。(全局级别) 二级缓存是mapper级别的缓存,多个sqlSession去操作同一个Mapper的sql语句,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSession的。
2.1、mybatis查询缓存的作用
查询缓存来缓存数据,从而达到提高查询性能的要求,以提高我们项目的效率!!
为什么要用缓存,如果缓存中有数据就不从数据库中获取,可以提高系统性能。
2.2、一级缓存
2.2.1、一级缓存工作原理
第一次查询用户id为1的用户信息,先去缓存中查询是否有id为1的用户信息,如果没有,从
数据库中查询用户信息。得到用户信息,将用户信息储存到一级缓存中。
如果sqlSession去执行commit操作(插入、更新、删除),清空sqlSession中的一级缓存,保证
缓存中始终保存的是最新的信息,避免脏读。
第二次查询用户id为1的用户信息,先去缓存中查询是否有id为1的用户信息,如缓存中有,直接
从缓存中获取。
2.2.2、mybaits默认支持一级缓存,不需要配置。
测试:
//一级缓存测试
@Test
public void testFirstLevelCache() {
SqlSession sqlSession = sf.openSession();
// 创建代理对象
OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
// 调用接口的方法
try {
//第一次查询id为一得用户
User user=ordersMapperCustom.selectUserById(1);
System.out.println(user);
/*//更新用户数据 执行此操作讲清空缓存
ordersMapperCustom.updateUserById(new User(1,"guai",1,"任性的白菜"));
sqlSession.commit();*/
//第二查询id为1的用户
User user2=ordersMapperCustom.selectUserById(1);
System.out.println(user2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sqlSession.close();
}
输出结果:
将更新用户信息语句的注释去掉,执行结果为:
2.2.3、一级缓存应用
在开发中,一般将mybatis和spring进行整合开发,事务控制在service中。一个service方法中包括许多mapper方法调用。
service 开始执行时,开启事务,创建SqlSession对象
第一次调用mapper方法selectUserById(1)
第二次调用mapper方法selectUserById (1),从一级缓存中获取数据。
方法结束,SqlSession关闭
如果是两个service执行两次service调用查询同一用户信息,不走一级缓存,因为session方法结束,SqlSession就关闭,一级缓存就清空。
2.3、二级缓存
2.3.1、原理
sqlSession1去查询用户id为1的用户信息。查询到用户信息会将查询数据保存到二级缓存中。
sqlSession2查询用户id为1的用户信息,先去耳机缓存中找是否存在对应数据,如果存在直接从二级缓存中取出数据。
二级缓存与一级缓存的区别:
二级缓存范围更大,多个sqlSession可以共享一个userMapper二级缓存区域。
每个Mapper有各自的二级缓存区域(按照mapper.xml中的nameSpace分的)
2.3.2、开启二级缓存
mybatis的二级缓存是mapper级别的,除了在sqlMapConfig.xml(mybatis全局配置文件)中开启二级缓存之外,还要在具体的mapper.xml中开启二级缓存。
<settings>
<!-- 打开延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 设置按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 开启二级缓存 其实默认就是开启的-->
<setting name="cacheEnabled" value="true"/>
</settings>
在userMapper中开启二级缓存,
<!-- 开启本mapper的namespace下的二级缓存 -->
<cache></cache>
属性:type指定cache接口的实现类的类型,mybatis默认使用PrepetualCache,
如果要和ehcache整合,需要配置type为ehcache并实现cache接口的类型
2.3.3、在pojo类中实现序列化接口
为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不都是在内存中。
序列化与反序列化
测试代码:
//二级缓存测试
@Test
public void testSecondLevelCache() {
//多个sqlSession用于跨sqlSession
SqlSession sqlSession = sf.openSession();
SqlSession sqlSession1= sf.openSession();
SqlSession sqlSession2= sf.openSession();
// 创建代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
// 调用接口的方法
try {
//第一次查询id为一得用户
User user=userMapper.selectUserById(1);
System.out.println(user);
//这里必须关闭sqlSession才会将数据保存到二级缓存中
sqlSession.close();
user=userMapper1.selectUserById(1);
System.out.println(user);
sqlSession1.close();
user=userMapper2.selectUserById(1);
System.out.println(user);
sqlSession2.close();
//更新用户数据 执行此操作讲清空缓存
/*userMapper.updateUserById(new User(1,"guai",1,"任性的白菜"));
sqlSession.commit();
*/
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
结果:
只有第一次查询时执行了sql其余两次从二级缓存区域取出
Cache Hit Ratio 缓存率
2.3.4、禁用缓存
userCache=“false”" 指定statement
例:
2.4、mybatis整合ehcache
ehcache是一个分布式缓存框架
2.4.1、 分布式缓存
我们系统为了提高系统并发,一般会对系统进行分布式部署(集群部署方式)
mybatis无法实现分布式缓存。
在分布式中,系统各个模块分别部署在不同的服务器中,对缓存的集中管理显得尤为重要
例如,将用户登录信息保存到 一个被各个服务器可共享的缓存中,而不是分别保存在每一个服务器中。
2.4.2、整合方法
mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口即可。
例如:mybatis和ehcache整合,mybatis和ehcache整合包中提供了一个cache接口的实现类
2.4.3、配置ehcache
<!-- type:type指定cache接口的实现类的类型,mybatis默认使用PrepetualCache,
要和ehcache整合,需要配置type为ehcache实现cache接口的类型
-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
</cache>
2.4.4、加入ehcache的配置文件
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
</ehcache>
2.5、二级缓存应用场景
对于访问多的查询请求且用户对查询结果实时性要求不高,次吃可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景对比:耗时较高的统计分析sql,电话账单查询sql等。
实现方法如下:通过设置刷新间隔时间,有mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如30分钟、24小时等。
2.6、局限性
mybatis二级缓存对细粒度缓存的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品信息变化时只刷新该商品的信息而不刷新其他商品的信息,因为mybatis的二级缓存区域已mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。