(八)MyBatis学习笔记-运行原理

掌握MyBatis运行原理是学习MyBatis插件技术的基础。学习MyBatis运行原理,我们可以使用Debug一步一步地去理解。

下面分析笔记二的HelloWorld工程,分为如下步骤分析MyBatis的运行原理。

  • 获取sqlSessionFactory对象
  • 获取sqlSession对象
  • 获取接口的代理对象(MapperProxy),即getMapper方法的执行过程。
  • 执行增删改查方法

一、获取sqlSessionFactory对象

在这里插入图片描述
在XmlConfigBuilder中会创建XML解析器parser,它用来解析全局配置信息和映射器文件mapper.xml 信息。
在这里插入图片描述
在这里插入图片描述
XPathParser子解析器主要的属性和方法如下所示:
在这里插入图片描述
解析器解析全局配置文件的部分代码如下:
在这里插入图片描述
需要说明的是在解析映射器文件mapper.xml 时,会将解析出来的数据封装到一个MappedStatement对象中,它封装了一个增删改查标签的详细信息。具体的一些属性如下所示:
在这里插入图片描述
从上图中可以发现,MappedStatement对象中的id值和sql语句正是我们HellowWorld工程中EmployeeMapper.xml中select查询标签的内容。

每一次解析都会把配置文件的内容封装到Configuration中,也就是说Configuration对象保存了所有配置文件的详细信息。 比如解析setting标签的代码如下:
在这里插入图片描述
Configuration类的一些属性如下所示:
在这里插入图片描述
显然上方一些属性比如懒加载aggressiveLazyLoading,二级缓存cahceEnabled等信息都被封装到Configuraton中。

Configuraton类中一些比较重要的属性,比如我们上面提到的用来封装解析映射器文件mapper.xml数据的MappedStatement。
在这里插入图片描述
以及MapperRegistry对象,它表明了一个映射器接口对应了一个MapperProxyFactory,也就是映射器代理工厂。
在这里插入图片描述
最后,Configuraton对象会被封装到一个DefaultSqlSessionFactory类中,并返回给调用者, 其中DefaultSqlSessionFactory为SqlSessionFactory接口的实现类。至此,sqlSessionFactory对象的构建基本完成。

二、获取sqlSession对象

在这里插入图片描述
sqlSessionFactory调用openSession方法,会执行SqlSession接口的子类DefaultSqlSessionFactory的openSession方法。在该方法中,实际调用的是openSessionFromDataSource方法。
在这里插入图片描述
执行openSessionFromDataSource时,会获取一些信息还有创建事务器工厂创建事务等操作。其中需要注意的是,该方法中会根据配置信息调用Configuration对象的newExecutor方法创建对应的执行器Executer—一个真正执行Java和数据库交互的对象。执行器提供了查询、更新等方法。
在这里插入图片描述
再创建出执行器Executor后,接着会根据是否开启了二级缓存配置判断是否要包装该执行器。执行完该步骤后,需要注意的是,MyBatis会执行executor = (Executor) interceptorChain.pluginAll(executor);该语句来使用每一个拦截器来包装执行器。
在这里插入图片描述
最后,创建SqlSession接口的子类DefaultSqlSession来封装Configration和执行器Executor,并返回给调用者。
在这里插入图片描述
至此,获取sqlSession对象的过程便结束了。

三、获取接口的代理对象

在这里插入图片描述
sqlSession对象调用getMapper方法后会调用Configuration对象的getMapper方法,接着是调用mapperRegistry对象的getMapper方法。

在mapperRegistry对象的getMapper方法中,则会根据接口类型获取MapperProxyFactory对象,该对象通过执行该语句mapperProxyFactory.newInstance(sqlSession);来创建MapperProxy对象。
在这里插入图片描述
其中newInstance方法的执行,是通过动态代理执行MapperProxy代理逻辑类(实现InvocationHandler)的newInstance来创建的其对象实例的。
在这里插入图片描述

最后会返回MapperProxy类的代理对象给调用者,该对象包含有DefaultSqlSession实例。 如下图示:
在这里插入图片描述
至此,获取接口的代理对象的过程,即getMapper方法的调用过程也结束了。

四、执行增删改查方法(查询)

在这里插入图片描述
调用者在调用查询方法时,其实会调用代理对象MapperProxy的invoke方法,然后会调用MapperMethod的execute方法。

执行execute方法时,会先判断增删改查的类型,也就是先判断当前是要进行增加还是查询等操作,这里是要进行查询。接着调用convertArgsToSqlCommandParam方法,该方法是我们熟悉的将查询所需要的参数封装为一个Map集合等操作。
在这里插入图片描述
包装完后会接着执行DefaultSqlSession类的selectOne方法,然后执行selectList方法。在该方法中,会通过Configuration对象的来获取一个MappedStatement,我们知道MappedStatement它封装了一个增删改查标签的详细信息。接着会调用执行器Excutor的的query方法,该query方法判断查询所需要的参数类型是否为Collection集合、List集合或者array。我们之前说过,若查询参数是Collection集合,MyBatis会有自定义的别名collection,这个功能实现便是通过query里的参数方法wrapCollection实现的。
在这里插入图片描述
在这里插入图片描述
紧接着,调用query方法后,会执行CachingExcutor里的query方法。它通过MappedStatement对象获取到了BoundSql对象,该对象代表sql语句的详细信息。其属性等内容如下所示:
在这里插入图片描述
获取完BoundSql对象后,会根据是否开启了二级缓存来获取相应的key来创建缓存。其中的key的内容如下:
在这里插入图片描述
缓存中保存的key:方法id+sql+参数xxx等等。接着便又开始执行CachingExcutor的query方法。CachingExcutor里的query方法的代码如下:
在这里插入图片描述
调用query方法中SimpleExcutor的query方法后,会先查看本地缓存是否有数据,没有就调用queryFromDatabase,查出数据以后将数据保存到本地缓存中。
在这里插入图片描述
执行queryFromDatabase方法后,便会在该方法中执行doQuery方法。
在这里插入图片描述
在doQuery方法中,会通过Configuration对象来创建StatementHadler对象,StatementHadler即为数据库会话器,其专门用来处理数据库会话的。 如下为创建过程:
在这里插入图片描述
需要注意的是,MyBatis会执行statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);该语句来使用每一个拦截器来包装StatementHandler。

返回之后,会调用prepareStatement方法进行预编译将产生返回的PrepareStatement对象设置给对象stmt。

在预编译sql产生PrepareStatement期间会创建ParameterHandler和ResultSetHandler。ParameterHandler即为参数处理器,MyBatis是通过该类对预编译语句进行参数设置的。而ResultSetHandler为结果处理器,用于组装结果集返回的。
在这里插入图片描述
在调用prepareStatement方法时会调用ParameterHandler设置参数。在调用setParameters时,会调用TypeHandler给sql预编译设置参数。
在这里插入图片描述
这些执行完后,会继续调用doQuery方法的query方法。
在这里插入图片描述
并将查出的数据使用ResultSetHandler处理结果,其是通过使用TyperHandler来获取查询的值的。
在这里插入图片描述
后续就将查询到的数据的List集合或其元素返回给调用者。这里因为是根据1号员工的id查询,所以返回List集合的第一个元素。

查询流程总结

在这里插入图片描述
调用者在调用查询方法时,实际调用的是DefaultSqlSession里的执行器Executor对象的query方法。然后由它调度StatementHadler对象来进行设置参数等处理sql语句预编译相关工作。而StatementHadler具体的工作由ParameterHandler和ResultSetHandler处理。ParameterHandler负责sql语句的参数设置,而ResultSetHandler负责查询结果集的处理。 而两者工作的整个过程中,需要TypeHandler进行数据库类型和javaBean类型的映射, 并由TypeHandler来进行参数设置和查询结果集的处理工作。 TypeHandler底层使用的是原生JDBC的方式来访问数据库。

SqlSession下的四大对象分别如下:

  • Executor — 执行器
  • StatementHandler — 数据库处理器
  • ParameterHandler — 参数处理器
  • ResultSetHandler — 结果集处理器

另外:TypeHandler类型处理器后续会讲解。

五、总结

上述的总体流程如下:

  1. 根据配置文件(全局配置文件,映射器文件)初始化出Configuration对象;
  2. 创建一个DefaultSqlSession对象,它里面包含Configuration以及Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
  3. DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy,MapperProxy里面有(DefaultSqlSession);
  4. 执行增删改查方法:
    • 调用DefaultSqlSession的增删改查(Executor);
    • 然后创建一个StatementHandler对象;(同时也会创建出ParameterHandler和ResultSetHandler);
    • 调用StatementHandler预编译参数以及设置参数值;使用ParameterHandler来给sql设置参数;
    • 调用StatementHandler的增删改查方法;
    • 使用ResultSetHandler封装结果;

注意:四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler);

猜你喜欢

转载自blog.csdn.net/weixin_40374341/article/details/86568019