mybatis源码考究二

1.mybatis整合spring解决sqlsession线程安全问题

2.mybatis整合spring一级缓存失效问题

mybatis结合spring使用

1.项目依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><optional>true</optional></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency><!-- mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.17</version><scope>runtime</scope></dependency><!-- mybatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.4</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.4</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency>

依赖说明:

本项目采用springboot,但不会直接引入mybatis-starter,这一部分选择手动配置,其中spring-mybatis依赖了spring-jdbc,这里直接用starter方式引入

2.整合配置

/*** @author authorZhao* @date 2020年05月26日*/
@Configuration
@EnableTransactionManagement
public class PlusMybatisConfiguration {/*** datasource在里jdbc-starter面已经配置了,jdbc默认数据源是HikariCP,据说性能很高* @param dataSource* @return* @throws IOException*//*@Bean@ConditionalOnMissingBean(DataSource.class)public DataSource dataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/no_name");dataSource.setPassword("root");dataSource.setUsername("root");return dataSource;}*//*** 配置的是工厂,实际是要生产SqlSessionFactory,通过SqlSessionFactory#opneSession()* @param dataSource* @return* @throws IOException*/@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {//数据源SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);//xml文件,注意mapper文件采用注解扫描PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();Resource[] resources = resolver.getResources("classpath:mybatis/*.xml");sqlSessionFactoryBean.setMapperLocations(resources);//配置类,这个可以读取xml也可以手写org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();//开启突驼峰映射configuration.setMapUnderscoreToCamelCase(true);sqlSessionFactoryBean.setConfiguration(configuration);return sqlSessionFactoryBean;}/*** TransactionManager在jdbc-starter里面已经自动配置了,可忽略*//*@Beanpublic TransactionManager transactionManager(DataSource dataSource){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource);}*/}

3.使用的话直接注入mapper就行

4.问题分析

spring-mybatis如何解决sqlsession线程安全问题,一级缓存为何失效

1.SqlSessionTemplate

spring默认都是单例模式,假设你有多个mapper那么问题来了,你的这些mapper用的是不是同一个sqlsession,因为在非spring环境mapper是sqlsession里面代理生成

整合spring之后可以看到spring-mybatis提供了mapper扫描注解,

@MapperScan 扫描的大致原理

mapper里面混有@Service

其实在spring里面为mapper代理的是MapperFactoryBean,也是一个工厂bean

看他的getObject方法

@Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);}

这里的sqlsession是SqlSessionTemplate,SqlSessionTemplate是spring-mybatis里面新加的

看源码getsession里面获取SqlSessionTemplate,但是发现SqlSessionTemplate初始化却在这里,问题来了,这个方法什么时候执行

 public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);}}

回到MapperFactoryBean方法这里,spring-mybatis扫描的时候有一段代码

definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);

这个就表明MapperFactoryBean创建完成之后就会进行属性赋值,执行这个set方法

说了这么多mapper是由SqlSessionTemplate生成的

2.线程安全和缓存问题源码跟踪

接着说SqlSessionTemplate为什么线程安全

下面以修改为例说明SqlSessionTemplate的线程安全问题和一级缓存失效问题

     @Autowiredprivate UserService userService;@Testpublic void tsst2(){User user = new User();user.setId(2);user.setName("曹操");user.setPassword("孟德");userService.updateUser(user);}

1.mapper执行方法就会到SqlSessionTemplate里面update方法

2.SqlSessionTemplate里面有一个sqlsession也是代理生成的看代理的方法

private class SqlSessionInterceptor implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//这个是获取真正干活的sqlsession,本质还是defalutsqlsessionSqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);try {//执行操作方法Object result = method.invoke(sqlSession, args);//判断有没有开启事务,如果没有开启事务就直接提交,提交之后那么一级缓存也就失效了,提交是会有清空缓存的操作if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {// force commit even on non-dirty sessions because some databases require// a commit/rollback before calling close()//提交事务sqlSession.commit(true);}return result;} catch (Throwable t) {Throwable unwrapped = unwrapThrowable(t);if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {// release the connection to avoid a deadlock if the translator is no loaded. See issue #22closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);sqlSession = null;Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);if (translated != null) {unwrapped = translated;}}throw unwrapped;} finally {if (sqlSession != null) {//关闭sqlsession,里面有文章,开启事务不会直接关闭,而是计量数减少,没有开启事务就直接关闭closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);}}}}

先看获取sqlsession的方法

 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) {notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);SqlSession session = sessionHolder(executorType, holder);if (session != null) {return session;}//注意这句话,这就是为什么经常有这句话打印的问题,开启事务就不会每次都创建session,不开启就会LOGGER.debug(() -> "Creating a new SqlSession");//默认sqlsession获取,本质就是defalutSqlSessionsession = sessionFactory.openSession(executorType);//注册到registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);return session;}

自己看一下这个TransactionSynchronizationManager类,这个类是spring的

这个类清一色的使用ThreadLocal,因此可以看到每个线程实际上用的是自己的sqlsession,所以能解决线程安全问题

上面一路操作,只能大概分析出线程安全和缓存失效的原因,事务管理还藏在其中

总结:

1.mybatis整合spring解决sqlsession线程安全问题

  • 每个线程实际上都是自己的sqlsession,关键点ThreadLocal

2.mybatis整合spring一级缓存失效问题

  • 关闭事务,缓存失效
  • 事务内部一级缓存有用

下回解决spring如何管理事务,以及声明式事务原理

mybatis源码考究二(sqlsession线程安全和缓存失效)相关推荐

  1. mybatis源码阅读(二):mybatis初始化上

    转载自  mybatis源码阅读(二):mybatis初始化上 1.初始化入口 //Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与 ...

  2. 手把手带你阅读Mybatis源码(二)执行篇

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 前言 上一篇文章提到了MyBatis是如何构建配置类的,也说了MyBatis在运行过程中主 ...

  3. ROS源码学习 二、线程池

    2021SC@SDUSC 目录 1.写在前面 2.ROS线程池概述 3.ROS线程池模型 4.ROS线程池源码详解 5.总结 1.写在前面 ROS作为一个操作系统,其职责是协调具有不同功能的node之 ...

  4. Mybatis源码学习二(一级缓存)

    一级缓存流程 一级缓存有效的因素 一级缓存有效测试 public class User {private Integer id;private String name;public Integer g ...

  5. mybatis源码分析4 - sqlSession读写数据库完全解析

    1 引言和主要类 创建完sqlSession实例后,我们就可以进行数据库操作了.比如通过selectOne()方法查询数据库,如代码 // 读取XML配置文件 String resource = &q ...

  6. Mybatis源码阅读(二)

    本文主要介绍Java中,不使用XML和使用XML构建SqlSessionFactory,通过SqlSessionFactory 中获取SqlSession的方法,使用SqlsessionManager ...

  7. mybatis源码分析3 - sqlSession的创建

    1 引言和主要类 初始化mybatis,也就是创建完单例SqlSessionFactory后,就进入到了mybatis的运行阶段.mybatis每次的运行都是通过SqlSession对象来进行,它是运 ...

  8. 初看Mybatis 源码 (二) Java动态代理类

    先抛出一个问题,用过Mybatis的都知道,我们只需要定义一个Dao的接口,在里面写上一些CRUD相关操作,然后配置一下sql映射文件,就可以达到调用接口中的方法,然后执行sql语句的效果,为什么呢? ...

  9. MyBatis 源码分析之 SqlSession 创建

    三哥 内容来自[自学星球] 欢迎大家来了解我的星球,和星主(也就是我)一起学习 Java ,深入 Java 体系中的所有技术.我给自己定的时间是一年,无论结果如何,必定能给星球中的各位带来点东西. 想 ...

最新文章

  1. DPDK — CLI 指令行模块
  2. html 弹出遮罩 iframe,iframe正在加载时显示遮罩层 加载完毕后显示iframe
  3. 十分钟内学会 Python
  4. 1.移动端测试知识笔记(面试必备,测试点,adb命令)
  5. 怎样用css设置图片下的投影,css – 做这种投影的最佳方法是什么?
  6. SQLAlchemy中filter_by()和filter()的用法不同
  7. 头像和Karma汽车
  8. 一张图看懂python编程
  9. 以FORWARD为例,分步骤演示Filter对转发请求的拦截效果
  10. 剑指offer面试题[59]-对称的二叉树
  11. Java实现棒子老虎鸡小游戏
  12. java 结束循环_java如何终止多层循环
  13. idea 破解版安装
  14. 考研题目 第五章 数组和广义表
  15. 推荐一款非常好看notepad++主题和字体
  16. 03.项目管理实践工具-团队绩效评价
  17. 高中数学知识点归纳总结三角函数与解三角形
  18. 蓝牙麦克风 android,带蓝牙麦克风的Android语音识别器
  19. SAP ABAP开发个别概念理论区分理解
  20. 传奇版本中云客户端状态在哪里去掉?

热门文章

  1. 注册表修改windows软件默认打开程序
  2. 浅谈自己学习设计模式的感受
  3. 包络检波仿真matlab,包络检波和相干解调仿真程序.doc
  4. 3.OSPF的协议报文和链路状态通告
  5. 10个你可能不知道的有趣的Gutenberg功能(一)
  6. php产品经理面试题目,12/11/17 百度PHP笔试题目整理
  7. ubuntu rm命令
  8. IAR 中设置 CSTACK HEAP作用
  9. java matlab混合编程_java和matlab混合编程
  10. 湿式自动喷水灭火系统实验实训,QY-LY62