MyBatis缓存处理

通常情况下,缓存可以将程序查询数据频率较高,更新频率较低的数据存储在内存中,它具有可被快速读取和使用的特点。

MyBatis支持的缓存分为一级缓存和二级缓存。

其中,一级缓存SqlSession级别的缓存,二级缓存Mapper级别的缓存。在实际应用中,一级缓存是默认开启的二级缓存需要手动开启

在MyBatis 中,如果一级缓存开启,当同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis 会将查询结果自动写入到一级缓存,此后,如果程序没有执行插入更新删除操作,当第二次执行相同的查询语句时,MyBatis 会直接读取一级缓存中的数据。
Update(插入、更新、删除)操作清空缓存的目的是避免出现脏读

一级缓存

—级缓存的工作流程:
        对于某个查询,根据statementIdparamsrowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果,判断从Cache 中根据特定的key值取的数据数据是否为空,即是否命中:如果命中,则直接将缓存结果返回;如果没命中,去数据库中查询数据,得到查询结果,将key和查询到的结果分别作为key,value对存储到Cache 中,将查询结果返回。

实例演示 MyBatis一级缓存的应用,关键步骤如下:
1、pom.xml 中导入Log4j的依赖

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2、创建log4j.properties文件

#全局日志配置
log4j.rootLogger=ERROR, stdout
# MyBatis日志配置, student是Mapper文件的namespace
log4j.logger.student=DEBUG
#控制台输出配置
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %5p[%t]-%m%n

3、编写测试类(同一个sqlSession查询和更新)

InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
get(sqlSession); // 向sqlSession的一级缓存写入查询结果
get(sqlSession); // 从sqlSession的一级缓存提取查询结果
update(sqlSession); // 清空sqlSession的一级缓存
get(sqlSession); // 向sqlSession的一级缓存写入查询结果

3、编写测试类(不同SqlSession查询和更新)

InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
get(sqlSession); // 向sqlSession的一级缓存写入查询结果
get(sqlSession); // 从sqlSession的一级缓存提取查询结果
SqlSession sqlSessionl = sqlSessionFactory.openSession(true);
update(sqlSessionl); // 清空sqlSession1的一级缓存
get(sqlSession); // 从sqlSession的一级缓存提取查询结果

使用某个 SqlSession往表里插入数据,但是并不会导致另一个操作该表的SqlSession的一级缓存清空,这就会导致脏读,如何去避免?
避免一级缓存导致的脏读有以下方法:
1、每次执行完SQL,调用SqlSession的clearCache()方法,清除一级缓存
2、<select>元素设置属性flushCache = 'true’,会清空一级缓存和二级缓存
3、执行SqlSession的commit操作都会刷新缓存(更新操作必须commit才能起效)
4、在Mybatis配置文件中设置<setting name= "localCacheScope" value="STATEMENT”/>,关闭所有的select语句的一级缓存

二级缓存

二级缓存是基于事务的,事务结束才写入缓存。其作用域是Mapper,与一级缓存相比,二级缓存的作用域范围更大,它可以跨多个SqlSession对象,并且二级缓存可以自定义缓存源


当开启二级缓存时,MyBatis 以namespace区分Mapper,如果多个SqlSession对象使用同一个Mapper 的相同查询语句去操作数据库,在第一个 SqlSession对象执行完成并且提交事务后,MyBatis 会将查询结果写入到二级缓存,此后,如果程序没有执行插入、更新、删除操作,当第二个SqlSession对象执行相同的查询语句时,MyBatis 会直接读取二级缓存中的数据。

与一级缓存不同,MyBatis的二级缓存需要手动开启,开启二级缓存通常要完成以下两个步骤。
1、开启二级缓存的全局配置
使用二级缓存首先要打开全局配置,这可以通过MyBatis配置文件中的<settings>元素来完成,具体代码如下。默认情况下cacheEnabled为true。

<settings>
    <setting name="cacheEnabled" value="true"></setting>
</settings>

2、开启当前Mapper的namespace的二级缓存
在Mapper 映射文件中添加<cache>元素,具体代码如下:

<cache />

以上代码等同于:

<cache type="org.apache.ibatis.cache.impl.PerpetualCache" size="1024" eviction="LRU" readOnly="false"/>

以上代码实现的效果如下:

  • 映射语句文件中的所有select语句的结果将会被缓存。
  • 映射语句文件中的所有insert、update和delete语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU,Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的1024个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

3、编写测试类

示例一:

SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 数据写入二级缓存
get(sqlSession,StudentMapper.class); // 向sqlSession的一级缓存写入数据
sqlSession.commit(); // 向StudentMapper的二级缓存写入数据
SqlSession sqlSessionl = sqlSessionFactory.openSession(true); // 从二级缓存提取数据
get(sqlSession1, StudentMapper.class); // 从StudentMapper的二级缓存提取数据

示例二:

SqlSession sqlSession = sqlSessionFactory.openSession(true);
//查询数据写入二级缓存
get(sqlSession, StudentMapper.class); // 向sqlSession的一级缓存写入数据
sqlSession.commit(); // 向StudentMapper的二级缓存写入数据
SqlSession sqlSessionl = sqlSessionFactory.openSession(true);
get(sqlSession1, StudentMapper.class); // 从StudentMapper的二级缓存提取数据
update(sqlSession1, StudentMapper.class); // 清空StudentMapper和sqlSessionl的缓存
get(sqlSession1, StudentMapper.class); // 向sqlSessionl的一级缓存写入数据
sqlSession.close();
sqlSession1.close();

MyBatis自带的一级和二级缓存谨慎使用,同时开始的时候,先从二级缓存中获取数据,再从一级缓存中获取数据,只有严格按照要求使用才能避免出现脏读的情况。

使用二级缓存避免出现脏读,必须遵守以下准则:
        1、尽量关闭一级缓存,避免一级缓存导致的脏读
        2、对同一表的操作放置在同一个namespace中

简单总结

MyBatis自带的一级和二级缓存的工作原理简述:

一级缓存是SqlSession级别的缓存,其生命周期即为SqlSession的生命周期。当执行查询操作时,MyBatis 会将结果写入一级缓存,当在SqlSession中执行更新(DML)操作时,一级缓存会被清空。


二级缓存是Mapper级别的缓存,其是基于事务的。当执行查询操作且提交事务后,MyBatis 才会将结果写入二级缓存,当在同一个Mapper 中执行更新(DML)操作时,二级缓存会被清空。
 

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值