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

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

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

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

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

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

3)、SQLSession实例的创建

1、解析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解析的过程。

2、Configuration实例创建

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

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

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

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

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

 进入parse方法:

 进入parseConfiguration方法

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

<configuration>
	<settings>
		<setting name="useGeneratedKeys" value="true"/>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
		<setting name="logImpl" value="LOG4J"/>
	</settings>

	<environments default="dev" >
		<environment id="dev">
			<transactionManager type="JDBC">
				<property name="" value="" />
			</transactionManager>
			<dataSource type="UNPOOLED">
				<property name="driver" value="org.hsqldb.jdbcDriver" />
				<property name="url" value="jdbc:hsqldb:mem:mybatis" />
				<property name="username" value="sa" />
				<property name="password" value="" />
			</dataSource>
		</environment>
		<environment id="qa">
			<transactionManager type="JDBC">
				<property name="" value="" />
			</transactionManager>
			<dataSource type="UNPOOLED">
				<property name="driver" value="org.hsqldb.jdbcDriver" />
				<property name="url" value="jdbc:hsqldb:mem:mybatis_qa" />
				<property name="username" value="admin" />
				<property name="password" value="admin" />
			</dataSource>
		</environment>
	</environments>

	<mappers>
		<mapper resource="com/blog4java/mybatis/example/mapper/UserMapper.xml"/>
		<!--
		<mapper resource="file:///mybatis/com/blog4java/mybatis/example/mapper/UserMapper.xml"/>
		<mapper class="com.blog4java.mybatis.com.blog4java.mybatis.example.mapper.UserMapper"/>
		<package name="com.blog4java.mybatis.com.blog4java.mybatis.example.mapper"/>
		-->
	</mappers>
</configuration>

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

 

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

 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中<select|update|delete|delete>标签或注解的配置类)

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

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

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

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

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

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

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

可以看到该方法为每个<select|update|delete|insert>标签对应的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

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,那我简单讲一下Java Mapper接口MyBatis框架中的作用和使用方法。 在MyBatis中,Mapper接口是对应于SQL映射文件中定义的SQL语句的接口,可以通过Mapper接口来调用SQL语句。Mapper接口的作用是将SQL语句与Java代码解耦,使得Java代码更加简洁易懂,同时也方便了SQL语句的维护和管理。 Mapper接口的使用方法如下: 1. 定义Mapper接口Java项目中定义一个Mapper接口,该接口方法名和参数列表应与SQL映射文件中定义的SQL语句对应。 例如,如果SQL映射文件中定义了一个查询用户信息的SQL语句: ``` <select id="getUserById" resultType="User"> SELECT * FROM user WHERE id = #{id} </select> ``` 则对应的Mapper接口应该定义为: ``` public interface UserMapper { User getUserById(int id); } ``` 2. 配置Mapper接口MyBatis的配置文件中,需要将Mapper接口和SQL映射文件进行关联,使得Mapper接口能够调用对应的SQL语句。 例如,在配置文件中添加以下配置: ``` <mappers> <mapper resource="com/example/mapper/UserMapper.xml"/> </mappers> ``` 其中,resource属性指定SQL映射文件的路径。 3. 使用Mapper接口Java代码中,可以通过MyBatis的SqlSession对象获取Mapper接口的实例,从而调用Mapper接口中定义的方法执行SQL语句。 例如: ``` SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.getUserById(1); ``` 在这个例子中,首先通过SqlSessionFactory对象获取SqlSession对象,然后通过SqlSession对象获取UserMapper接口的实例,最后调用getUserById方法执行SQL查询,并将查询结果封装为一个User对象返回。 希望这些内容能够对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值