MyBatis源码分析

MyBatis 分析流程

1 分析MyBatis是怎么解析配置文件
Mapper接口和映射文件如何进行绑定的
2 MyBatis是如何执行Sql 并映射结果
MyBatis中SQL语句的执行流程(寻找sql语句,执行sql语句)
自定义MyBatis中的参数设置处理器typeHandler
自定义MyBatis中结果集处理器typeHandler
3 分析MyBatis是如何使用缓存的

源码分析流程

读源码的前提:
1 先会用框架,了解框架特性
2 官方文档要吃透
如何看源代码:
1 先看框架架构
2 再看启动流程
3 最后在看执行方法

要读优秀的源代码

当然,其中很多细节并没有提到,而看源码我们也并不需要追求每一行代码都能看懂,就比如我们一个稍微复杂一点的业务系统,即使我们是项目开发者如果某一个模块不是本人负责的,恐怕也很难搞清楚每一行代码的含义。所以对于MyBatis及其他框架的源码中也是一样,首先应该从大局入手,掌握整体流程和设计思想,然后如果对某些实现细节感兴趣,再深入进行了解。

前期准备

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

分析MyBatis是怎么解析配置文件

Configuration类保存了所有Mybatis的配置信息。也就是说mybaits-config.xml及UserMapper.xml中所有配置信息都可以在Configruation对象中找到相应的信息。
一般情况下Mybatis在运行过程中只会创建一个Configration对象,并且配置信息不能再被修改。

configuration的属性主要分为两大部分:
1 从mybatis-config.xml中读取的配置
2 从mapper配置文件或Mapper注解读取的配置

针对mybatis-config.xml配置文件和Mapper配置文件,Mybatis也是由两个相对应的类来解析的。
1 XMLConfigBuilder解析mybatis-config.xml的配置到Configuration中
2 XMLMapperBuilder解析Mapper配置文件的配置到Configuration中
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通过 SqlSessionFactoryBuilder.build() 解析,并通过 如下方法获取

  SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
    System.out.println(sqlSessionFactory.getConfiguration());

在这里插入图片描述
在这里插入图片描述
加载Mapper配置文件
在这里插入图片描述
XMLMapperBuilder 主要是加载mapper配置文件加Configuration中。主要加载两大内容
1 ResultMap:结果集映射,对应Configuration中的resultMaps属性
在这里插入图片描述
2sql:即mapper配置文件中的select、update、insert、delete节点。对应Configuration中的mappedStatements
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解析resultMap 使用resultMapResolver.resolve()方法,解析各个节点,创造出ResultMap对象,并添加到ResultMaps中

XMLMapperBuilder对配置文件中的resultMap元素的解析并生成ResultMap对象的分析基本完成。这里总结几点:

1ResultMap对象是结果集中的一行记录和一个java对象的对应关系。
2ResultMapping对象是结果集中的列与java对象的属性之间的对应关系。
3ResultMapp由id,type等基本的属性组成外,还包含多个ResultMapping对象。这类似于一个java对象由多个属性组成一个道理。
4ResultMapping最主要的属性column(结果集字段名),property(java对象的属性),ResultMapping可以指向一个内查询或内映射。
5 XMLMapperBuilder调用如下方法来解析并生成ResultMap对象
6 ResultMap和ResultMapping对象都是由相对应的Builder构建的。Builder只是进行了一些数据验证,并没有太多的业务逻辑

MappedStatement的解析与获取
在这里插入图片描述
resultMap元素的解析已经分析完毕。与resultMap不一样,XmlMapperBuilder在解析select/update/insert/delete的元素时会创建一个XMLStatementBuilder对象,解析的工作交由其方法parseStatementNode()方法完成
在这里插入图片描述

 public void parseStatementNode() {
        String id = context.getStringAttribute("id");
        String databaseId = context.getStringAttribute("databaseId");

        if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;

        Integer fetchSize = context.getIntAttribute("fetchSize");
        Integer timeout = context.getIntAttribute("timeout");
        String parameterMap = context.getStringAttribute("parameterMap");
        String parameterType = context.getStringAttribute("parameterType");
        Class<?> parameterTypeClass = resolveClass(parameterType);
        String resultMap = context.getStringAttribute("resultMap");
        String resultType = context.getStringAttribute("resultType");
        String lang = context.getStringAttribute("lang");
        LanguageDriver langDriver = getLanguageDriver(lang);

        Class<?> resultTypeClass = resolveClass(resultType);
        String resultSetType = context.getStringAttribute("resultSetType");
        //Statement的类型,对应jdbc里的三个类型:Statement、PreparedStatement、CallableStatement,默认使用PreparedStatement
        StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
        //这个也是跟jdbc里相对应的,一般采用默认即可
        ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

        //Sql的类型,select/update/insert/delete
        String nodeName = context.getNode().getNodeName();
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        //是否刷新缓存
        boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
        //是否使用缓存
        boolean useCache = context.getBooleanAttribute("useCache", isSelect);
        boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

        //不做分析
        // Include Fragments before parsing
        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
        includeParser.applyIncludes(context.getNode());
        //不做分析
        // Parse selectKey after includes and remove them.
        processSelectKeyNodes(id, parameterTypeClass, langDriver);

        //生成SqlSource对象,这个对象非常重要,接下来详细分析
        // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
        SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
        String resultSets = context.getStringAttribute("resultSets");
        String keyProperty = context.getStringAttribute("keyProperty");
        String keyColumn = context.getStringAttribute("keyColumn");

        //自动生成key,这里也不做讨论
        KeyGenerator keyGenerator;
        String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
        keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
        if (configuration.hasKeyGenerator(keyStatementId)) {
          keyGenerator = configuration.getKeyGenerator(keyStatementId);
        } else {
          keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
              configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
              ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
        }
        //生成MappedStatement对象,并加到Configuration中
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
            fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
            resultSetTypeEnum, flushCache, useCache, resultOrdered,
            keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
      }

在parseStatmentNode里面除了解析一些基本的属性外还有两个主要的部分:
1 SqlSource的构建过程
在这里插入图片描述

2 MappedStatement的构建过程
在这里插入图片描述
sqlSource接口
在这里插入图片描述
在这里插入图片描述
SqlBound代码并不多,就是一个普通的java对象,有两个属性非常重要

1 sql:看代码里的注解,这个sql已经是经过了一些处理,可以被jdbc执行的了。xml里配置的sql可能有占位符#{username},这里的sql占位符已经被替换成"?"号了。
2 parameterMappings:执行sql对象的实际的参数。由此可以判断,每执行一条sql都会创建一个BoundSql对象。

SqlSource和BoundSql本身并不复杂,复杂的是这两个对象被创建的过程。
SqlSource对象是通过LanguageDriver对象构建的,在mapper.xml配置sql里可以通过lang属性指定一个LanguageDriver,但我们通常不会这样子做。当lang属性没有配置时,Mybatis会属性默认给一个。这个默认的LanguageDriver在Configuration的构造方法中定义的:
在这里插入图片描述
马上来看XMLLanguageDriver.createSqlSource()方法
在这里插入图片描述
XMLScriptBuilder.parseScriptNode()方法
在这里插入图片描述
再看parseDynamicTagS(context)方法,进行了动态sqlNode的拼接
在这里插入图片描述
在这里插入图片描述

1 SqlSource.getBoundSql()方法
2 SqlNode.apply(DynamicContextcontext)方法
因为这两个方法都是在sql被执行时才调用。在后续的SqlSession实现章节再讨论!

MyBatis中的 缓存类型
1 一级缓存,也叫本地缓存。这个缓存是在sqlSession中的实现的,sqlSession关闭之后这个缓存也将不存在,默认是开启的,当然了也可以在Mybatis-config配置文件中关闭。对于这个缓存策略后面会析到。

2 二级缓存。这个缓存是在命名空间有效,可以被多个sqlSession共享。开启这个缓存是在mapper.xml中配置的,这里主要是讨论缓存的配置怎么样加载到Configuration中。缓存的具体实现以后再讨论。

本章节下面的文档如出现"缓存",如果没有特别说明,指的就是二级缓存。
在这里插入图片描述
源码在相应节点里面做

总结:
这里xml配置是由两个对象加载Configuration对象中
1 XMLConfigBuilder。这个对象负责加载除mappers映射器之外的所有配置。
2 XMLMapperBuilder。这个对象只负责加载mappers映射器中的配置。其中他还有个助手:MapperBuilderAssisant。XMLMapperBuilder与 MapperBuilderAssisant有明确的分工。

XMLConfigBuilder加载配置
这个加载过程比较简单,主要是读取配置,再创建对象,最后把对象加入Configruation中。而创建对象主要有两方式:

通过类名加载对象。

从配置中加载类名或类的别名
通过类名实例化对象
设置对象属性
将对象加入Configuration中

XMLMapperBuilder加载配置
XMLMapperBuilder主要加载如下三个配置,其他的比较少用。

cache
resultMap
select/update/insert/delete
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

MyBatis 里面使用了那些设计模式

http://cmsblogs.com/?p=5705

MyBatis是如何执行Sql 并映射结果

在得到SqlSessionFactory之后就可以创建一个SqlSession对象了,SqlSession对象的生命周期通常像如下方法所示
在这里插入图片描述

sqlSession 接口定义:
SqlSession定义了操作数据库的基本,这个Mybatis定义的用户层接口,使用该接口基本能满足用户(调用客户端)访问数据库的基本要求。由于接口定义的代码和注释比较多,这里就不贴了。其主要的方法如下:
select类方法
update/insert/delete方法
commit()
rollback()
close()
如果了解过jdbc,肯定知道这些方法的用途!

sqlSession的创建工程:
会创建一个DefaultSqlSessionFactory 对象
在这里插入图片描述
在这里插入图片描述
这里的Executor 使用Configuration里面的的ExecutorType
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
DefaultSqlSession中五个重要的方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由上面方法可以看出,DefaultSqlSession主要的操作都是交由Executor处理,这应该是设计模式中的适配器模式!
在看executor的五个方法,可以整理出如下关系。

1 DefaultSqlSession持有一个Executor对象,默认为SimpleExecutor,如果没有设置缓存的话。
2 Executor持有一个Transaction对象
3 DefaultSqlSession将select/update/insert/delete/commit/rollback/close交由Executor处理
4 Executor又将commit/rollback/close方法交由Transaction处理
5 DefaultSqlSession/Executor/Transaction对象都在DefaultSqlSessionFactory.openSessionFromDataSource方法中创建
在这里插入图片描述
SqlSessionFactory和SqlSession对象的范围和线程安全
来自官网的描述
在这里插入图片描述
可以看到

1 SqlSessionFactory可以在整个应用程序中保持一个单例,也就是它是线程安全的
2 SqlSession则需要每个线程持有不同的对象,也就是说它不是线程安全的。

我们来看代码验证上面两点
在这里插入图片描述
上面代码采用了线程封闭的技术,也就是说将对象封闭在当前线程范围内,保证这些对象其他线程不可见,所以openSession创建sqlSession是线程安全的

多个线程使用selectList方法,对于executor没有使用线程安全策略,所以不是线程安全的
5在这里插入图片描述
在这里插入图片描述

Executor接口实现方式
Executor接口定义了对象操作库操作的基本方法:select/update/insert/delete/commit/rollbak/close。
Mybatis对Executor接口的实现主要采用了模板模式和装饰模式两种设计模式。
在这里插入图片描述
1 BaseExecutor为模板模式中的模板类。这个类在Executor接口实现中非常重要,其实现了Executor的大部分方法。他的子类只要实现三个方法即可,其中两个是doUpdate和doSelect方法,子类在实现这两个方法时直接操作数据库即可,其余的工作交由BaseExecutor完成。

2 CachingExecutor是一个Executor的装饰器,给一个Executor增加了缓存的功能。

Executor接口的主要方法:
在这里插入图片描述
BaseExecutor模板是怎么实现Executor:
在这里插入图片描述
在这里插入图片描述
cachingExecutor 实现缓存
在这里插入图片描述
sql解析执行,BoundSql加载
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
sqlSession.selectList方法非常简单,他是用executor来完成查询的在这里插入图片描述

BoundSql类定义:
在这里插入图片描述
从源代码可以看出,BoundSql只是一个简单的java对象,有两个属性比较重要
1 sql:从解析时可以看出这个sql不是配置文件中的sql,这个sql已经经过了处理(如:占用位符的处理、动态语句的解析if、foreach等待)
2 parameterMappings:sql对应的参数列表
在这里插入图片描述
MappedStatement.getBoundSql()方法:
使用sqlSource 获取BoundSql
在这里插入图片描述
还记得sqlSource是怎么被创建的吗?(前面章节有详细说明)在这里插入图片描述
其实是使用了 DynamicSqlSource.getBoundSql()方法
在这里插入图片描述
在这里插入图片描述

SqlSource 与SqlNode
在这里插入图片描述
在这里插入图片描述
接下来看看每一种SqlNode是怎么解析sql并生成parameterMapping的

使用的是 StaticTextSqlNode.apply()方法 具体实现后面在具体看
在这里插入图片描述
"#{}"这种占位符还没有处理的具体处理方式,具体再看

SimpleExecutor执行sql过程
BoundSql加载完成之后,下一步就可以执行select/update/insert/delete语句了。在SimpleExecutor中执行语句最终会由doQuery和doUpdate方法完成。
在这里插入图片描述
这里在return上面一行调用prepareStatement方法创建Statement对象的时候会去设置参数,替换占位符
继续进入PreparedStatementHandler对象的query方法,可以看到,这一步就是调用了jdbc操作对象PreparedStatement中的execute方法,最后一步就是转换结果集然后返回。
在这里插入图片描述
后续执行的是 statementHandler 里面的query方法,里面进行了最后一步就是结果集的映射,然后返回:
在这里插入图片描述

所以完整流程是:Executor是怎么利用StatementHandler 执行SQL 的
1 获取一个数据库连接
2 调用StatementHandler.prepare()方法获取一个statement
3 调用StatementHandler.parameterize()方法设置sql执行时所需要的参数
4 调用StatementHandler.update或query方法执行sql

StatementHandler 其实就是个代理类似Executor,全全交给StatementHandler 去处理
在这里插入图片描述
StatementHandler接口,定义了一些执行sql的基本操作
在这里插入图片描述

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

Statement初始化和参数设置:
在这里插入图片描述
这里PreparedStatementHandler为例,详细分析这两个过程,具体看就好了,
在这里插入图片描述
在这里插入图片描述
TypeHandler主要有两个功能:
1 设置sql执行时的参数
2 从结果集中取数据
在这里插入图片描述
结果集合映射:
在PreparedStatementHandler中的query()方法中,是用ResultSetHandler来完成结果集的映射的。
在这里插入图片描述
Mybatis中只提供了一个ResultSetHandler的实现,那就是DefaultResultSetHandler。下面来看看他的handleResultSets()方法
在这里插入图片描述
继续看handlResultSet方法,
在这里插入图片描述
在这里插入图片描述
简单handleRowValuesForSimpleResultMap
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

更复杂的映射需要更具体的去看,内联映射等

从上面的代码片段我们也可以看到,实际上解析结果集还是很复杂的,就如我们上一篇介绍的复杂查询一样,一个查询可以不断嵌套其他查询,还有延迟加载等等一些复杂的特性 的处理,所以逻辑分支是有很多,但是不管怎么处理,最后的核心还是上面的一套流程,最终还是会调用typeHandler来获取查询到的结果。

是的,你没猜错,这个就是上面我们映射参数的typeHandler,因为typeHandler里面不只是一个设置参数方法,还有获取结果集方法(上面设置参数的时候省略了)。

到此,Mybatis是怎么利用ResultSet生成对象的过程已经分析完毕。分为简单映射和复杂映射。
简单映射就是不包含内映射的resultMap
复杂映射就是包含内映射的resultMap。
复杂映射的过程比较复杂,源代码也没有一行注释,本人是写了个实例,再通过eclipse中的debuger一步步来分析的。

缓存的实现

Mybatis主要有两种缓存:一级缓存和二级缓存。

一级缓存的生命周期与SqlSession的生命周期一样。一级缓存是在BaseExecutor中实现。
二级缓存的生命周期跟SqlSessionFactory一样,通常在整个应用中有效。二级缓存是通过CachingExecutor来实现的。
在这里插入图片描述
BaseExecutor中会有一个localCache对象,就是来保存缓存数据的。
在这里插入图片描述
再来看BaseExecutor中的query方法是怎么实现一级缓存的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果执行了update方法,localCache也会被清除:
在这里插入图片描述
以上代码可以看出一级缓存中的基本策略。

1 一级缓存只在同一个SqlSession中共享数据
2 在同一个SqlSession对象执行相同的sql并参数也要相同,缓存才有效。
3 如果在SqlSession中执行update/insert/detete语句的话,SqlSession中的executor对象会将一级缓存清空。

二级缓存:
二级缓存对所有的SqlSession对象都有效。需要注意如下几点:

1 二级缓存是跟一个命名空间绑定的。
2 在一个SqlSession中可以执行多个不同命名空间中的sql,也是就说一个SqlSession需要对多个Cache进行操作。
3 调用SqlSession.commit()之后,缓存才会被加入到相应的Cache。

下面来看CachingExecutor是怎么实现的。
在这里插入图片描述
这个manager实现了对多个Cache的管理,SqlSession.commit()之后,数据加入到相应的Cache也是由这个对象来实现的。

如下是CachingExecutor的commit()和rollback()方法
在这里插入图片描述
如下是TransactionCacheManager的源代码
在这里插入图片描述
再看看TransactionCache对象是怎么管理数据缓存数据的
1 TransactionCache.put()方法是先将数据保存在临时的数据区域,并未在Cache加入数据

2 TransactionCache.remove()方法是先在一个临时区域中保存要删除的数据,并未在Cache中删除数据

3 TransactionCache.commit()方法将保存在临时区域的数据真正加入Cache中,将临时区域中需要删除的数据真正删除

4 TransactionCache.rollback()方法,只是清除了临时区域中的数据

5 TransactionCache.clear()方法,告诉commit()方法,先清除缓存的数据,再执行后续操作。但clear方法本身不会清除缓存中的数据

下面来看CachingExecutor是怎么利用这几个方法来实现缓存的:
在这里插入图片描述
在这里插入图片描述
一级缓存只在一个SqlSession中有效,执行update/insert/delete语句后,一级缓存将会被清除。
二级缓存对所有的SqlSession有效,执行flushCache="true"的语句后,二级缓存将会被清除。

Mapper 生成过程

在这里插入图片描述
在这里插入图片描述
接下来的几个章节,分析如下内容:

1 Mybatis是怎么读取相关的注解,并加入到Configuration中去的。
2 Mybatis是怎么生成UserDao的代理对象的。
3 Mybatis是怎么实现UserDao中的方法的。

Mybatis是怎么读取相关的注解,并加入到Configuration中去的。
1 mybatis-config.xml中可以在包(package)和class的方法让Mybatis加载一个Mapper
在这里插入图片描述

2 通过包的方式是从包加载所有的class,最条还是通过class方法来实现加载
3 加载过程中主要通过读取方法中的注解来生成MappedStatement对象,再加入到Configuration中。这个过程跟解析mapper.xml配置文件中的select/insert/update/delete节点的过程差不多。一个是从xml里读取信息,一个是从注解里读取信息。
4 通过注解生成的MappedStatement的Id生成规则为接口全限定名名加方法的名字,所以在定义Mapper接口类时千万不要重载方法,否则会发生预想不到的问题。
5 Mapper接口中的只有包含如下八种注解中的一种才会被Mybatis解析成一个MappedStatement对象。

1Select/Insert/Update/Delete
2SelectProvider/InsertProvider/UpdateProvider/DeleteProvider

一个方法如果没有这些注解中的其中一个,执行时Mybatis会报MappedStatement找不到

6 Mybatis加载一个Mapper接口时会为其生成一个MapperProxyFactory对象,由这个对象来创建Mapper接口的实例。MapperProxyFactory这个类的实现在下一节详细分析。

Mapper 的加载是从XmlConfigBuilder.mapperElement()方法中触发的
在这里插入图片描述
通过package方式加载,最终也是找出所有符合条件的mapper类,再通过class的方法加载。
先来看configuration.addMaper()方法
在这里插入图片描述
在这里插入图片描述
MapperAnnotationBuiler如何使用parser方法解析的,具体考的时候再看吧

这个parse 不是addMapper 里面的parse,而是mapperElement 里面的解析
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个方法里面会去解析@Select等注解,需要注意的是,parse方法里面会同时再解析一次xml映射文件,上面提到了mappers节点有4中配置方式,其中两种配置的是Mapper接口,而配置Mapper接口会直接先调用addMapper 接口,并没有解析映射文件,所以进入注解解析方法parse之中会需要尝试解析一次xml映射文件

在这里插入图片描述
解析完成之后,还会对Mapper接口中的方法进行解析,并将每个方法的全限定类名作为key 存入存入Configuration中的mappedStatements属性。

需要指出的是,这里存储的时候,同一个value会存储2次,一个全限定名作为key,另一个就是只用方法名(sql语句的id)来作为key :

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

串使用流程就用如下的图

在这里插入图片描述

获取Mapper 接口(getMapper)

1、在调用getMapper之后,会去Configuration对象中获取Mapper对象,因为在项目启动的时候就会把Mapper接口加载并解析存储到Configuration对象

在这里插入图片描述
2、通过Configuration对象中的MapperRegistry对象属性,继续调用getMapper方法
在这里插入图片描述
3、根据type类型,从MapperRegistry对象中的knownMappers获取到当前类型对应的代理工厂类,然后通过代理工厂类生成对应Mapper的代理类
在这里插入图片描述
4、最终获取到我们接口对应的代理类MapperProxy对象
在这里插入图片描述
而MapperProxy可以看到实现了InvocationHandler,使用的就是JDK动态代理。
在这里插入图片描述
至此获取Mapper流程结束了

SQL执行流程分析

1 寻找sql
在这里插入图片描述

1、了解代理模式的应该都知道,调用被代理对象的方法之后实际上执行的就是代理对象的invoke方法
在这里插入图片描述
2、因为我们这里并没有调用Object类中的方法,所以肯定走的else。else中会继续调用MapperProxy内部类MapperMethodInvoker中的方法cachedInvoker,这里面会有一个判断,判断一下我们是不是default方法,因为Jdk1.8中接口中可以新增default方法,而default方法是并不是一个抽象方法,所以也需要特殊处理(刚开始会从缓存里面取,缓存相关知识我们这里先不讲,后面会单独写一篇来分析一下缓存))。
在这里插入图片描述
3、接下来,是构造一个MapperMethod对象,这个对象封装了Mapper接口中对应的方法信息以及对应的sql语句信息:
在这里插入图片描述这里面就会把要执行的sql语句,请求参数,方法返回值全部解析封装成MapperMethod对象,然后后面就可以开始准备执行sql语句了

2 执行sql语句

在这里插入图片描述1、我们继续上面的流程进入execute方法:
在这里插入图片描述
2、这里面会根据语句类型以及返回值类型来决定如何执行,本人这里返回的是一个集合,故而我们进入executeForMany方法:
在这里插入图片描述
3、这里面首先会将前面存好的参数进行一次转换,然后绕了这么一圈,回到了起点SqlSession对象,继续调用selectList方法:在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值