文章目录

  • 一、搭建失效场景
    • 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)

最终到达该方法

该方法的大致上分为四部分:

  1. 去Sping的事务管理器中获取对应的SqlSessionHolder对象(包装了一层的sqlSession对象,可以理解为就是sqlSession对象)
  2. 如果不为空,就直接返回(缓存中有对应的sqlSession对象)
  3. 如果没有就开启对应的连接,这里是直接调用MyBatis中的openSession方法
  4. 然后将对应的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一级缓存失效相关推荐

  1. Spring整合MyBatis导致一级缓存失效问题

    熟悉MyBatis的小伙伴都知道MyBatis默认开启一级缓存,当我们执行一条查询语句后,MyBatis会以我们查询的信息生成一个缓存key,查询的结果为value,存到一个map中,即存入一级缓存. ...

  2. MyBatis一级缓存失效的几种情况

    MyBatis一级缓存失效的几种情况 文章目录 MyBatis一级缓存失效的几种情况 1 MyBatis一级缓存概述 2 四种失效的基本情况 3 几种特殊情况 1 MyBatis一级缓存概述 MyBa ...

  3. Mybatis基础学习之一级缓存和二级缓存的简单使用

    前言: 小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师. 这个Mybatis ...

  4. [mybatis]缓存_一级缓存_一级缓存失效的四种情况

    1.sqlSession不同 @Testpublic void test05() throws IOException {SqlSessionFactory sqlSessionFactory = g ...

  5. mybaits二十二:一级缓存失效的几种情况

    /*      *缓存      *  一级缓存(本地缓存),sqlSession级别的缓存,一级缓存是一直开启的.      *      与数据库同一次会话期间查询到的数据会放在本地缓存中.    ...

  6. MyBatis一级缓存及失效

    MyBatis提供了查询缓存机制,对于大量重复的查询使用缓存可以减轻数据库压力. 官网介绍 MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制. 为了使它更加强大而且易于配 ...

  7. Mybatis一级缓存,二级缓存的实现就是这么简单

    介绍 又到了一年面试季,所以打算写一点面试常问的东西,争取说的通俗易懂.面试高级岗,如果你说熟悉Mybatis,下面这些问题基本上都会问 Mybatis插件的实现原理? 如何写一个分页插件? Myba ...

  8. Mybatis一级缓存和二级缓存 Redis缓存

    一级缓存 Mybatis的一级缓存存放在SqlSession的生命周期,在同一个SqlSession中查询时,Mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对 ...

  9. 12干货!spring整合mybatis底层源码分析

    核心代码 1.解析配置类上的@MapperScan("com.liqi.mapper") @Import(MapperScannerRegistrar.class) 会调用Mapp ...

  10. mybatis一级缓存和二级缓存使用详解

    文章目录 一.概念说明 1.一级缓存 2.二级缓存 3.比较 二.mybatis缓存的生命周期 三.一级缓存的使用 四.二级缓存的使用 五.自定义二级缓存 六.mybatis缓存.spring缓存和r ...

最新文章

  1. pytorch图像分类_使用PyTorch和Streamlit创建图像分类Web应用
  2. 延迟分析中的案例研究:锁定与同步
  3. Linux网络编程服务器模型选择之并发服务器(下)
  4. 个人知识体系思维导图_“知识体系”打得好,学霸孩子跑不了,巧用“思维导图”来帮忙...
  5. linux中的变量前加__user,linux中的环境变量,别名,变量传递和函数块
  6. 发那科机器人示教器电缆线_FANUC发那科机器人示教盒维修
  7. 谈EBOM 、PBOM 、MBOM 在PDM中的统一
  8. 嵌入式开发中三种操作系统的分析与比较
  9. 复制瑞幸模式,出局的陆正耀再创业,要先开500家面馆
  10. SQLServer安装程序无法打开注册表项 UNKNOWN\Components\…的问题
  11. 用vue2写的开发者在线简历导出
  12. Logic Pro X中文汉化版
  13. SSL/TLS协议交互流程分析
  14. 电信光纤猫 f412超级密码
  15. 消除图片中指定颜色(框、线等)
  16. 测试基础:测试用例设计方法
  17. 从《流浪地球》撤资?——“数据思维”害的
  18. html下拉框 multiple,多选下拉控件的使用(select-option)multiple-select
  19. 利用python获取股票涨停板数据
  20. 实现一个函数,可以左旋字符串中的k个字符。

热门文章

  1. 存储基本概念与SAN存储
  2. history 历史命令
  3. linux下槽函数的响应时间,Qt信号与槽之connectSlotsByName函数
  4. mysql使用变量填值为数组_MySQL如何有效的存储IP地址?
  5. 树莓派python智能家居_用树莓派DIY一个智能家居服务器
  6. oracle 外连接内连接,oracle多表查询之内连接,外连接语句总结
  7. php api接口怎么写,php如何写api接口?
  8. spss中的aic值计算_常用SPSS数据处理方法,你都会吗?
  9. 吉林大学计算机专业张文政,张晋东 - 吉林大学 - 计算机科学与技术学院
  10. python 操作psd_python psd