SqlSessionFactory 是 MyBatis 核心类之一,其重要功能是创建 MyBatis 的核心接口 SqlSession。MyBatis 通过 SqlSessionFactoryBuilder 构建 SqlSessionFactory,构建分为两步:

这种创建方式是一种 Builder 模式。对于复杂对象而言,直接使用构造函数构建会导致大量的逻辑放在构造函数中,使得代码看起来很繁杂。使用一个参数类总领全局,然后按步骤构建可以降低构建的复杂性

Configuration 的作用如下:

插件需要频繁访问映射器内部组成,所以有必要单独研究一下映射器的内部组成。一般而言,映射器由 3 部分组成:

image

image

一般而言,我们主要对参数和 SQL 进行修改,这部分主要反映在 BoundSql 类上,在插件中可以通过 BoundSql 拿到当前运行 SQL 和参数及参数规则,并做出适当修改,满足我们的需求

BoundSql 主要有三个属性:sql、rameterObject 和 parameterMappings

有了 Configuration 便可以快速构建 SqlSessionFactory

todo

映射器其实就是一个动态代理对象,最终会进入 MapperMethod 的 execute 方法。execute 经过简单判断就调用 SqlSession 的删除、更新、插入、选择等方法。这些方法是通过 Executor、StatementHandler、ParameterHandler 和 ResultHandler 来完成数据库操作和结果返回的

Executor 负责执行 Java 和 数据库交互,在 MyBatis 中存在三种执行器,可以在配置文件的 setting 元素的 defaultExecutorType 指定

构造过程如下:

执行过程中,interceptorChain.pluginAll(executor) 就是 MyBatis 插件执行的地方,这行代码会为 executor 构建一层层代理对象,在调度真实的 Executor 方法之前执行配置插件的代码可以修改。SimpleExecutor 的查询处理过程如下所示:

如代码所示,SimpleExecutor 的查询过程一共有 4 步:

StatementHandler,顾名思义就是处理数据库会话的,其创建过程如下:

实际创建的是 RoutingStatementHandler 对象,它实现了 StatementHandler 接口。和 Executor 一样用代理对象做了一层层封装

但 RoutingStatementHandler 对象也不是实际处理查询任务的对象,它通过 适配模式 找到对应的 StatementHandler 来执行具体的数据库操作,MyBatis 也有三种 StatementHandler:SimpleStatementHandler、PreparedStatementHandler 以及 CallableStatementHandler

image

image

我们以最常用的 PreparedStatementHandler 为例讲解 StatementHandler 的三个主要方法:prepare、parameterize 和 query 是如何执行的

首先 Executor 调用 StatementHandler#prepare 进行 SQL 预编译

instantiateStatement 对 SQL 进行预编译,做了一些基础设置(超时、获取最大行数等),然后 Executor 调用 StatementHandler#parameterize 设置参数

可以看到这步操作是调用 ParameterHandler 完成的,2.2.3 中将详细描述参数设置过程。最后 Executor 调用 StatementHandler#query 执行查询

StatementHandler#query 执行 PreparedStatement#execute 执行 SQL,并将结果通过 ResultSetHandler 处理返回,详见 2.2.4

综上所述,我们就知道 MyBatis 处理一条 SQL 的执行过程了,整体过程如下图所示(以 SimpleExecutor 的查询为例):

image

image

下面我们再具体看看 ParameterHandler 和 ResultHandler 是如何设置参数并将结果封装返回的

ParameterHandler 是参数处理器,主要完成对预编译参数的设置。我们先看一下其接口定义:

ParameterHandler#getParameterObject 返回参数对象,ParameterHandler#setParameters 设置预编译 SQL 语句的参数

MyBatis 提供了 默认实现类 DefaultParameterHandler,我们主要看一下 setParameters 方法的实现

ResultHandler 负责处理 SQL 执行结果集,接口定义如下:

其中,handleResultSets 负责封装结果集。MyBatis 提供了实现类 DefaultResultSetHandler,它涉及到使用 JAVASSIST 或 CGLIB 作为延迟加载,然后通过 TypeHandler 和 ObjectFactory 进行组装再返回结果

SqlSession 内部运行图如下所示

image

image

SqlSession 通过 Executor 创建 StatementHandler 来执行 SQL 请求的。StatementHandler 要经过三个步骤:

其中 parameterize 调用 ParameterHandler 设置参数,参数类型根据类型处理器 TypeHandler 处理。query/update 方法通过 ResultHandler 进行结果封装,如果是 update 语句则返回整数,否则通过 TypeHandler 处理结果类型,然后用 ObjectFactory 提供的规则封装对象,返回给调用者

在 MyBatis 中使用插件必须实现 Interceptor 接口

可以看到,MyBatis 使用 模板模式 进行插件开发

插件初始化在 MyBatis 初始化的时候完成,关键代码如下:

MyBatis 在上下文初始化过程中,就开始读入插件节点和我们配置的参数,然后通过反射生成 Interceptor 实例,并调用 setProperties 方法设置我们配置的参数,最后将 interceptorInstance 保存到配置对象 configuration 中

所以插件的初始化是 MyBatis 初始化的时候完成的,这样有助于提高性能

插件使用的是 责任链模式,责任链处理的对象是 MyBatis 四大对象中的一个,在 InterceptorChain 中的每个 Interceptor 都有机会处理目标对象

1 在 SqlSession 四大对象初始化时,调用 InterceptorChain#pluginAll 为其嵌套生成代理对象

image

image

2 InterceptorChain#pluginAll 为目标类生成嵌套代理对象,即有多个拦截器,就为目标类嵌套生成代理类

image

image

3 在调用四大对象的方法时,会代用 InvocationHandler#invoke,由于 Plugin 实现了 InvocationHandler 接口,故实际调用 Plugin#invoke 方法

如果该方法是 @Intercepts 注解设置的拦截方法,则调用 Interceptor.intercept 进行拦截处理,否则直接反射调用 method.invoke(target, args)。因此在 Interceptor#intercept 中可以自定义实际的拦截逻辑

Invocation 封装了目标类 target、要代理的方法 method 以及 method 的参数 args,可通过 Invocation#proceed 完成方法的反射调用。具体代码如下

image

image

MetaObject 可有效读取或设置类属性,其关键的方法有三个:

MetaObject SystemMetaObject#forObject(Object object)

用于包装对象,获取目标对象 object 的 MetaObject

Object MetaObject#getValue(String name)

获取对象属性值,支持 OGNL

void MetaObject#setValue(String name, Object value)

设置对象属性值,支持 OGNL

对象导航图语言(Object Graph Navigation Language),简称 OGNL,是一种表达式语言

QueryTrancePlugin 插件用于在 SQL 中追加调用链路和应用名称

在应用的配置文件中,插件配置如下

最新文章

  1. 一步步搭建react-native环境(苹果OS X)
  2. DataGrid--多记录CRUD
  3. flex的用途
  4. 1018Mysql分表分库
  5. java,我准备好了
  6. IAP的几个问题
  7. python3爬虫 url管理器
  8. VmWare Workstation 10 安装 Ubuntu 14.04 问题解决
  9. poj3252
  10. 01---HTML整理
  11. jquery get checkbox inside element(td).
  12. c++分割字符串(类似于boost::split)
  13. LINUX 笔记-mv命令
  14. 【烂笔头】adb命令篇
  15. std::set
  16. Zsh和oh my zsh的安装和使用
  17. php编程疑难解决-1
  18. models.doc2vec – Deep learning with paragraph2vec
  19. 重启服务器后,启动oracle监听报错 The listener supports no services The command completed successfuslly
  20. UI5-文档-4.36-Device Adaptation

热门文章

  1. oracle impdp 数据迁移 至RDS 亚马逊云
  2. QT_QGIS_基本使用
  3. Docker 私有镜像仓库的搭建及认证
  4. 能卖课 会带货的CRMEB知识付费系统v1.30来了
  5. Android java程序员必备技能,集合与数组中遍历元素,增强for循环的使用详解及代码
  6. Kubernetes-14:一文详解Pod、Node调度规则(亲和性、污点、容忍、固定节点)
  7. ACboy needs your help (动态规划背包)
  8. HTML语言基本单词与css基本单词
  9. 文本三剑客之sed的用法
  10. 仅显示sessionid,servername,serverport的一个springboot小程序