Mybatis一级缓存和二级缓存的过程、区别和执行顺序(图文、很详细) |
您所在的位置:网站首页 › 一级和二级标签 › Mybatis一级缓存和二级缓存的过程、区别和执行顺序(图文、很详细) |
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不同(会话不同)
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(); } }
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级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。 映射语句文件中的所有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{ } }
说明:第二次又重新发送了sql,因为从二级缓存中取数据时,会话没关闭所以二级缓存中没数据,所以又去一级缓存中查询,也没有数据则发送了sql查数据库; 所以,只有会话关闭或提交后,一级缓存中的数据才会转移到二级缓存中,然后因为是同一个namespace所以可以获取到数据; 四、和缓存有关的设置/属性1)mybatis全局配置文件中配置全局缓存开启和清空 1.1)控制二级缓存的开启和关闭 cacheEnabled=true:开启缓存;false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)1.2)控制一级缓存的开启和关闭 localCacheScope:本地缓存作用域(一级缓存SESSION); 当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁用一级缓存;注意:一级缓存关闭后,二级缓存自然也无法使用; 2)方法中sqlSession清除缓存测试 sqlSession.clearCache();只是清除当前session的一级缓存; 如果openSession清空了缓存,即执行了openSession.clearCache()方法: 3)mapper.xml中也可以配置一级和二级缓存开启和使用 3.1)每个select标签都默认配置了useCache=“true”: 如果useCache= false:则表示不使用缓存(一级缓存依然使用,二级缓存不使用) 3.2)每个增删改标签默认配置了flushCache=“true”:(一级二级都会清除) 1、先判断二级缓存是否开启,如果没开启,再判断一级缓存是否开启,如果没开启,直接查数据库 2、如果一级缓存关闭,即使二级缓存开启也没有数据,因为二级缓存的数据从一级缓存获取 3、一般不会关闭一级缓存 4、二级缓存默认不开启 5、如果二级缓存关闭,直接判断一级缓存是否有数据,如果没有就查数据库 6、如果二级缓存开启,先判断二级缓存有没有数据,如果有就直接返回;如果没有,就查询一级缓存,如果有就返回,没有就查询数据库; 综上:先查二级缓存,再查一级缓存,再查数据库;即使在一个sqlSession中,也会先查二级缓存;一个namespace中的查询更是如此;缓存执行顺序是:二级缓存–>一级缓存–>数据库 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |