本章通过一个简单的例子,来了解 MyBatis 执行一条 SQL 语句的大致过程是怎样的。

案例代码如下所示:

public class MybatisTest {

    @Test
public void selectByPrimaryKey() throws IOException {
//
StudentDao studentDao = getSqlSession().getMapper(StudentDao.class);
//
Student student = studentDao.selectByPrimaryKey(1L);
System.out.println(student);
} /**
* 获取SqlSession
*
* @return
*/
private SqlSession getSqlSession() throws IOException {
//
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//
return sqlSessionFactory.openSession(true);
}
}

Configuration

第一步,通过资源加载模块加载配置文件,解析器模块解析 XML 文件,生成 Configuration 对象。

源码内容参考:org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties) 方法

深入 parse() 方法可以查看配置文件的具体解析过程,以及如何生成 Configuration 对象。

Configuration 对象中缓存了 mybatis-config.xml 配置文件以及映射配置文件的所有内容。

SqlSession

第二步,通过 SqlSessionFactory 创建 SqlSession,SqlSession 是 MyBatis 暴露给外部使用的统一接口层,所有和数据库打交道的操作都通过 SqlSession 这层。

下面通过时序图描述 SqlSession 对象的创建流程:

MapperProxy

第三步,通过 SqlSession 获得 StudentMapper 对象,为了便于理解,下面通过时序图描述 Mapper 对象的获取流程。

MapperRegistry 是 Configuration 的一个属性,MapperRegistry 缓存了 MapperProxyFactory 的 Map 集合,也就是说在解析完配置文件后,knownMappers 集合数据已经在 Configuration 对象中存在了。

问题一:为什么 StudentMapper(接口) 可以调用方法?

通过断点调试来看看 StudentMapper 的真实对象是什么:

MapperProxy 类的代码如下:

MapperProxyFactory 的 newInstance 方法实现代码如下:

通过上面的源码内容,可以得出如下结论:

  • StudentMapper 的真实对象是 MapperProxy;
  • MapperProxy 继承 InvocationHandler,实现 invoke 方法;
  • MapperProxyFactory 的 newInstance 方法,通过 JDK 动态代理的方式创建了一个 MapperProxy 的代理类;

MyBatis 的 Mapper 是通过动态代理实现的,调用 Mapper 的任何方法都会执行 MapperProxy 的 invoke 方法。

MyBatis 使用的动态代理和通常的动态代理有点区别,没有实现类,只有接口。

动态代理类图结构如下所示:

MyBatis 动态代理类图结构如下所示:

selectByPrimaryKey

第四部,执行 selectByPrimaryKey 查询学生信息。在第8行代码上面打上断点,调试选择“step into”,进入子函数内部,调用 org.apache.ibatis.binding.MapperProxy#invoke 方法。

MapperMethod 通过如下方式创建。

  • mapperInterface:interface com.yjw.mybatis.dao.StudentMapper;
  • method:invoke 方法中传进来的 method 类;
  • Configuration:MyBatis 的核心类,所有配置信息都存在该类中;

接着执行 mapperMethod.execute(sqlSession, args) 方法。

接下来还是走到 SqlSession 类中,调用 selectOne 方法。继续往下执行,会执行 Executor 的 query 方法。

通过上面的分析,简单总结一下,我们可以抽象出 MyBatis 在执行一条 SQL 查询的过程中涉及到的主要类:Configuration、SqlSession、MapperProxy、Exector,根据这些类画出如下 MyBatis 执行 SQL 的时序图:

  • 第一步是获取 MapperProxy 代理对象;
  • 第二步是根据获取的代理对象,执行查询操作;

Exector

SqlSession 中的 JDBC 操作部分最终都会委派给 Exector 实现,接着上面的断点往下执行,进入 Exector 的 query 方法。

下面通过时序图描述 Exector 的执行流程,真实的调用链路类比较多,这里简化了调用链路,省略了一些装饰类、代理类,便于理解:

根据 Exector 执行的时序图,可以抽象出的主要类是:SqlSession、Exector、StatementHandler、ParameterHandler、ResultSetHandler。

StatementHandler 接口是 MyBatis 的核心接口之一,它是 Exector 接口实现的基础。StatementHandler 的主要功能很多,例如创建 Statement 对象,为 SQL 语句绑定实参,执行 SQL 语句,将结果集映射成结果对象。

StatementHandler 类中包含了 ParameterHandler 和 ResultSetHandler 的属性。ParameterHandler 的主要功能是为 SQL 语句绑定实参,也就是使用传入的参数替换 SQL 语句中的“?”占位符。ResultSetHandler 的主要功能是将结果集映射成结果对象。

参考 BaseStatementHandler 类:org.apache.ibatis.executor.statement.BaseStatementHandler

结论

根据上面的分析,抽象出的主要类有:Configuration、SqlSession、MapperProxy、Exector、StatementHandler、ParameterHandler、ResultSetHandler。

MyBatis 源码篇

MyBatis 源码篇-SQL 执行的流程的更多相关文章

  1. 精尽MyBatis源码分析 - SQL执行过程(四)之延迟加载

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  2. 精尽MyBatis源码分析 - SQL执行过程(二)之 StatementHandler

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  3. 精尽MyBatis源码分析 - SQL执行过程(三)之 ResultSetHandler

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  4. MyBatis 源码篇-MyBatis-Spring 剖析

    本章通过分析 mybatis-spring-x.x.x.jar Jar 包中的源码,了解 MyBatis 是如何与 Spring 进行集成的. Spring 配置文件 MyBatis 与 Spring ...

  5. MyBatis 源码篇-Transaction

    本章简单介绍一下 MyBatis 的事务模块,这块内容比较简单,主要为后面介绍 mybatis-spring-1.**.jar(MyBatis 与 Spring 集成)中的事务模块做准备. 类图结构 ...

  6. MyBatis 源码篇-DataSource

    本章介绍 MyBatis 提供的数据源模块,为后面与 Spring 集成做铺垫,从以下三点出发: 描述 MyBatis 数据源模块的类图结构: MyBatis 是如何集成第三方数据源组件的: Pool ...

  7. MyBatis 源码篇-插件模块

    本章主要描述 MyBatis 插件模块的原理,从以下两点出发: MyBatis 是如何加载插件配置的? MyBatis 是如何实现用户使用自定义拦截器对 SQL 语句执行过程中的某一点进行拦截的? 示 ...

  8. MyBatis 源码篇-日志模块2

    上一章的案例,配置日志级别为 debug,执行一个简单的查询操作,会将 JDBC 操作打印出来.本章通过 MyBatis 日志部分源码分析它是如何实现日志打印的. 在 MyBatis 的日志模块中有一 ...

  9. MyBatis 源码篇-日志模块1

    在 Java 开发中常用的日志框架有 Log4j.Log4j2.Apache Common Log.java.util.logging.slf4j 等,这些日志框架对外提供的接口各不相同.本章详细描述 ...

随机推荐

  1. 异步机制 - IO完成端口

    1 KQUEUE KeInitializeQueue VOID KeInitializeQueue( IN PKQUEUE  Queue, IN ULONG  Count  OPTIONAL ); l ...

  2. 消息中间件MQ

    消息中间件MQ:为方便预览,将思维导图上传至印象笔记,博客园直接上传图片受限于图片大小 https://app.yinxiang.com/shard/s24/nl/27262531/c3e137a5- ...

  3. Linux 时间同步 Chrony

    chrony 是网络时间协议(NTP)的通用实现. chrony 包含两个程序:chronyd 是一个可以在启动时启动的守护程序.chronyc 是一个命令行界面程序,用于监视 chronyd 的性能 ...

  4. Facebook币Libra学习-3.小试牛刀第一笔交易

    我们提供了一个命令行界面(CLI)客户端来与区块链进行交互. 假设 本文档中的所有命令均假定: 您运行的是Linux(基于Red Hat或Debian)或macOS系统. 您可以稳定地连接到互联网. ...

  5. java调用js脚本

    有些情况下,需要java去调用js,groovy等脚本语言,传入参数获取脚本运行的结果. js脚本例子: function add(a,b){ return a + b + number; } jav ...

  6. JAVA 基础编程练习题48 【程序 48 加密】

    48 [程序 48 加密] 题目:某个公司采用公用电话传递数据,数据是四位的整数,在传递过程中是加密的,加密 规则如下:每位数字都加上 5,然后用和除以 10 的余数代替该数字,再将第一位和第四位交 ...

  7. ElasticSearch——集群搭建

    1.准备 1.1.组件 JDK:1.8版本及以上: ElasticSearch:6.2.4版本: 1.2.服务器 3台服务器 2.安装 2.1.下载解压 wget https://artifacts. ...

  8. Lua中用table统一管理需要获取的unity物体

    unity上的组件,可以用table统一管理 然后在初始化时候统一给table赋值,这样需要用到时候直接调用table中对应的key便可拿到对应的物体,省下了在脚本开头一堆声明的脚本,这样就不用声明这 ...

  9. JS实现对数组进行自定义排序

    /** * 数组排序 * @param source 待排序数组 * @param orders 排序字段数组 * @param type 升序-asc 倒序-desc * 调用:var res =  ...

  10. vue-cli 3.x搭建项目以及其中vue.config.js文件的配置

    参考链接:https://www.cnblogs.com/wxh0929/p/11271551.html vue-cli3全面配置详解:https://www.jb51.net/article/150 ...