Mybatis主要流程源码分析

news2024/11/16 21:45:26

分层架构图

image

主要流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d9uhH9IK-1677658947721)(https://note.youdao.com/yws/public/resource/7f152b4b25320263d411a49583d3f4db/xmlnote/WEBRESOURCE9ea90840088eaeaa4a463bbc3f1912e8/17619)]

主要流程总结:

  • 1.获取sqlSessionFactory对象
  • 2.获取sqlSession对象
  • 3.获取mapper接口的代理对象(MapperProxy)
  • 4.执行增删改查方法

源码分析

0、mybatis的数据源配置文件

我们的项目一般都是spring,在集成mybatis的时候一定会有一个数据源的配置,用来初始化mybatis的各种内置组件、解析xml文件等功能。如:

@Configuration
@MapperScan(basePackages = "com.xxx.xxx.mapper", sqlSessionTemplateRef = "sqlSessionTemplate")
public class DataSourceConfig {
    
    public static final Logger logger = LogManager.getLogger(DataSourceConfig.class);
    
    @Value("${mysql.lion.mapperLocations}")
    private String mapperLocations;
    
    // 加载全局的配置文件
    @Value("${mysql.configLocation}")
    private String configLocation;
    
    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("routeDataSource") DataSource dataSource) {
        logger.info("--------------------  sqlSessionFactory init ---------------------");
        try {
            SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
            sessionFactoryBean.setDataSource(dataSource);
            
            // 设置mapper.xml文件所在位置
            Resource[] resources = new PathMatchingResourcePatternResolver().getResources(mapperLocations);
            sessionFactoryBean.setMapperLocations(resources);
            // 设置mybatis-config.xml配置文件位置
            sessionFactoryBean.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
            
            return sessionFactoryBean.getObject();
        } catch (IOException e) {
            logger.error("mybatis resolver mapper*xml is error", e);
        } catch (Exception e) {
            logger.error("mybatis sqlSessionFactoryBean create error", e);
        }
        return null;
    }
}

断点打在这个的return处,我们启动项目,就能开始deug分析源码了,让我们发车!

1、SqlSessionFactoryBean

按照上面的步骤,我们一直跟踪源码,会发现得到了一个 DefaultSqlSessionFactory 对象,其实从类名字就不难看出,这是一个生成 SqlSession 对象的工厂类。具体时序图:

image

我们简单看一下最终初始化出来了的 SqlSessionFactory :

image

SqlSessionFactory是应用程序级别的,一次启动就只会初始化一个单例对象。

2、获取sqlSession对象

这个sqlSession对象比较复杂,因为要和spring集成,就存在代理的场景,我们先看服务启动的时候,debug结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OkjE5bKy-1677658947724)(https://note.youdao.com/yws/public/resource/7f152b4b25320263d411a49583d3f4db/xmlnote/WEBRESOURCEa6d69245d97e98e191c760dd0d0718d0/17717 “image”)]

注意这里的四大对象,都非常有用。

并且,这时候的sqlSession对象是代理对象。

项目启动完成之后,需要用到mybatis操作数据库的时候,每次请求都会创建一个sqlSession对象,也就是说这个对象是线程不安全的。

不知道你有没有这样的疑问,为什么这里的四个主流程是创建sqlSession对象在前,创建Mapper对象在后,一定是这样吗??在debug源码的时候怎么验证呢??

那么,这个代理对象的invoke方法是什么时候被执行的呢?我们一起debug一次请求,断点就放在这个方法处,断住以后,我们发现调用栈是:

image

这就将sqlSession对象和Mapper对象的创建先后关系展示的很清楚了。

说清楚了这两个的先后关系,我们再来着重看一下SeqSession对象创建的时序图:

image

主流程代码粘贴:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    final Executor executor = configuration.newExecutor(tx, execType);
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    closeTransaction(tx); // may have fetched a connection so lets call close()
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

注意看这里的finally模块,每次创建完sqlSession对象都会reset 当前的sqlSession的相关信息。

3、获取Mapper

其实,在刚开始用mybatis的时候,我们可能都有疑惑,为什么我们定义了一个接口,加了@Mapper注解就能执行我们的SQL了?这里面用的就是代理技术,实际执行SQL的是代理类。

那么,在一次请求中,Mapper是如何获取的呢?

简单看下时序图:

image

补充说明几个点:

  • Mapper中的数据主要分两部分,一部分是项目启动的时候解析出来,然后加载进去的,一部分是代理类执行了invoke方法附加进去的

  • 在项目启动的时候就会把Mapper接口加载并解析存储到Configuration对象,这里从Configuration对象中再去拿具体的Mapper

    这部分的代码比较直观,结合上面第二步的创建SeqSession对象的debug调用栈就能梳理清楚。

4、获取和使用目标对象

创建完Mapper代理对象之后,就要执行业务请求的具体操作了。

先看一下时序图:

image

我们顺着时序图,一直跟踪下去,会的带断点图:

image

到这里,已经清楚地肯见了JDBC的接口,并且执行了具体的SQL操作。

对于查询请求,还有一个封装结果集的操作,我们再发起一个查询请求,debug一下:

image

5、spring是怎么保证SeqSession线程安全性的

回答这个问题,我们想回顾一下第2步,创建SeqSession对象的逻辑,这里粘贴一下SeqSessionUtils的getSeqSession方法的源码:

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
    PersistenceExceptionTranslator exceptionTranslator) {

  notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
  notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

  // 注意这一行代码是如何获取SeqSession对象的
  SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

  SqlSession session = sessionHolder(executorType, holder);
  if (session != null) {
    return session;
  }

  LOGGER.debug(() -> "Creating a new SqlSession");
  session = sessionFactory.openSession(executorType);

  registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

  return session;
}

然后,我们将 TransactionSynchronizationManager 展开说说:

public abstract class TransactionSynchronizationManager {

   private static final ThreadLocal<Map<Object, Object>> resources =
         new NamedThreadLocal<>("Transactional resources");

   private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
         new NamedThreadLocal<>("Transaction synchronizations");

   private static final ThreadLocal<String> currentTransactionName =
         new NamedThreadLocal<>("Current transaction name");

   private static final ThreadLocal<Boolean> currentTransactionReadOnly =
         new NamedThreadLocal<>("Current transaction read-only status");

   private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
         new NamedThreadLocal<>("Current transaction isolation level");

   private static final ThreadLocal<Boolean> actualTransactionActive =
         new NamedThreadLocal<>("Actual transaction active");

所以,结论找到了:spring将SqlSession通过 TransactionSynchronizationManager 的ThreadLocal 管理起来了,由此实现了线程安全性。

补充

  • SqlSessionTemplate是Mybatis为了接入Spring提供的Bean。通过TransactionSynchronizationManager中的ThreadLocal<Map<Object, Object>>保存线程对应的SqlSession,实现session的线程安全
  • SqlSessionManager是Mybatis不接入Spring时用于管理SqlSession的Bean。通过SqlSessionManagger的ThreadLocal实现session的线程安全。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/381234.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

索引优化与查询优化

1.哪些维度可以进行数据库调优 索引失效、没有充分利用到索引–》索引建立关联查询太多JOIN (设计缺陷或不得已的需求)–》SQL优化服务器调优及各个参数设置(缓冲、线程数等)–》调整my.cnf数据过多–》分库分表 大方向上完全可以分成 物理查询优化和 逻辑查询优化 两大块 物…

记录一次挖矿病毒kthreaddk和rcu_bj,导致CPU飙高处理

htop命令 存在kthreaddk和rcu_bj进程&#xff0c;cpu飙高 占用一般cpu或者70-80% 1、检查定时任务 查看是否有 # crontab -l 检查root账号是否有异常定时任务 有的话crontab -e 修改定时任务保存 并检查所有的用户有没有定时任务( 注&#xff1a;我的是gitlab git账户被入侵)异…

Nginx 配置详解(二)

序言Nginx的代理功能与负载均衡功能是最常被用到的&#xff0c;描述一些关于代理功能的配置&#xff0c;再说明负载均衡详细。Nginx 代理服务的配置说明1、设置 404 页面导向地址error_page 404 https://www.runnob.com; #错误页 proxy_intercept_errors on; #如果被代理服务…

PowerShell Install Mysql 5.7

MySQL介绍 MySQL 是最流行的关系型数据库管理系统&#xff0c;在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System&#xff1a;关系数据库管理系统)应用软件之一。 mysql download Mysql ServerdownloadPowershell 使用使用参数参考 前提条件 开启…

机器学习算法-KNN、决策树

目录1、最近邻算法 KNN1.1 K的选择1.2 案例&#xff1a;鸢尾花2、决策树2.1 决策树介绍2.2 案例&#xff1a;鸢尾花数据2.3 补充1、最近邻算法 KNN 原理&#xff1a;找到K 个与新数据最近的样本&#xff0c;取样本中最多的一个类别作为新数据的类别 要点&#xff1a;距离—是欧…

Three.js上手——搭建Vue3+Three.js项目

上一篇文章 Three.js初试 介绍了一些 Three.js 的基本概念&#xff0c;这一篇主要是介绍一下它的应用。 结合 Vue3 Vite 一起搭建一个项目。 项目初始化 Vite 项目构建 兼容性注意 Vite 需要 Node.js 版本 14.18&#xff0c;16。然而&#xff0c;有些模板需要依赖更高的 Nod…

代码随想录【Day29】|491. 递增子序列、46. 全排列、47. 全排列 II

491. 递增子序列 题目链接 题目描述&#xff1a; 给定一个整型数组, 你的任务是找到所有该数组的递增子序列&#xff0c;递增子序列的长度至少是2。 示例: 输入: [4, 6, 7, 7]输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]] 说明: …

教务选课排课系统

技术&#xff1a;Java、JSP等摘要&#xff1a;Internet网是目前全球最大的计算机通信网&#xff0c;它遍及全球几乎所有的国家和地区。www系统是一个大型的分布式超媒体信息数据库&#xff0c;它极大的推动了Internet的发展&#xff0c;己经成为Internet中最流行、最主要的信息…

防抖节流函数

防抖和节流对于每一个开发者来说&#xff0c;都不陌生。防抖和节流的概念其实最早并不是出现在软件工程中&#xff0c;防抖是出现在电子元件中&#xff0c;节流出现在流体流动中。 而JavaScript是事件驱动的&#xff0c;大量的操作会触发事件&#xff0c;加入到事件队列中处理…

骨传导耳机发声原理是什么,如何选购骨传导耳机

骨传导耳机很早以前就已经有了&#xff0c;但真正流行到运动圈里也还是最近两年的时间&#xff0c;所以导致很多人对骨传导耳机还不是很了解&#xff0c;不明白其工作原理是什么&#xff0c;在购买骨传导耳机时也总是会感到纠结&#xff0c;不知如何下手&#xff0c;作为一个多…

香港双重牌照、准入安排和禁止事项等重要制度已明确 20多万字的《虚拟资产咨询文件》以证雄心

前不久&#xff0c;香港证监会就加密货币交易发布的《有关适用于获证券及期货事务监察委员会发牌的虚拟资产交易平台营运者的建议监管规定的咨询文件》&#xff08;以下简称《咨询文件》&#xff09;&#xff0c;并如期就有关监管虚拟资产交易平台的建议展开咨询&#xff0c;以…

制造业数据质量提升的方法和实践

制造业的数据治理尚处于早期阶段&#xff0c;而数据质量管理是所有数据类项目重点关注的领域。数据治理以数据标准、数据质量和元数据的管理为根本&#xff0c;是企业实现数据资产价值创造的基础。上周&#xff0c;在由武汉市经济和信息化局主办的“万企育才工程”之《制造业数…

随机森林在sklearn中的实现

目录 一.集成算法 二.sklearn中的集成算法模块ensemble 三.RandomForestClassifier(随机森林分类器) 四.重要参数 1.基评估器参数 2.随机森林参数 五.重要属性和接口 六.Bagging的另一个必要条件 七.RandomForestRegressor(随机森林回归器) 八.机器学习中调参的基本思…

2023 年 6 大智能合约语言

如果你想成为一名 Web3 开发人员&#xff0c;你需要知道如何编写智能合约&#xff0c;智能合约是所有 Web3 应用程序的支柱。 简而言之&#xff0c;智能合约是在区块链网络上部署和执行的计算机程序&#xff0c;提供确定性保证&#xff0c;使多方能够达成一致的、防篡改的结果…

CSDN时隔一年,我又回来了还愿

CSDN时隔一年&#xff0c;我又回来了还愿 去年的今天我申请到了Jetbrains学生试用,前两天刚买了JetBrains。 特别感谢Jetbrains和Jetbrains的客服小姐姐。 情况说明&#xff1a; 由于本人读非全日制大学&#xff0c;每周也同全日制一样上五天学放二两天。首先非全日制已经不符…

vue - vue是mvvm模型吗?

先说明一下什么是标准的mvvm模型&#xff1a; M: Model&#xff1b;既是数据&#xff0c;主要负责业务数据相关&#xff1b;V: View&#xff1b;即是视图&#xff0c;展示给用户看的页面&#xff0c;细分下来就是htmlcss层&#xff1b;VM: ViewModel&#xff1b;是连接界面View…

ZOJ-搜索专题

1002 题意 思路 深搜&#xff0c;每个格子都搜一遍。技巧dfs(cnt,ans)dfs(第几个格子&#xff0c;答案)&#xff1b; 代码 #include <iostream>using namespace std;int n,i,j,ans; char s[5][5];int c_put(int n,int m) {for (i n-1;i > 0;i --) {if (s[i][m] …

怎么解决SPA首屏加载速度慢?

首屏加载 首屏时间&#xff08;First Contentful Paint&#xff09;&#xff0c;指的是浏览器从响应用户输入网址地址&#xff0c;到首屏内容渲染完成的时间&#xff0c;此时整个网页不一定要全部渲染完成&#xff0c;但需要展示当前视窗需要的内容 首屏加载可以说是用户体验…

LeetCode第2577题-在网格图中访问一个格子的最少时间-python实现-图解思路与手撕代码

LeetCode第2577题-在网格图中访问一个格子的最少时间-python实现-图解思路与手撕代码 文章目录一、题目描述二、解题思路与代码实现1.解题思路2.代码实现总结一、题目描述 二、解题思路与代码实现 1.解题思路 这道题求最短距离&#xff0c;首先检查起始点0,0的右边0,1和下边1…

Linux配置mysql主从复制

Linux配置mysql主从复制 systemctl restart mysqld 重启mysql服务 Mysql主从复制 在linux里面部署mysql 主库Master 192.168.162.138 contos 7 从库Slave 192.168.162.137 contos 7测试 log-binmysql-bin町[必须]启用二进制日志 第三步:登录Mysql数据库&#xff0c;执…