Mybatis一级缓存和二级缓存的过程、区别和执行顺序(图文、很详细)

您所在的位置:网站首页 一级和二级标签 Mybatis一级缓存和二级缓存的过程、区别和执行顺序(图文、很详细)

Mybatis一级缓存和二级缓存的过程、区别和执行顺序(图文、很详细)

2023-12-18 18:38| 来源: 网络整理| 查看: 265

Mybatis一级缓存和二级缓存的区别

缓存原理图: 在这里插入图片描述

一级缓存

Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。 每一次会话都对应自己的一级缓存,作用范围比较小,一旦会话关闭就查询不到了。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

生命周期 a、MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

b、如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。

c、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。

d、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用    判断某两次查询是完全相同的查询 mybatis认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询。

2.1 传入的statementId

2.2 查询时要求的结果集中的结果范围

2.3. 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )

2.4 传递给java.sql.Statement要设置的参数值    一级缓存失效的四种情况:

1、sqlsession不同(会话不同) 在这里插入图片描述 在这里插入图片描述 2、sqlsession相同,查询缓存中没有的数据

@Test public void testFirstLevelCache() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try{ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp01 = mapper.getEmpById(1); System.out.println(emp01); //sqlSession相同,查询条件不同 Employee emp03 = mapper.getEmpById(3); System.out.println(emp03); System.out.println(emp01==emp03); }finally{ openSession.close(); } }

在这里插入图片描述 当缓存中没有数据时,会重新查数据库

3、sqlsession相同,但两次查询之间执行了增删改操作

@Test public void testFirstLevelCache() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try{ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp01 = mapper.getEmpById(1); System.out.println(emp01); //sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响) mapper.addEmp(new Employee(null, "testCache", "cache", "1")); System.out.println("数据添加成功"); Employee emp02 = mapper.getEmpById(1); System.out.println(emp02); System.out.println(emp01==emp02); }finally{ openSession.close(); } }

在这里插入图片描述 原因:每个增删改标签都有默认清空缓存配置:flushCache=“true”,不过这是默认的是一级和二级缓存都清空

4、sqlsession相同,但手动清楚了一级缓存(缓存清空)

清空缓存:openSession.clearCache();

@Test public void testFirstLevelCache() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try{ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp01 = mapper.getEmpById(1); System.out.println(emp01); //sqlSession相同,手动清除了一级缓存(缓存清空) openSession.clearCache(); Employee emp02 = mapper.getEmpById(1); System.out.println(emp02); System.out.println(emp01==emp02); }finally{ openSession.close(); } }

在这里插入图片描述 手动清空缓存后,需要重新查数据库

二级缓存

MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。 在这里插入图片描述 SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置就可以开启缓存了,如果我们配置了二级缓存就意味着:

映射语句文件中的所有select语句将会被缓存。 映射语句文件中的insert、update和delete语句会刷新缓存。 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。 根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。

基于namespace名称空间级别的缓存:一个namespace对应一个二级缓存 即一个mapper.xml对应一个缓存:

1、工作机制:

1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;

2、如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;

3、sqlSession=EmployeeMapper>Employee DepartmentMapper===>Department 不同namespace查出的数据会放在自己对应的缓存中(map) 效果:数据会从二级缓存中获取 查出的数据都会被默认先放在一级缓存中。 只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中

2、 使用:

1)开启全局二级缓存配置: 在这里插入图片描述

2)去mapper.xml中配置使用二级缓存 :

select * from tbl_employee where last_name like #{lastName}

或者 eviction:缓存的回收策略: • LRU – 最近最少使用的:移除最长时间不被使用的对象。 • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。 • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。 • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 • 默认的是 LRU。 flushInterval:缓存刷新间隔。缓存多长时间清空一次,默认不清空,设置一个毫秒值 readOnly:是否只读: true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。 mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快 false:非只读:mybatis觉得获取的数据可能会被修改。 mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢 size:缓存存放多少元素; type="":指定自定义缓存的全类名; 实现Cache接口即可;

select * from tbl_employee where last_name like #{lastName}

3)我们的POJO需要实现序列化接口

测试: 注意:需要openSession.close();后,才能从二级缓存中查数据;

@Test public void testSecondLevelCache() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); SqlSession openSession2 = sqlSessionFactory.openSession(); try{ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class); Employee emp01 = mapper.getEmpById(1); System.out.println(emp01); openSession.close(); //第二次查询是从二级缓存中拿到的数据,并没有发送新的sql Employee emp02 = mapper2.getEmpById(1); System.out.println(emp02); openSession2.close(); }finally{ } }

在这里插入图片描述 从二级缓存中拿数据 在这里插入图片描述 如果openSession.close();在第二次查询之后才关闭,则第二次查询会从一级缓存中查,如果不是一个session,则查询不到数据:

@Test public void testSecondLevelCache() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); SqlSession openSession2 = sqlSessionFactory.openSession(); try{ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class); Employee emp01 = mapper.getEmpById(1); System.out.println(emp01); Employee emp02 = mapper2.getEmpById(1); System.out.println(emp02); openSession.close(); openSession2.close(); }finally{ } }

在这里插入图片描述

说明:第二次又重新发送了sql,因为从二级缓存中取数据时,会话没关闭所以二级缓存中没数据,所以又去一级缓存中查询,也没有数据则发送了sql查数据库;

所以,只有会话关闭或提交后,一级缓存中的数据才会转移到二级缓存中,然后因为是同一个namespace所以可以获取到数据;

四、和缓存有关的设置/属性

1)mybatis全局配置文件中配置全局缓存开启和清空

1.1)控制二级缓存的开启和关闭

cacheEnabled=true:开启缓存;false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)

1.2)控制一级缓存的开启和关闭

localCacheScope:本地缓存作用域(一级缓存SESSION); 当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁用一级缓存;

注意:一级缓存关闭后,二级缓存自然也无法使用;

2)方法中sqlSession清除缓存测试

sqlSession.clearCache();只是清除当前session的一级缓存;

如果openSession清空了缓存,即执行了openSession.clearCache()方法: 在这里插入图片描述 在这里插入图片描述 openSession清空缓存不影响二级缓存;只清空了一级缓存;因为在openSession.close()时,就将一级缓存保存至了二级缓存;

3)mapper.xml中也可以配置一级和二级缓存开启和使用

3.1)每个select标签都默认配置了useCache=“true”: 如果useCache= false:则表示不使用缓存(一级缓存依然使用,二级缓存不使用) 3.2)每个增删改标签默认配置了flushCache=“true”:(一级二级都会清除) 在这里插入图片描述 在这里插入图片描述 查询标签默认flushCache=“false”:如果flushCache=true;每次查询之后都会清空缓存;一级和二级缓存都无法使用

Mybatis的一级缓存和二级缓存执行顺序

1、先判断二级缓存是否开启,如果没开启,再判断一级缓存是否开启,如果没开启,直接查数据库

2、如果一级缓存关闭,即使二级缓存开启也没有数据,因为二级缓存的数据从一级缓存获取

3、一般不会关闭一级缓存

4、二级缓存默认不开启

5、如果二级缓存关闭,直接判断一级缓存是否有数据,如果没有就查数据库

6、如果二级缓存开启,先判断二级缓存有没有数据,如果有就直接返回;如果没有,就查询一级缓存,如果有就返回,没有就查询数据库;

综上:先查二级缓存,再查一级缓存,再查数据库;即使在一个sqlSession中,也会先查二级缓存;一个namespace中的查询更是如此;缓存执行顺序是:二级缓存–>一级缓存–>数据库



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3