博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis的缓存机制Cache
阅读量:4691 次
发布时间:2019-06-09

本文共 4634 字,大约阅读时间需要 15 分钟。

Mybatis提供对缓存的支持,分为一级缓存和二级缓存,在没有配置的情况下,系统默认会使用一级缓存。

一级缓存(SqlSession级别)

  我们都知道每个SqlSession对象之间的缓存是互不影响的,当同一个SqlSession执行多次相同的SQL语句时(主要针对select),系统只会到底层访问数据库一次,后续执行sql时,会从一级缓存里面读取第一次访问的数据。当这个SqlSession对象执行close(),或者显示声明清空缓存时,对应的一级缓存也会清空,从而提高效率。值得注意的是,如果SqlSession执行DML操作(即insert、update、delete),并提交到数据库时,也会起到清空该SqlSession对象对应的一级缓存的作用,目的是为了保证缓存里面的数据是最新的。,避免脏读现象。

二级缓存(SqlSessionFactory级别)

  有些书籍说二级缓存是Mapper级别,可能是依据二级缓存的配置是在xxxMapper.xml上配置的吧,不过本人更认同是SqlSessionFactory级别,为什么呢?因为同一个Configuration里面是创建一个SqlSessionFactory,SqlSessionFactory是属于线程安全的,SqlSessionFactory可以创建很多个SqlSession来进行事务操作,也就是说,SqlSessionFactory是由很多个SqlSession对象共享的,同样的,二级缓存的数据也是由很多个SqlSession共享的。如何才能让系统操作二级缓存呢?原理很简单,不同的SqlSession对象执行相同的namespace下的sql语句,当第一个SqlSession对象调用close()关闭一级缓存时,第一次查询得到的数据将会被保存到二级缓存,后面的SqlSession对象调用相同sql语句时,就会从二级缓存中获取数据。当然,使用二级缓存,需要对应的返回对象(即POJO)实现序列化。

配置:在需要使用的xxxMapper.xml里面配置<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true" />

eviction:代表回收策略,目前支持4种策略

1.LRU,最近最少使用的,移除最长时间不用的对象;

2.FIFO,先进先出;

3.SOFT,移除基于垃圾回收器状态和软引用规则的对象;

4.WEAK,更积极移除基于垃圾回收器状态和弱引用规则的对象。

flushInterval:代表刷新时长,单位毫秒

size:代表缓存最多可以存储多少个对象,注意,设置过大会导致内存溢出

readOnly:意味着缓存数据只能读取,不能修改

上面是较为详细配置,当然也可以简单一点,直接写上<cache />就可以了。

下面给出一个较为有意思的栗子:

xml配置代码如下:

JUnit4测试代码如下:

@Test    public void testCache(){        SqlSession ss1 = null;        SqlSession ss2 = null;        try{            ss1 = SqlSessionFactoryUtil.initSqlSessionFactory().openSession();            ss2 = SqlSessionFactoryUtil.initSqlSessionFactory().openSession();            EmployeeMapper em1 = ss1.getMapper(EmployeeMapper.class);            EmployeeMapper em2 = ss2.getMapper(EmployeeMapper.class);            List
list1 = em1.getEmpList(); //ss1.close(); list1 = em2.getEmpList(); }catch(Exception e){ ss1.rollback(); ss2.rollback(); e.printStackTrace(); }finally{ if(ss1 != null){ ss1.close(); } if(ss2 != null){ ss2.close(); } } }

代码中实例化两个SqlSession对象,分别是ss1和ss2,用户检验数据读取的操作。留意上面注释了ss1.close();这一行。

下面是执行上面测试代码的日志结果:

Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.PooledDataSource forcefully closed/removed all connections.PooledDataSource forcefully closed/removed all connections.PooledDataSource forcefully closed/removed all connections.PooledDataSource forcefully closed/removed all connections.Cache Hit Ratio [com.learn.mapper.EmployeeMapper]: 0.0Opening JDBC ConnectionCreated connection 275310919.Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]==>  Preparing: select * from tb_employee ==> Parameters: <==      Total: 6Cache Hit Ratio [com.learn.mapper.EmployeeMapper]: 0.0Opening JDBC ConnectionCreated connection 1948863195.Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@74294adb]==>  Preparing: select * from tb_employee ==> Parameters: <==      Total: 6Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]Returned connection 275310919 to pool.Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@74294adb]Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@74294adb]Returned connection 1948863195 to pool.

可以看到即使配置了<cache />二级缓存,第一个SqlSession对象ss1没有手动执行close()方法时,ss1对应的一级缓存数据仍然没有保存到二级缓存里面,即二级缓存里面没有数据,当第二个SqlSession对象ss2执行相同的sql语句时,会找一级缓存有没有数据,因为第一次执行,当然没有数据了,然后找二级缓存,刚才说过了,ss1的一级缓存数据并没有保存到二级缓存里面,所以二级缓存也没有数据,因此ss2就会再次操作数据库。可以看到log日志会有两条select语句。

此时,如果将close();的注释去掉,再执行一下测试代码,日志结果如下:

Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.PooledDataSource forcefully closed/removed all connections.PooledDataSource forcefully closed/removed all connections.PooledDataSource forcefully closed/removed all connections.PooledDataSource forcefully closed/removed all connections.Cache Hit Ratio [com.learn.mapper.EmployeeMapper]: 0.0Opening JDBC ConnectionCreated connection 275310919.Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]==>  Preparing: select * from tb_employee ==> Parameters: <==      Total: 6Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1068e947]Returned connection 275310919 to pool.Cache Hit Ratio [com.learn.mapper.EmployeeMapper]: 0.5

显而易见,此时只访问一次数据库。

转载于:https://www.cnblogs.com/SysoCjs/p/9574318.html

你可能感兴趣的文章
RTP Payload Format for Transport of MPEG-4 Elementary Streams over http
查看>>
Java环境变量设置
查看>>
【JBPM4】判断节点decision 方法3 handler
查看>>
filter 过滤器(监听)
查看>>
node启动时, listen EADDRINUSE 报错;
查看>>
杭电3466————DP之01背包(对状态转移方程的更新理解)
查看>>
kafka中的消费组
查看>>
python--注释
查看>>
SQL case when else
查看>>
SYS_CONTEXT 详细用法
查看>>
Pycharm配置autopep8让Python代码更符合pep8规范
查看>>
我的第一篇博客
查看>>
【C++算法与数据结构学习笔记------单链表实现多项式】
查看>>
C#垃圾回收机制
查看>>
31、任务三十一——表单联动
查看>>
python之hasattr、getattr和setattr函数
查看>>
maven使用阿里镜像配置文件
查看>>
Copy code from eclipse to word, save syntax.
查看>>
arguments.callee的作用及替换方案
查看>>
23 Java学习之RandomAccessFile
查看>>