文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Mybatis(五)------Mybatis执行Mapper接口的方法流程

2023-09-16 06:56

关注

前面几篇文章我们介绍了JDBC、Mybatis的工具类等,下面我们开始对于mybatis的各个机制开始解析。

前面我们知道,mybatis对excutor进行封装成sqlsession提供给开发人员进行数据库的增删改查,我们先从Mybatis最顶层的API入手。

SQLSession的创建过程分为3个阶段:

1)、Configuration实例的创建(配置类)

2)、SQLSessionFactory实例的创建(工厂类)

3)、SQLSession实例的创建

解析xml文件

        mybatis的主配置文件和mapper配置文件常用的都是xml格式,Configuration组件用来描述主配置文件等信息,框架在启动时会解析xml配置文件,然后将其转换为Configuration对象。

JDK API中提供了3中方式去解析xml,分别为DOM、SAX、XPath。其中最易使用的是Xpath方式,Mybatis也是采用XPath方式去解析xml文件。

我们可以看看如何使用XPath方式解析xml文件:

假如我们有xml文件将其映射为实体对象:

使用XPath方式进行解析:

 

 为了简化XPath解析操作,Mybatis通过XPathParser工具类封装了对xml的解析操作,同时使用XNode类增强对xml节点的操作。使用XNode对象,我们可以很方便的获取节点的属性、子节点等信息。

我们可以看看XPathParser如何进行解析:

 可以看到XPathParser对xml解析,省去了Dociment对象和XPath对象的创建过程,还封装了执行XPath表达式的方法,简化了xml解析的过程。

Configuration实例创建

Configuration组件主要有下面三个作用:

1)、用于描述mybatis的主配置信息,例如等标签

2)、作为容器注册存储Mybatis的其他组件,例如TypeHandler、MapperStatement等

3)、提供工厂方法,创建ResultSetHandler、StatementHandler、Executor、ParameterHandler等组件实例

SqlSession实例化前,会先解析mybatis的主配置文件+所有Mapper文件,其中部分配置文件由Configuration对象存储。mybatis通过XMLConfigBuilder类完成对Configuration对象的构建。例如下面的代码:

 进入parse方法:

 进入parseConfiguration方法

 我们·先看看主配置xml文件大概是这样的:

mybatis中传进来了XNode root(实际就是标签及包含内容),然后依次处理多个子标签,例如等。我们列出了各个标签的作用如下:

 

 对于每个标签的解析细节,可以自行阅读源码。

 3、SqlSession实例创建过程

mybatis创建Sqlsession使用工厂模式创建,在创建之前需要先创建SqlSessionFactory对象,然后调用工厂对象的openSession方法,例如:

进入build方法:

可以看到先创建一个XMLConfigBuilder对象,然后调用 XMLConfigBuilder对象的parse方法对主配置类进行解析,然后返回Configuration对象作为build方法的参数,我们再看看这个重载的build方法:

我们再看看DefaultSqlSessionFactory类对openSession方法的实现:

 进入openSessionFromDataSource方法是如何返回一个SqlSession实例的:

 

(这也就证实了之前说的SqlSession是对Executor的封装的面向开发人员的实例,用来执行增删改查等功能) 

=============================SqlSession 执行Mapper的过程====================

上面讲了如何解析xml配置文件、怎么创SqlSession实例(可以看成sql的执行器),下面我们开始看看SqlSession如何去执行我们定义的Mapper.

一、获取Mapper接口的代理对象

在我们日常开发中,Mapper一般分为两部分,Mapper接口+XML/注解,我们先看看如何去执行Mappper中定义的方法:

如上面所示,创建SqlSession实例后,需要调用getMappper获取Mappper对象(接口的代理对象),然后再去调用相应方法。

而这个Mapper接口的代理在mybatis中是由MapperProxy代理工具类去实现的,MapperProxy工具类的关键代码如下:

在java中常用的动态代理一般有两种方式,jdk动态代理和cglib动态代理。MapperProxy使用了JDK动态代理,实现了InvocationHandler接口,在invoke方法中为统一的拦截逻辑,然后需要调用java.lang.reflect.Proxy类的newProxyInstance方法创建代理对象。创建代理对象的过程mybatis使用MapperProxyFactory进行封装,具体代码:

注意:拦截的代理逻辑是在MapperProxy中的invoke方法定义的,而创建代理对是使用MapperProxyFactory工厂返回代理对象MapperProxy的,MapperProxyFactory中的newInstance方法会去实例化一个MapperProxy进行返回。

可以看到,最后是由MapperProxyFactory进行创建了代理对象,这里需要注意的是newInstance方法并不是静态方法,也就是说我们要调用这个方法需要先实例化MapperProxyFactory对象,那么这个工厂对象是什么时候被创建的?

我们之前在说的Configuration配置对象时,里面就有个mapperRegister属性,具体如下:

 

 而mapperRegister是用来注册Mapper接口和MapperProxyFactory对象之间的关系:

(注意:MapperProxyFactory的类型T指定我Mapper的类型,即一个Mapper对应一个MapperProxyFactory对象)

 

 可以看出用了一个map来存储Mapper接口的Class对象与MapperProxyFactory对象之间的关系,然后在addMapper方法向其中注册关系,然后就能调用getMapper方法根据Mapper接口的来获取对应的代理对象(通过对应的MapperProxyFactory调用newInstance方法创建)。

 (mybatis启动时就会解析所以mapper接口,然后调用MapperRegistry对象的addMapper方法将Mapper接口信息和对应的MapperProxyFactory对象注册到MapperRegister对象中,待需要获取mappper的代理对象时调用getMapper执行对应的MapperProxyFactory对象的newInstacne方法返回代理对象)

二、MapperStatement的注册过程(Mapper中标签或注解的配置类)

前面已经说过,mybatis通过MappedStatement类描述Mapper的sql配置信息。而在将Configuration对象时,说过Configuration类有一个mappedStatements属性,该属性用来注册Mybatis中所有的MappedStatement对象,具体如下:

 在讲Configuration时,我们讲过Mybatis主配置文件的解析通过XMLConfigBuilder对象完成的,XmlConfigBuilder类通过parseConfiguration方法中去调用不同的方法解析对应的标签:

 而MappedStatement对象的创建重点需要关注的是对标签的解析过程,我们进入mapperElement方法:

如上代码,首先获取的所有子标签,然后根据不同标签做不同的处理,mappers标签的配置一般如下: (标签表示一个Mapper的配置,例如一个UserMapper接口就对应一个

 获取到子标签信息后,就创建XMLMapperBuilder对象并调用parse方法进行解析,我们进入该方法:(我们这里以这个子标签来看看parse方法怎么解析的,即看怎么解析Mapper.xml文件的)

首先会调用XPathParser对象的evalNode方法获取根节点对应的XNode对象,接着调用configurationElement方法对Mapper配置内容进一步做解析,我们进去看看configurationElement方法:

 如上面代码所示,configurationElement方法对Mappper.xml文件的所有标签进行解析,这里我们重点关注标签的解析,在上面代码中获取到标签对应的XNode对象后,调用buildStatementFromContext方法进一步做解析:

可以看到该方法为每个标签对应的XNode对象创建一个XMLStatementBuilder对象,然后调用其parseStatementNode方法进行解析。我们进入parseStatementNode方法看看:

可以从XMLStatementBuilder类的parseStatementNode方法看出解析标签对应的XNode对象大概分下面几个步骤:

三、调用Mapper方法

上面讲了获取Mapper接口的动态代理对象,创建MapperStatement配置对象,下面开始看看怎么去调用Mapper接口中定义的方法。

 为了执行Mapper接口中定义的方法,我们需要调用SqlSession对象的getMapper方法获取一个动态代理对象,然后通过动态代理对象调用方法即可,具体如下:

 我们前面说过调用代理对象方法时,会执行MapperProxy类的invoke方法:

 此时对Mapper接口的方法,调用cachedMapperMethod方法获取到一个MapperMethod对象:我们先看 cachedMapperMethod方法:

 首先该方法中从缓存中获取MapperMathod对象,如果获取不到则创建一个并放进缓存中,而MapperMethod是对Mapper方法相关信息的封装,通过MapperMethod能够方便的获取sql语句的类型、方法的签名信息等。下面是MapperMethod类的构造方法:

 MapperMethod构造方法中创建了一个SqlCommand对象和一个MethodSignature对象,SqlCommand对象用于获取Sql语句的类型、Mapper的Id等信息。MethodSignature对象用于获取方法的签名信息、参数注解等信息。我们先看下SqlCommand类的构造方法:

可以看出现获取方法的类或接口的class对象,然后调用resolveMappedStatement方法根据Mapper接口的全限定名和方法名获取对应的MappedStatement对象(配置对象),然后通过MappedStatement对象获取Sql语句的类型和mapper的id。下面是ResolveMappedStatement方法的实现:

 可以看到先将接口的全限定名+方法进行拼接,作为Mapper的id从Configuration对象中查找对应的MappedStatement对象,查找不到先从父接口递归调用,还找不到则返回null。

SqlCommand封装了Sql语句的类型和Mapper的id,下面我们看看MethodSignature对象的创建过程,下面是MethodSignature的构造方法:

ParaNameResolver构造方法中完成了Mapper方法参数的解析过程,代码如下:

 

到此我在,整个MapperMethod对象已经创建完成,接下来解释Mapper方法的执行,MapperMethod提供了一个execute方法用来 执行sql命令,我们可以回到MapperProxy的invoke方法看看:

进入改execute方法:

 

 如图所述,先通过SqlCommand获取sql语句的类型,然后根据类调用SqlSession对象对应的方法

例如Insert类型,先获取参数信息,然后通过SqlCommand的getName方法获取Mapper的id(全限定类或接口名+方法名),然后调用SqlSession的api。

四、SqlSession执行Mapper过程

上面我讲了先通过动态代理获取Mappper的代理对象,而代理方法invoke方法(代理方法调用时会调用,代理方法逻辑是通过Mapperproxy类,而代理对象的创建时通过MapperProxyFactory工厂实例化MapperProxy对象返回)中创建一个MapperMethod对象(用来描述Mappe接口中方法信息的对象),然后通过调用该对象的execute方法去执行sql语句。

而execute中执行sql语句是通过sqlsession提供的增删改查的方法。大概流程如下例子:

下面我们以select类型为例子,介绍SqlSession怎么执行Mapper的?我们之前的文章讲过SqlSessioon接口只有一个实现类就是DefaultSqlSession,下面是DefaultSqlSession类对SqlSession接口中定义的SelectList方法的实现:

上面代码中先根据Mapper的Id获取MappedStatement对象,然后作为参数调用Executor实例的query方法完成查询操作,我们来看看Excutor的实现类BaseExecutor对query方法的实现:

 BaseExecutor类的query反方中,先对从MappedStatement对象中获取BoundSql对象,该对象封装了经过解析后的sql语句以及参数映射信息,然后创建CacheKey对象,该对象用于缓存Key值,接着调用重载query放,代码如下:

 先从缓存中获取查询结果,如果没有则调用queryFromDatabase方法从数据库中查询,该方法的关键代码如下:

 调用doQuery方法进行查询,然后将查询结果进行缓存,doQuery是一个模板方法,由BaseExecutor子类实现(SimpleExecutor、ReuseExecutor),我们以SimpleExecutor对doQuer方法的实现举例:

 SimpleExecutor类的doQuery方法中,首先使用Configuration对象的newStatementHandler方法创建了StatementHandler对象(此时返回了一个RoutingStatementHandler类型的),在RoutingStatementHandler中会根据配置Mapper是StatementType属性指定的StatementHandler类型创建对应的StatementHandler实例进行处理(例如statementType属性为simple时则创建SimpleStatementHandler实例)

        StatementHandler对象创建完后,就会调用SimpleExecutor类的prepareStatement方法创建JDBC中的Statement对象(JDBC中的sql执行器),然后对Statement对象设置参数操作,Statement对象初始化后,再去调用其query方法执行操作,我们在来看看prepareStatement放怎么去创建Statement对象的。具体代码如下:

 上面代码中,首先会获取JDBC的Connection对象吗,然后调用StatementHandler对象的prepare方法创建Statement对象,接着调用StatementHandler对象的parametersize方法(该方法会使用Parameterhandler为Statement对象设置参数)

(StatementHandler、parameterHandler都是mybatis对JDBC的Statement、Parameter操作的封装)

mybatis的StatementHandler有几个不同的实现类,分别为SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,默认情况下回使用PreparedStatementHandler与数据库交互,接下来我们来看看PreparedStatementhandler中对query方法的实现,代码如下:

 首先调用了PrepareStatement对象的execute方法执行sql语句,然后调用ResultSetHandler的handleResultSet方法处理结果集。

ResultSetHandler只有一个默认的实现,即DefaultResultSetHandler类,下面是其handleResultSet方法的关键代码:

如上图所示,该方法的具体实现步骤如下:

 小结:

aaa

来源地址:https://blog.csdn.net/qq_35599414/article/details/129938161

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯