源码解析--mybatis一级缓存和二级缓存
泛域名ssl证书 239元1年送1个月、单域名39元1年,Sectigo(原Comodo证书)全球可信证书,强大的兼容性,高度安全性,如有问题7天内可退、可开发票
加微信VX 18718058521 备注SSL证书
【腾讯云】2核2G4M云服务器新老同享99元/年,续费同价
MyBatis缓存
使用缓存可以时应用更快地获取数据,避免频繁地数据库交互,尤其是在查询越多、缓存命中率越高地情况下,使用缓存地作用就越明显。MyBatis作为持久化框架,提供了非常强大地查询缓存特性,可以非常方便地配置和定制使用。
MyBatis提供了一级缓存、二级缓存两种缓存方式,一般提到MyBatis缓存的时候,都是指二级缓存。一级缓存(也叫本地缓存)默认会开启,并且不能控制,因此很少会提到。
一级缓存主要是sqlSession级别的缓存,在操作数据库时是需要构造sqlSession会话对象的,对同一个对象中的数据可以使用到缓存,不同的sqlSession之间的缓存是不同享的。
二级缓存主要是Mapper级别的缓存(namespace),多个sqlSession是共享缓存的。
一级缓存
一级缓存是sqlSession级别的缓存,作用于在同一个sqlSession当中,在同一个sqlSession中连续执行同一个SQL语句,第一次执行的结果会放入缓存当中,第二次进行查询操作时就可以直接访问缓存结果。
MyBatis是默认支持一级缓存的,不需要做相关配置。
下面对一级缓存进行测试。测试场景如下:
case1:同一个sqlSession下连续的执行同一个查询操作;
case2:在同一个sqlSession下,先进行查询操作,再进行变更操作,然后接着进行同一个SQL查询;
case3:不同的sqlsession下,进行同一个SQL查询操作;
测试环境与前面博客中的测试环境相同。
case1验证:
测试代码:
//case1:同一个sqlSession下连续的执行同一个查询操作
@Test public void getStudentByID() throws IOException { SqlSession sqlSession = this.getSqlSessionFactory().openSession(); //通过接口产生代理类 UserMapper mapper = sqlSession.getMapper(UserMapper.class); //第一次执行查询操作,查询id =1 User user = mapper.queryUserById(1); System.out.println(user); //第二次查询 User user1 = mapper.queryUserById(1); System.out.println(user1); }
日志打印:
结果:在同一个sqlSession下,连续的查询同一个SQL语句,只查询一次数据库,第二次查询即访问缓存拿到结果。
case2验证:
测试代码:
//case2:在同一个sqlSession下,先进行查询,在变更操作,然后进行同一个SQL查询
@Test public void getStudentByID2() throws IOException { SqlSession sqlSession = this.getSqlSessionFactory().openSession(); //通过接口产生代理类 UserMapper mapper = sqlSession.getMapper(UserMapper.class); //第一次执行查询操作,查询id =1 User user = mapper.queryUserById(1); System.out.println(user); //进行更新SID=1的学生信息 User user2 = new User(); user2.setId(1); user2.setUsername("大刀王五"); mapper.updateUserById(user2); sqlSession.commit(); //第二次查询 User user1 = mapper.queryUserById(1); System.out.println(user1); }
日志打印:
结果:在第一次查询之后,会将数据存入缓存中,当进行了变更操作时,将缓存清空,第二次执行同一个SQL的查询操作时,此时缓中不存在数据,就会继续查询数据库。
case3验证:
测试代码:
//case3:不同的sqlsession下,同一个SQL查询操作
@Test public void getStudentByID3() throws IOException { //创建SQLSession实例1 SqlSession sqlSession1 = this.getSqlSessionFactory().openSession(); UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class); //创建SQLSession实例2 SqlSession sqlSession2 = this.getSqlSessionFactory().openSession(); UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); //第一次执行查询操作 User user = mapper1.queryUserById(1); System.out.println(user); //第二次查询 User user1 = mapper2.queryUserById(1); System.out.println(user1); }
日志打印:
结果:在同一个sqlsession实例下,连续查询操作可以使用缓存,在不同的sqlSession实例下,缓存是不共享的。
二级缓存
二级缓存是Mapper级别的缓存,默认情况下二级缓存是关闭的。同一个Mapper下不同的sqlSession是可以共享二级缓存的,不同Mapper的缓存是相互隔离的。
二级缓存和一级缓存的区别:
二级缓存范围更大,多个sqlSession可以共享一个Mapper的二级缓存区域。
二级缓存使用步骤:
1、需要在全局配置文件中打开二级缓存的开关。
<settings> <!--开启二级缓存的开关--> <setting name="cacheEnabled" value="true"/> </settings>
2、将映射的pojo类实现序列化。
public class Student implements Serializable
3、在mapper配置文件中添加cache标签。
<mapper namespace="com.tulun.dao.StudentMapper"> <!--开启本Mapper的二级缓存--> <cache></cache> </mapper>
cache的参数说明:
<mapper namespace="com.tulun.dao.StudentMapper"> <!-- cache参数说明: flushInterval(缓存刷新间隔)单位毫秒,默认情况下不设置,在变更操作时,进行缓存刷新 size:(引用次数)记录缓存对象的数量 默认值1024 readOnly(只读)参数为true:false 表示缓存的数据对象实例是不能被修改 eviction:缓存失效的策略 默认LRU LRU:最近最少使用的:移除最长时间不使用的 FIFO:先进先出,按照缓存对象的顺序来淘汰 SOFT:软引用 WEAK:弱引用 --> <cache eviction="LRU" readOnly="true" flushInterval="10000" size="12"></cache> </mapper>
cache的配置禁用:
在statement上配置useCache=“false”,禁用的是二级缓存。
<select id="queryUserById" useCache="false" parameterType="int" resultType="com.test.mybatis.User"> select id,username,birthday,sex,address from user where id = #{id} </select>
二级缓存的测试:
case:在同一个Mapper实例下,不同的sqlSession执行同一个查询操作。
测试代码:
//case:在同一个Mapper实例下,不同的sqlsession下,同一个SQL查询操作
@Test public void getStudentByID3() throws IOException { //创建SQLSession实例1 SqlSession sqlSession1 = this.getSqlSessionFactory().openSession(); UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class); //创建SQLSession实例2 SqlSession sqlSession2 = this.getSqlSessionFactory().openSession(); UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); //第一次执行查询操作 User user = mapper1.queryUserById(1); System.out.println(user); //第二次查询 User user1 = mapper2.queryUserById(1); System.out.println(user1); }
日志打印:
结果:在同一个Mapper下,不同的sqlSession实例可以共享缓存。
源码解读:
DefaultSqlSession类实现了SqlSession,所有的增删改查都会走到这个类里面,但是只有select会有缓存,我们找一个selectlist方法进去,会看到这里用到了executor执行器,并且调用query方法
点进去Executor这个接口,会看到这里有2个实现类,一个是baseExecutor,和cacheExecutor,当我们配置缓存开启时会走cacheExecutor,否则就会走baseExecutor,
其中baseExecutor是一级缓存类,cacheExecutor是二级缓存类
<setting name="cacheEnabled" value="true"/>
先看baseExecutor一级缓存,这里的this.localCache.getObject(key),就是从缓存里面读取数据,这里第一次肯定是没有的NULL,
再看list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); 这行代码直接从数据库查,我们看this.queryFromDatabase方法
注意this.localCache.putObject(key, list);就会把查到数据put到缓存中,如果下次还是本SqlSession的同一条sql进来查询会直接从上面的this.localCache.getObject(key)中获取数据
说完一级缓存再说二级缓存,当二级缓存配置为true时,会进入到CachingExecuor类,如图所示
ms.isUseCache==true时,执行 this.tcm.getObject(Cache,key) 获取缓存,如果没有缓存再进入到 BaseExecutor类里面,获取数据并且缓存到一级缓存里面,
然后会再放入到二级缓存里面,因此这里缓存的顺序应该是 二级缓存---》一级缓存--》数据库
总结,mybatis二级缓存需要开启并且需要再mapper里面设置 useCache=true 和 <cache></cache>标签,并且不同session之间的二级缓存是可以共享的
二一级缓存默认就是开启的,无法控制,只有当同一个session时缓存才会起作用。
这次就分享到这里,需要有不同意见的人可以留言,并且关注我的公众号 【码农翻生】
找梯子最重要的就是稳定,这个已经上线三年了,一直稳定没有被封过,赶紧下载备用吧!