MyBatis执行一条sql语句的流程(源码解析)

embedded/2025/1/19 4:42:00/

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

MyBatis执行一条sql语句的流程(源码解析)

  • MyBatis执行sql语句的流程
  • 加载配置文件
    • 加载配置文件的流程
  • 创建sqlsessionFactory对象
    • 解析Mapper
    • 创建sqlsessionFactory流程
  • 获取sqlsession
  • 创建mapper代理
  • 执行mapper方法
    • 一级缓存和二级缓存
  • 总结:MyBatis执行一条sql语句的流程


sql_7">MyBatis执行sql语句的流程

    //<1> 加载配置文件InputStream is = Resources.getResourceAsStream("mybatis-config.xml");//<2> 创建sessionFactory对象SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);//<3> 获取sqlSession对象信息SqlSession session = sessionFactory.openSession();//<4> 构建映射器的代理对象SysUserMapper mapper = session.getMapper(SysUserMapper.class);//<5>调用相关方法信息SysUserPO sysUserPO = mapper.selectById(1L);

加载配置文件

我们进入<1>处的方法的:
在这里插入图片描述
我们发现一个陌生的对象classLoaderWrapper,看着像一个类加载器,我们进入这个类中查看:
在这里插入图片描述
发现这个对象包含两个类加载器,那么classLoaderWrapper也是关于进行类加载的对象。
我们进入getResourceAsStream方法中查看:
在这里插入图片描述
注意这里传入了一个空的类加载器对象,进入最底层:
在这里插入图片描述我们发现这里传入了一个类加载器的数组,并且遍历了这个类加载器数组,然后尝试让类加载加载这个资源,加载成功后就立即返回。
但是这个类加载器数组是什么呢?我们返回上一层方法查看:
在这里插入图片描述
进入这个返回类加载器数组的方法:

    ClassLoader[] getClassLoaders(ClassLoader classLoader) {return new ClassLoader[]{//null,传入的时候为nullclassLoader,//classLoaderWrapper对象的类加载器变量this.defaultClassLoader, //关于当前线程的类加载器Thread.currentThread().getContextClassLoader(), //当前类的类加载器this.getClass().getClassLoader(), //classLoaderWrapper对象的另一个类加载器变量this.systemClassLoader};}

加载配置文件的流程

通过以上的代码追踪,<1>的步骤如下:通过classLoaderWrapper对象的类加载器变量对mybatis的配置文件进行加载,返回了一个输入流。

sqlsessionFactory_53">创建sqlsessionFactory对象

步骤<2>中我们发现创建啦一个SqlSessionFactoryBuilder对象并且通过build方法返回了sqlsessionFactory对象,我们进入方法查看:
在这里插入图片描述
该方法内首先通过步骤<1>获取的输入流创建了一个XmlConfigBuilder对象,然后调用了其parse方法,我们先查看parse方法:
在这里插入图片描述
这个方法首先是对mybatis配置文件标签进行了解析,并且返回了configuration对象,那么这个解析配置的方法应该是对configration对象的属性进行了填充,那么这个configuration对象是什么呢?我们进入这个类查看:
在这里插入图片描述
可以看到Configuration类中含有许多变量,包括我们很熟悉的缓存、请求映射和返回结果映射,我们其实就可以想到整个解析配置的方法中应该是对配置文件中的每个标签进行了解析然后填充到Configuration对象中,我们查看这个解析方法:

解析Mapper

在这里插入图片描述
进入这个方法中,我们可以发现确实如此,对每个标签进行了解析,我们查看一个很关键的方法mapperElement:
在这里插入图片描述

我们发现生成了一个迭代器,应该是对mappers的多个子标签mapper进行了遍历,然后获取mapper标签的多个属性,可以看到mapper存在三个属性,分别是resource、url、class,由下方的非空判断可知这三者是互斥的,进行判断后,由通过属性对每一个mapper进行加载获取输入流,然后又创建了XmlMapperBuilder,调用其parse方法对每一个mapper进行解析,进入方法:
在这里插入图片描述
我们发现,第一个方法对mapper标签进行了解析,我们进入第一个方法:
在这里插入图片描述
可以看到,这个方法对mapper标签下的每一条sql进行了解析,包括缓存、请求映射、响应结果映射、查询类型等,我们注意上方我框起来的方法:获取mapper的命名空间,并且存储在了一个对象中。
我们接着查看第二个方法:
在这里插入图片描述
还记得上面讲的吗,我们获取对mapper进行解析时存储的mapper的命名空间,然后通过命名空间进行加载获取了mapper的类型,然后这个addMapper方法看起来好像是对configuration对象的属性进行了填充,并且传入了mapper的类型,我们点进去查看:
在这里插入图片描述
这里又出现了MapperRegistry对象,是Configuration对象的变量,继续查看:
在这里插入图片描述在这里插入图片描述
这里创建了一个MapperProxyFactory对象,似乎是创建mapper代理工厂,并且把mapper和代理工厂放入一个map集合中。
我们再回到sqlsessionFactory的创建:
在这里插入图片描述
发现我们之前通过XmlConfigBuider解析的配置文件的标签以及通过XmlMapperBuilder解析的每一个mapper的标签得到的Configuration对象,通过这个Configuration对象创建了DefalutSqlsessionFactory。

sqlsessionFactory_81">创建sqlsessionFactory流程

首先创建了XmlConfigBuilder对象,解析mybatis配置文件,来填充Configuration,在解析<mappers标签过程中,创建了XmlMapperBuilder对象解析每一个mapper中的标签,并且利用了Configuration对象的mapperRegistry的map变量,存储了键为mapper类型,值为mapper代理工厂。

sqlsession_83">获取sqlsession

进入方法最底层:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;DefaultSqlSession var8;try {// 获取sqlsessionFactory的configuration变量的环境Environment environment = this.configuration.getEnvironment();// 通过环境获取事务工厂TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);// 创建新的事务tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// 通过configuration创建ExecutorExecutor executor = this.configuration.newExecutor(tx, execType);// 通过Configuration和Executor创建sqlSessionvar8 = new DefaultSqlSession(this.configuration, executor, autoCommit);} catch (Exception var12) {this.closeTransaction(tx);throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);} finally {ErrorContext.instance().reset();}return var8;}

创建mapper代理

第<4>步返回了一个mapper对象,但不是实际的mapper,而是mapper的代理对象,我们进入方法最底层:
在这里插入图片描述
我们发现会从关于mapper代理工厂的map集合中,通过mapper类型获取代理工厂,并且通过代理工厂创建代理对象,点进去查看:
在这里插入图片描述
mapper代理对象的创建是基于JDK动态代理实现的,通过Proxy的newProxyInstance方法创建:
在这里插入图片描述
而创建代理的方法第三个参数是InvocationHandler,那么mapperProxy一定实现了InvocationHandler这个类,并且当我们调用代理对象的方法时,代理类的方法会中转到InvocationHandler对象的invoke方法,然后就可以在这个方法中对原方法做出一些增强,具体可参考:代理模式:静态代理和动态代理(JDK动态代理原理)

执行mapper方法

我们来查看使用代理模式做了哪些增强:
在这里插入图片描述
在这个方法中,首先判断这个类是不是接口,由于JDK动态代理生成的代理类需要继承Proxy类,又需要实现或继承被代理类来获取方法信息,但是java是多实现单继承,因此这个类必须是接口,然后判断是不是默认方法。
查看第一个方法,我们发现多了一个新的对象MapperMethod,进入查看:

在这里插入图片描述

发现是从缓存中获取一个MapperMethod对象,如果不存在进行创建,创建时可以发现闯入了mapper接口、调用的方法以及sqlsession的configuration对象,并且执行方法时也调用了其execute方法,我们看一下MapperMethod到底是什么:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

其存在两个对象,分别是sql命令和方法签名,sql命令包括sql的类型和statement的id,方法签名包括方法的返回类型以及一些信息。
那么第一个方法就是从缓存中获取方法签名和sql信息,进入第二个方法查看:
在这里插入图片描述
首先会根据sql的类型,来选择执行方法,然后将参数装换为sql可以识别的参数,然后再次交给sqlsession来执行方法,我们随机进入一个执行方法查看:

在这里插入图片描述

根据sql命令的id在configuration对象中的MappedStatement缓存中获取相应MapperStatement,然后交给Sqlsession内部的executor来执行sql
进入其中:

一级缓存和二级缓存

在这里插入图片描述
我们发现其包括两个实现类,当mybatis开启二级缓存时会进入第二个实现类中,
查看第二个实现类的方法:

在这里插入图片描述
这个方法会获取根据sql的参数等信息和MappedStatement包装成一个key
在这里插入图片描述
该方法首先会检验二级缓存标签是否存在,然后判断缓存是否需要进行刷新,最后从二级缓存中获取缓存,如果缓存不存在,那么就进入一级缓存中查看:
在这里插入图片描述
在这里插入图片描述
localCache就是一级缓存,是Executor的属性,而每一个Sqlsession包换一个Executor,因此每一个sqlsession都存在独立的一级缓存,而二级缓存是mapper级别的缓存,被多个sqlsession所共享。
进入最底层:
在这里插入图片描述
可以发现最后会通过statement执行sql语句

sql_160">总结:MyBatis执行一条sql语句的流程

加载配置文件:通过类加载器ClassLoaderWrapper以及一些其他的类加载器加载mybatis配置文件获得输入流。
SqlsessionFactory的创建:通过XmlConfigBuilder通过输入流解析配置文件的标签,来填充Configuration属性,在解析的mappers标签的过程中,遍历每一个mapper标签,通过mapper标签的属性解析相应的mapper,并通过XmlMapperBuilder对相应的mapper类的标签进行解析,在此期间,通过mapper的命名空间获取mapper类,并且创建mapper代理工厂,以mapper类为键,mapper代理工厂为值放入mapperRegistry的map集合中。
获取sqlsession:通过SqlsessionFactory的Configuration对象获取环境变量,通过环境变量获取事务工厂并且创建事务,然后通过事务和环境创建Executor对象,最后通过创建的Executor对象和事务来创建DefaultSqlsession并返回
获取代理对象:根据mapper类从关于mapper代理工厂的键值对中取出mapper代理工厂并且创建代理对象返回
执行相关方法:由于创建代理对象时传入了MapperProxy对象,其实现了InvocationHandler接口,因此调用代理相关方式时会进入MapperProxy的invoke方法,在该方法内首先会从缓存中获取MapperMethod对象,然后调用其Execute方法,根据sql的类型然后由Executor执行。
一二级缓存:在MapperMethod中会根据sql类型执行相应的方法,首先会将参数转换为sql可以识别的参数,如果执行的是查询语句,首先会走二级缓存,如果二级缓存未开启,直接走一级缓存,最后交给Statement执行sql语句


http://www.ppmy.cn/embedded/155140.html

相关文章

一、1-2 5G-A通感融合基站产品及开通

1、通感融合定义和场景&#xff08;阅读&#xff09; 1.1通感融合定义 1.2通感融合应用场景 2、通感融合架构和原理&#xff08;较难&#xff0c;理解即可&#xff09; 2.1 感知方式 2.2 通感融合架构 SF&#xff08;Sensing Function&#xff09;&#xff1a;核心网感知控制…

Spring Bean的提前加载和懒加载

一、将特定的bean 提前加载来满足其他bean对该bean 的依赖 通过实现 BeanFactoryPostProcessor 该接口重写postProcessBeanFactory&#xff08;&#xff09; 方法来进行bean的提前加载 在Bean1中加载Bean2 让Bean提前加载。 再写一个Bean3让系统加载&#xff08;无调用&#x…

Vue.js组件开发-如何实现表头搜索

在Vue.js组件开发中&#xff0c;实现表头搜索通常涉及在表格组件的表头添加输入框&#xff0c;并让用户能够输入搜索关键字来过滤表格数据。 以下是一个使用Element UI的el-table组件实现表头搜索的示例&#xff1a; 一、准备阶段 ‌确保Element UI已安装‌&#xff1a; 确保…

Spring Boot与MyBatis

Spring Boot与MyBatis的配置 一、简介 Spring Boot是一个用于创建独立的、基于Spring的生产级应用程序的框架&#xff0c;它简化了Spring应用的初始搭建以及开发过程。MyBatis是一款优秀的持久层框架&#xff0c;它支持定制化SQL、存储过程以及高级映射。将Spring Boot和MyBa…

2025年01月蓝桥杯Scratch1月stema选拔赛真题—美丽的图形

美丽的图形 编程实现美丽的图形具体要求: 1)点击绿旗&#xff0c;角色在舞台中心&#xff0c;如图所示&#xff1b; 2)1秒后&#xff0c;绘制一个边长为 140的红色大正方形&#xff0c;线条粗细为 3&#xff0c;正方形的中心为舞台中心&#xff0c;如图所示; 完整题目可点击下…

大模型(LLM)的若干科普之问(五):调用LLM涉及哪些参数?

一、一个简单的示例 其实&#xff0c;调LLM并不复杂&#xff0c;看一个例子&#xff1a; 以下是一个使用 OpenAI API 的 Python 程序示例&#xff0c;它可以将一段文本进行修改和完善。程序会调用 GPT 模型对输入文本进行润色&#xff0c;使其更加流畅、清晰或符合特定风格。 …

ASP.NET Core - 依赖注入(三)

ASP.NET Core - 依赖注入&#xff08;三&#xff09; 4. 容器中的服务创建与释放 4. 容器中的服务创建与释放 我们使用了 IoC 容器之后&#xff0c;服务实例的创建和销毁的工作就交给了容器去处理&#xff0c;前面也讲到了服务的生命周期&#xff0c;那三种生命周期中对象的创…

C++实现设计模式--- 观察者模式 (Observer)

观察者模式 (Observer) 观察者模式 是一种行为型设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;使得当一个对象的状态发生改变时&#xff0c;其依赖者&#xff08;观察者&#xff09;会收到通知并自动更新。 意图 定义对象之间的一对多依赖关系。当一个对象状…