Spring整合MyBatis为什么导致MyBatis一级缓存失效
文章目录
- 一、搭建失效场景
- 1、使用MyBatis查询同一条SQL
- 2、使用Spring整合MyBatis查询同一条SQL
- 二、原理分析(重点)
- 1、明白的其它知识点
- 2、调用入口是接口中getObject方法
- 3、为什么加了@Transactional注解就可以使用缓存
- 4、再总结
如题,Spring整合MyBatis为什么导致MyBatis的一级缓存失效?
一、搭建失效场景
测试用例为,查询一个String类型的字符串
基础的环境配置、扫描路径配置就不再重复,直接看测试数据相关的代码信息
1、对应的mapper接口及对应的SQL语句(返回一个mobian的字符串)
1、使用MyBatis查询同一条SQL
1)直接调用编写好的工具类,开启对应的连接,执行三条一模一样的查询
2)查看对应的测试结果
熟悉MyBatis的小伙伴都知道MyBatis默认开启一级缓存,当我们执行一条查询语句之后,MyBatis会以我们查询的信息生成一个缓存key,查询的结果为value,存到一个map中,即存入一级缓存,下面是具体的源码,感兴趣可以看看。
缓存key生成的地方
每次 查询完会将查询的结果放入缓存池中
如果对应的LocalCacheScope是STATEMENT类型,那么就清空缓存池中的缓存(清除MyBatis中的一级缓存)
3)关闭一级缓存后再次测试
配置对应的LocalCacheScope类型,配置为STATEMENT类型,即关闭一级缓存
测试结果:
我们会发现,此时打印了三次同样的SQL,即每一次查询都是单独的去数据库查询
2、使用Spring整合MyBatis查询同一条SQL
1)同样查询三次SQL,直接访问对应的路径即可完成测试
2)查看对应的测试结果
我们在测试Spring整合MyBatis的用例时,MyBatis的一级缓存使用默认值,即开启一级缓存,但是我们的测试结果却表明,MyBatis的一级缓存好像没起作用。这也就时本文的重点——Spring整合MyBatis后一级缓存失效
3)添加@Transactional注解
此时我们的测试结果表明,三条SQL查询又使用了缓存
二、原理分析(重点)
结论:Spring将MyBatis的DefaultSqlSession类替换成了SqlSessionTemplate。
MyBatis的一级缓存是基于SqlSession来实现的,对应MyBatis中sqlSession接口的默认实现类是DefaultSqlSession,如果执行的SQL相同时,并且使用的是同一个SqlSession对象,那么就会触发对应的缓存机制。
但是在Spring整合MyBatis后,Spring使用MyBatis不再是直接调用MyBatis中的信息,而是通过调用调用mybatis-spring.jar中的类,继而达到间接调用MyBatis的效果。但在mybatis-spring.jar中,引入了一个SqlSessionTemplate类,它和Spring的事务管理器共同配合,创建对应的SqlSession连接。
即在没有添加@Transactional注解的情况下,每调用一次查询SQL,就会通过SqlSessionTemplate去创建sqlSession,即相当于新创建一次连接,故而每次查询在调试结果看来就是一级缓存失效。
结尾处还有原理总结,请一定要看完!!!
1、明白的其它知识点
本文关于MyBatis中Mapper接口与Spring中Bean相互整合的部分就不再过多重复,感兴趣的可以查看我之前的写的博客。
@Autwired自动注入XxxMapper接口原理(含mybstis-spring.jar源码)
最终得到的结论就是,Spring中使用@Autwired注解,注入xxMapper接口的时候,实际上是注册的是一个代理对象MapperFactoryBean(Spring的Bean是会默认忽略接口的,所以对应的扫描成为BD的逻辑也就是靠mybatis-spring来完成),注册了多少个mapper接口,就会生成多少个MapperFactoryBean代理对象,只是每个代理对象的mapperInterface不同罢了。
想要明白为什么可以通过该方式实现调用不同类型的Mapper代理出来的Bean的原理,需要你了解访问实现了FactoryBean接口的Bean的访问流程,巧了,你又有地方可以参考了:
浅谈使用实现FactoryBean接口的方式定义Bean
2、调用入口是接口中getObject方法
1)
为什么这个方法是入口,也请参考上面的博文浅谈使用实现FactoryBean接口的方式定义Bean
获取到对应的sqlSession,然后getMapper。如果你记得只MyBatis查询sql的时候你就会发现这两者的长相非常相似,区别也就仅仅存在于getSession现在是一个方法,而MyBatis是一个对象
2)
点开方法会会发现,该方法也就是一个sqlSession。顺着该对象的实例化流程继续往下看,即进如SqlSessionTemplate类中
3)
最后会进入到这个位置,即在实例化sqlSessionTemplate对象时,会实例化这些属性,查看这些属性的类型就会发现,sqlSessionProxy就是我们最终需要的sqlSession对象
4)
继续跟进对应的SqlSessionInterceptor代理逻辑部分代码,会发现sqlSession是调用getSqlSession方法生成,继续跟进
5)
最终到达该方法
该方法的大致上分为四部分:
- 去Sping的事务管理器中获取对应的SqlSessionHolder对象(包装了一层的sqlSession对象,可以理解为就是sqlSession对象)
- 如果不为空,就直接返回(缓存中有对应的sqlSession对象)
- 如果没有就开启对应的连接,这里是直接调用MyBatis中的openSession方法
- 然后将对应的sqlSession对象注册到缓存中(第二步就是往这个缓存里面获取)
3、为什么加了@Transactional注解就可以使用缓存
6)
核心就是注册的方法,我测试的场景是没有加@Transactional注解的时候,此处判断为false就不会再向缓存中添加数据。
当然如果判断成功就是会调用TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory))方法,将该sqlSession对象添加到对应的缓存中,数量+1
即最终注册到synchronizations对象的缓存中
7)
缓存池使用的是一个ThreadLocal(用于处理多个线程中数据的隔离问题,内部维护一个ThreadLocalMap)来存储
synchronizations = new NamedThreadLocal<>(“Transaction synchronizations”);
我认为,在MyBatis中使用sqlSession对象进行数据查询时,当遇到并发量比较大的一些情况时,就有可能出现线程不安全的情况,换句话说就是事务之间的隔离性没有做好。
该类是Spring中的事务同步管理器类,synchronizations集合用于存放每一个线程的事务同步器,即存放一个线程的所有事务,使用ThreadLocal类型来存储,可以保证不同线程之间的数据安全性。
4、再总结
如果我们没有添加@Transactional注解,Spring认为我的每一次查询都都是相互独立的,便开启了三次不同的事务也即是创建了三个不同的sqlSession对象。即无法使用到MyBatis的一级缓存。
如果我们添加了@Transactional注解,Spring在执行了第一次查询后,会将当前线程的事务情况存储到synchronizations 的集合中,当第二次再执行查询的时候,能够在缓存中直接获取到当前的事务情况(包含sqlSession对象),即不会再去调用openSession方法,继而创建一个新的sqlSession对象,而是使用缓存中的sqlSession对象(对应的代码在上面的第五步)。这就保证了在添加@Transactional注解的情况下,能够走MyBatis的一级缓存
Spring整合MyBatis为什么导致MyBatis一级缓存失效相关推荐
- Spring整合MyBatis导致一级缓存失效问题
熟悉MyBatis的小伙伴都知道MyBatis默认开启一级缓存,当我们执行一条查询语句后,MyBatis会以我们查询的信息生成一个缓存key,查询的结果为value,存到一个map中,即存入一级缓存. ...
- MyBatis一级缓存失效的几种情况
MyBatis一级缓存失效的几种情况 文章目录 MyBatis一级缓存失效的几种情况 1 MyBatis一级缓存概述 2 四种失效的基本情况 3 几种特殊情况 1 MyBatis一级缓存概述 MyBa ...
- Mybatis基础学习之一级缓存和二级缓存的简单使用
前言: 小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师. 这个Mybatis ...
- [mybatis]缓存_一级缓存_一级缓存失效的四种情况
1.sqlSession不同 @Testpublic void test05() throws IOException {SqlSessionFactory sqlSessionFactory = g ...
- mybaits二十二:一级缓存失效的几种情况
/* *缓存 * 一级缓存(本地缓存),sqlSession级别的缓存,一级缓存是一直开启的. * 与数据库同一次会话期间查询到的数据会放在本地缓存中. ...
- MyBatis一级缓存及失效
MyBatis提供了查询缓存机制,对于大量重复的查询使用缓存可以减轻数据库压力. 官网介绍 MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制. 为了使它更加强大而且易于配 ...
- Mybatis一级缓存,二级缓存的实现就是这么简单
介绍 又到了一年面试季,所以打算写一点面试常问的东西,争取说的通俗易懂.面试高级岗,如果你说熟悉Mybatis,下面这些问题基本上都会问 Mybatis插件的实现原理? 如何写一个分页插件? Myba ...
- Mybatis一级缓存和二级缓存 Redis缓存
一级缓存 Mybatis的一级缓存存放在SqlSession的生命周期,在同一个SqlSession中查询时,Mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对 ...
- 12干货!spring整合mybatis底层源码分析
核心代码 1.解析配置类上的@MapperScan("com.liqi.mapper") @Import(MapperScannerRegistrar.class) 会调用Mapp ...
- mybatis一级缓存和二级缓存使用详解
文章目录 一.概念说明 1.一级缓存 2.二级缓存 3.比较 二.mybatis缓存的生命周期 三.一级缓存的使用 四.二级缓存的使用 五.自定义二级缓存 六.mybatis缓存.spring缓存和r ...
最新文章
- pytorch图像分类_使用PyTorch和Streamlit创建图像分类Web应用
- 延迟分析中的案例研究:锁定与同步
- Linux网络编程服务器模型选择之并发服务器(下)
- 个人知识体系思维导图_“知识体系”打得好,学霸孩子跑不了,巧用“思维导图”来帮忙...
- linux中的变量前加__user,linux中的环境变量,别名,变量传递和函数块
- 发那科机器人示教器电缆线_FANUC发那科机器人示教盒维修
- 谈EBOM 、PBOM 、MBOM 在PDM中的统一
- 嵌入式开发中三种操作系统的分析与比较
- 复制瑞幸模式,出局的陆正耀再创业,要先开500家面馆
- SQLServer安装程序无法打开注册表项 UNKNOWN\Components\…的问题
- 用vue2写的开发者在线简历导出
- Logic Pro X中文汉化版
- SSL/TLS协议交互流程分析
- 电信光纤猫 f412超级密码
- 消除图片中指定颜色(框、线等)
- 测试基础:测试用例设计方法
- 从《流浪地球》撤资?——“数据思维”害的
- html下拉框 multiple,多选下拉控件的使用(select-option)multiple-select
- 利用python获取股票涨停板数据
- 实现一个函数,可以左旋字符串中的k个字符。
热门文章
- 存储基本概念与SAN存储
- history 历史命令
- linux下槽函数的响应时间,Qt信号与槽之connectSlotsByName函数
- mysql使用变量填值为数组_MySQL如何有效的存储IP地址?
- 树莓派python智能家居_用树莓派DIY一个智能家居服务器
- oracle 外连接内连接,oracle多表查询之内连接,外连接语句总结
- php api接口怎么写,php如何写api接口?
- spss中的aic值计算_常用SPSS数据处理方法,你都会吗?
- 吉林大学计算机专业张文政,张晋东 - 吉林大学 - 计算机科学与技术学院
- python 操作psd_python psd