1.查询缓存
1.1什么是查询缓存
mybatis提供查询缓存,用于减轻数据压力,提高数据库性能
mybatis提供一级缓存二级缓存
一级缓存是SQLSession级别的缓存
在操作数据库时需要构造SQLSession对象,在对象中有一个数据结构(HashMap)用户存储缓存数据
不同的SQLSession之间的缓存数据的区域(HashMap)是互不影响的。
二级缓存是mapper级别的缓存
多个SQLSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是SQLSession的
为什么要存储?
如果缓存中有数据就不用在从数据库中获取,大大提高系统的性能。
2一级缓存
2.1一级缓存的工作原理
第一次发起查询用户id为1的用户的信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户
信息
得到用户信息,将用户信息存储到一级存储中
如果在SQLSession去执行commit操作(执行插入,更新,删除)清空SQLSession中的一级缓存。这样做的目的
为了缓存中永远存储的是最新的信息,避免脏读
第二次发起查询用户id为1的用户信息,先去查询缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取
用户信息
2.2一级缓存测试
mybatis默认支持一级缓存,不需要在配置文件去配置
按照上边一级缓存原理步骤测试
测试代码
public testCacher1() throws Exception{
SqlSession sqlSesssion=sqlSessionFactory.openSession();//创建代理对象
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
//下边查询使用一个SqlSession
//第一次发起请求,查询id为1的用户
User uyser=userMapper.findUserById(1);
System.out.println(:user1);
//如果SQLSession去执行commit操作(执行插入,更新,删除) 清空SQLSession中的一级缓存
这样做的目的是为了让缓存中存储的是最新的信息,避main脏读
//更新user的信息
user.setUsername(“测试用户22”);
userMapper.updateUser(user1);
//执行commint操作去清除缓存
SQLSession.commit();
//第二次发起请求,查询id为1的用户
User user2=username.fiondUserById(1);
System.out.println(user2);
sqlSession.close();
}
2.3一级缓存的应用
正式开发,是将mybatis和spring进行整合开发,事务空载子service中
一个service方法中包括很多mapper方法调用
service
//开始执行执行时,开启事务,创建SQLSession对象
//第一调用mapper的方法findUserByID(1);
//第二次调用mapper的方法findUserByID(1);从一级缓存中取数据
//方法结束sqlSession关闭
如果是俩次service调用查询相同的用户信息,不走一级缓存,因为service方法结束,SQLSession就关闭
一级缓存就清空。
3.二级缓存
首先开启mybatis的二级缓存
SQLSession去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中
如果SQLSession去执行相同mapper下sql,执行commit提交,清空该mapper下的二级缓存区域的数据
SQLSession去查询用户id为1的用户信息,去缓存中查中是否存在数据,如果存在直接从缓存中取出数据
二级缓存与一级缓存区别:二级缓存的范围更大,多个SQLSession可以共享一个UserMapper二级缓存
UserMapper有一个二级缓存区域(按照namespace分),其他的mapper也有自己的缓存区域(按照namespace分)
每一个namespace的mapper有一个二级缓存区域,如果俩个mapper的namespace如果相同,这俩个
mapper执行sql查询到的数据就存在相同的二级缓存区域中
3.1开启二级缓存
mybatis的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml
中开启二级缓存
二级缓存使用,需要在主文件中进行配置:
①启用二级缓存
<!-- 启用二级缓存 -->
<setting name="cacheEnabled" value="true"/>
在UserMapper中开启二级缓存,UserMapper.xml下的sql执行完成会存储到它的缓存区域(HashMap)、
<mapper namespace="mybaties.mapper.User,apper">
<!--开启本mapper的namespace下的二级缓存-->
<cache/>
3.2调用pojo类实现序列化接口
public class User implements Serializables{
private int id;
private String username;
private String sex;
private Date birthday;
private String address;
}
为了将缓存的数据取出执行序列化操作,因为二级缓存的存储介质多种多样,不一定在内存
3.3测试方法
public testCacher2() throws Exception{
SqlSession sqlSesssion=sqlSessionFactory.openSession();
SqlSession sqlSesssion2=sqlSessionFactory.openSession();
SqlSession sqlSesssion3=sqlSessionFactory.openSession();
//创建代理对象
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
//下边查询使用一个SqlSession
//第一次发起请求,查询id为1的用户
User uyser=userMapper.findUserById(1);
System.out.println(:user1);
//这里执行关闭操作,将SQLSession中的数据写到二级缓存区域
SQLSession.close();
//使用SQLSession执行commit()操作
UserMapper userMapper3=sqlSession3.getMapper(UserMapper.class);
User user=userMapper3.findUserById(1);
user.setUsername(“测试用户22”);
userMapper3.updateUser(user1);
//执行commint操作去清除缓存
SQLSession3.commit();
SQLSession3.close();
UserMapper userMapper2=sqlSession2.getMapper(UserMapper.class);
//第二次发起请求,查询id为1的用户
User user2=userMapper2.findUserById(1);
Stystem.out.println(user2);
sqlSession2.close();
}
3.4useCache配置
①.设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。
②.清空缓存
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。
注意:
(1)当为select语句时:
flushCache默认为false,表示任何时候语句被调用,都不会去清空本地缓存和二级缓存。
useCache默认为true,表示会将本条语句的结果进行二级缓存。
(2)当为insert、update、delete语句时:
flushCache默认为true,表示任何时候语句被调用,都会导致本地缓存和二级缓存被清空。
useCache属性在该情况下没有。
当为select语句的时候,如果没有去配置flushCache、useCache,那么默认是启用缓存的,所以,如果有必要,那么就需要人工修改配置
4.mybatis整合ehcache
4.1分布式缓存
我们系统为了提高系统并发,性能。一般对系统进行分布式部署。(集群部署方式)
不使用分布缓冲池缓存的数据在各自服务器单独存储,不方便系统开发,所以要对分布式缓存对缓存数据进行集中管理
mybatis无法实现分布式缓存。需要和其他分布式缓存框架进行整合
4.2整合的方法
mybatis提供了一个cache接口,如果要实现自己的存储逻辑,实现cache接口开发即可
mybatis和ehcache整合,mybatis和ehcache集合包中提供了一个cache接口的实现类
public interface Cache{
String getId();
void putObject(Object key ,Object value);
Object getObject(Object key);
}
mybatis默认实现的cache类是
public class PerpetualCache implements Cache{
private String id;
private Map<Object,Object> cache=new HashMap<Object,Object>();
public PerpetualCache(String id){
this.id=id;
}
public String getId(){
return id;
}
}
4.3配置信息
配置mapper中的cache中的type为ehcache对cache接口的实现
<mapper namespace="mybatis.mapper.UserMapper">
<!-- 开启mapper的namespace下的二级缓存
type:指定Cache接口的实现类的类型,mybatis默认使用PerpetualCache
要和ehcache整合:需要配置type为ehcache实现cache接口类型
-->
<caahe type="org.mybatis.caches.ehcache.EhcacheCache"/>
3.4加入ehcache包
ehcache-core-2.6.5.jar
mybatis-ehcache-1.0.2jar
3.5加入ehcache的配置文件
在classpath下配置
1 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2 xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
3 <diskStore path="F:\develop\ehcache" />
4 <defaultCache
5 maxElementsInMemory="1000"
6 maxElementsOnDisk="10000000"
7 eternal="false"
8 overflowToDisk="false"
9 timeToIdleSeconds="120"
10 timeToLiveSeconds="120"
11 diskExpiryThreadIntervalSeconds="120"
12 memoryStoreEvictionPolicy="LRU">
13 </defaultCache>
14 </ehcache>
属性说明:
diskStore:指定数据在磁盘中的存储位置。
defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
以下属性是必须的:
maxElementsInMemory - 在内存中缓存的element的最大数目
maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出
4二级缓存应用场景
应用场景:
对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访
问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时
间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。
5.局限性:
mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,
但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓
存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息
的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。