【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

潘潘和他的朋友们发布于 今天 00:00

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

2020 年的大疫情,把世界撕成几片。

时至今日,依旧人心惶惶。

很庆幸,身处这安稳国,

兼得一份安稳工。

·

东家常讲的一个词:深秋心态 。

大势时,不跟风、起哄,

萧条时,不放弃播种和耕耘的信心,

热时不燥、冷时不弃,

这就是深秋心态。

·

大疫情,相信只是大自然的规律,

也恰是我们保持深秋心态的时候,

默默播种和耕耘吧,

今年,世界会慢慢复苏,希望都会来临。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

前言

上节我们介绍了 《 Mybatis系列全解(五):全网最全!详解Mybatis的Mapper映射文件 》,经此一文,我们基本能掌握 Mapper 映射器九大顶级元素的基本用法和其中技巧。在本节,我们开始深入,我挑选了 Mybatis 框架中几个比较硬核的 API ,跟大家一起探讨,夯实了这些 API ,有助于你学习理解整个 Mybatis 框架,特别是 Mybatis 核心的数据处理层,你绝对会形成一套清晰的脉络印记,总之,希望大家都能成为 Mybatis King !

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

另外, 我们的 Mybatis 全解系列一直在更新

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

Mybaits系列全解 (持续更新)

  • Mybatis系列全解(一):手写一套持久层框架
  • Mybatis系列全解(二):Mybatis简介与环境搭建
  • Mybatis系列全解(三):Mybatis简单CRUD使用介绍
  • Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解
  • Mybatis系列全解(五):全网最全!详解Mybatis的Mapper映射文件
  • Mybatis系列全解(六):Mybatis最硬核的API你知道几个?
  • Mybatis系列全解(七):全息视角看Dao层两种实现方式之传统方式与代理方式
  • Mybatis系列全解(八):Mybatis的动态SQL
  • Mybatis系列全解(九):Mybatis的复杂映射
  • Mybatis系列全解(十):Mybatis注解开发
  • Mybatis系列全解(十一):Mybatis缓存全解
  • Mybatis系列全解(十二):Mybatis插件开发
  • Mybatis系列全解(十三):Mybatis代码生成器
  • Mybatis系列全解(十四):Spring集成Mybatis
  • Mybatis系列全解(十五):SpringBoot集成Mybatis
  • Mybatis系列全解(十六):Mybatis源码剖析

本文目录

1、Mybatis 架构与核心API

2、Configuration -- 全局配置对象

3、Resources -- 资源辅助类

4、SqlSessionFactoryBuilder -- 会话工厂构建器

5、SqlSessionFactory -- 会话工厂

6、SqlSession -- 会话

7、Executor -- 执行器

8、StatementHandler -- 语句处理器

9、ParamerHandler -- 参数处理器

10、ResultSetHandler -- 结果集处理器

11、TypeHandler -- 类型转换器

12、MappedStatement -- 语句对象

13、SqlSource -- SQL源

14、BoundSql -- SQL语句

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

1、Mybatis 架构与核心API

不出意外的话,在后续源码剖析相关文章中,我们会对 Mybatis 的源码进行一次大扫荡,一起挖掘每一处值得大家深入理解/记忆的知识点。而在本文中,我们主要先把 Mybatis 的架构/层次铺开,俯视 Mybatis 架构的设计全貌,再把几个硬核的 API 详细消化。

整体顺序脉络,希望让你有所期待 ~

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

我们先简单揭开 Mybatis 神秘的源码包,

瞅瞅 Ta 大致目录结构 :

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

看,Mybatis 的源代码包整齐划一排在 org.apache.ibatis 目录下,基本设计用途我简单梳理成上面这张图,方便大家直观理解,当然只看源码包目录结构,难免会显得枯燥无物,所以我们再看一下,其实 Mybatis 的源码包功能上可以是这么划分:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

上图让我们对 Mybatis 的架构有了抽象的理解。

然而,实际上具体的职能分工,核心 API 的场景应用,到底会是怎样一套流程呈现呢?

看下面这幅功能架构设计,或许你能更好的理解。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

根据 Mybatis 功能架构我们划分成三层:

  • 接口层:该层提供一系列接口让用户直接参与使用,包含信息配置与实际数据操作调用。配置方式包括:基于 XML 配置方式、基于 Java API 配置方式两种方式,用户也可以通过接口层 API 对数据库发起增删改查等操作的请求, 本层接口会把接收到的调用请求交给数据处理层的构件去处理。

  • 数据处理层:该层是 Mybatis 的核心层,负责数据处理,主要包括SQL 参数映射解析、SQL 语句的实际执行、执行结果集的映射处理等。

  • 框架支撑层:该层属于 Mybatis 的后勤保障层,包括数据库连接管理、事务把控管理、配置加载与缓存处理、日志处理、异常处理等,提供基础支撑能力,保障上层的数据处理。

但是,好奇的我们其实想知道,Mybatis 核心部分的数据处理在整体流程中,是如何支撑用户请求?同时各个构件之间交互,又是怎样流转呢?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

很巧,我这里有一张图,介绍了整体流程:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

根据以上框架流程图进行讲解:

  • 创建配置并调用API:这一环节发生在应用程序端,是开发人员在实际应用程序中进行的两步操作,第一步创建核心配置文件 Configuration.xml 和映射文件 mapper.xml (通过注解方式也可创建以上两种配置),准备好基础配置和 SQL 语句之后;第二步就是直接调用 Mybatis 框架中的数据库操作接口。

  • 加载配置并初始化:Mybatis 框架会根据应用程序端提供的核心配置文件与 SQL 映射文件的内容,使用资源辅助类 Resources 把配置文件读取成输入流,然后通过对应的解析器解析并封装到 Configuration 对象和 MappedStatement 对象,最终把对象存储在内存之中。

  • 创建会话并接收请求:在 Mybatis 框架加载配置并初始化配置对象之后,会话工厂构建器 SqlSessionFactoryBuilder 同时创建会话工厂 SqlSessionFactory,会话工厂会根据应用程序端的请求,创建会话 SqlSession,以便应用程序端进行数据库交互。

  • 处理请求:SqlSession 接收到请求之后,实际上并没有进行处理,而是把请求转发给执行器 Executor,执行器再分派到语句处理器 StatementHandler ,语句处理器会结合参数处理器 ParameterHandler ,进行数据库操作(底层封装了 JDBC Statement 操作)。

  • 返回处理结果:每个语句处理器 StatementHandler 处理完成数据库操作之后,会协同 ResultSetHandler 以及类型处理器 TypeHandler ,对底层 JDBC 返回的结果集进行映射封装,最终返回封装对象。

针对以上总体框架流程涉及到的这些硬核 API,下面我们逐个展开介绍,但不会详细剖析源码与原理,包括构建细节,因为这些我们在后续的源码剖析章节中都会详细分析。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

2、Configuration -- 全局配置对象

对于 Mybatis 的全局配置对象 Configuration,我相信无论是初学者还是资深玩家,都不会陌生。整个 Mybatis 的宇宙,都围绕着 Configuration 转。Configuration 对象的结构和 config.xml 配置文件的内容几乎相同,涵盖了properties (属性),settings (设置),typeAliases (类型别名),typeHandlers (类型处理器),objectFactory (对象工厂),mappers (映射器)等等,之前我们有专门的一篇文章详细进行介绍,感兴趣的朋友往上翻到目录列表,找到 《Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解》 一文详细品味一番吧。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

配置对象 Configuration 通过解析器 XMLConfigBuilder 进行解析,把全局配置文件 Config.xml 与 映射器配置文件 Mapper.xml 中的配置信息全部构建成完整的 Configuration 对象,后续我们源码分析时详细剖析整个过程。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

3、Resources -- 资源辅助类

我们知道,像 Configuration 和 Mapper 的配置信息存放在 XML 文件中,Mybatis 框架在构建配置对象时,必须先把 XML 文件信息加载成流,再做后续的解析封装,而 Resources 作为资源的辅助类,恰恰干的就是这个活,无论是通过加载本地资源或是加载远程资源,最终都会通过 类加载器 访问资源文件并输出文件流。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

//加载核心配置文件

InputStream resourceAsStream =

Resources.getResourceAsStream("Config.xml");

Resources 实实在在提供了一系列方法分分钟解决你的文件读取加载问题:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

4、SqlSessionFactoryBuilder -- 会话工厂构建器

我们一撞见 xxxBuilder ,就大致能知道它是某类对象的构建器,这里 SqlSessionFactoryBuilder 也是一样,它是 Mybatis 中的一个会话工厂构建器,在资源辅助类 Resources 读取到文件流信息之后,它负责解析文件流信息并构建会话工厂 SqlSessionFactory。(解析的配置文件包含:全局配置 Configuration 与映射器 Mapper)

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

在程序应用端,我们一般使用 SqlSessionFactoryBuilder 直接构建会话工厂:

// 获得sqlSession工厂对象

SqlSessionFactory sqlSessionFactory =

new SqlSessionFactoryBuilder().build(resourceAsStream);

SqlSessionFactoryBuilder 内部通过解析器 XMLConfigBuilder 解析了文件流,同时封装成为配置对象 Configuration ,再把 Configuration 对象进行传递并构建实例。

public SqlSessionFactory build(

InputStream inputStream,

String environment,

Properties properties) {

// 配置解析器解析

XMLConfigBuilder parser =

new XMLConfigBuilder(

inputStream,environment, properties);

// 最终实例会话工厂

return build(parser.parse());

}

最终实例会话工厂,其实 Mybatis 默认实现是 new 了一个DefaultSqlSessionFactory 实例。

// 最终实例会话工厂

public SqlSessionFactory build(Configuration config) {

return new DefaultSqlSessionFactory(config);

}

会话工厂构建器 SqlSessionFactoryBuilder 应用了构建者模式,主要目的就是为了构建 SqlSessionFactory 对象,以便后续生产 SqlSession 对象,这个构造器基本上算是 Mybatis 框架的入口构建器,它提供了一系列多态方法 build(),支持用户使用 XML 配置文件或 Java API (Properties)来构建会话工厂 SqlSessionFactory 实例。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

因此 SqlSessionFactoryBuilder 实例的最佳作用域是 方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactoryBuilder 中灵活构建会话工厂的一系列接口:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

5、SqlSessionFactory -- 会话工厂

会话工厂 SqlSessionFactory 是一个接口,作用是生产数据库会话对象 SqlSession ,有两个实现类:

  • DefaultSqlSessionFactory (默认实现)

  • SqlSessionManager (仅多实现了一个 Sqlsession 接口,已弃用)

在介绍会话工厂构建器 SqlSessionFactoryBuilder 的时候,我们了解到构建器默认创建了 DefaultSqlSessionFactory 实例,并且会话工厂本身会绑定一个重要的属性 Configuration 对象,在生产会话时,最终也会把 Configuration 配置对象传递并设置到会话 SqlSession 上。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

会话工厂可以简单创建 SqlSession 实例:

// 创建 SqlSession 实例

SqlSession session = sqlSessionFactory.openSession();

会话工厂创建 SqlSession 时,会绑定数据源、事务处理、执行器等等,默认会话工厂实现类 DefaultSqlSessionFactory 在创建会话对象时,最终都会调用 openSessionFromDataSource 方法 ,即是如此实现:

// 每一个 openSession 最终都会调用此处

private SqlSession openSessionFromDataSource(

ExecutorType execType,

TransactionIsolationLevel level,

boolean autoCommit) {

// 环境配置

final Environment environment =

configuration.getEnvironment();

// 事务工厂

final TransactionFactory transactionFactory =

getTransactionFactoryFromEnvironment(environment);

// 事务

Transaction tx =

transactionFactory.newTransaction(

environment.getDataSource(),

level,

autoCommit);

// 执行器

final Executor executor =

configuration.newExecutor(tx, execType);

// 最终生成会话

return new DefaultSqlSession(

configuration, executor, autoCommit);

}

另外,会话工厂其实提供了一系列接口来灵活生产会话 SqlSession,你可以指定:

  • 事务处理:你希望在 session 作用域中使用/开启事务作用域(也就是不自动提交事务),还是使用自动提交(auto-commit),sqlSession 默认不提交事务,对于增删改操作时需要手动提交事务。

  • 数据库连接:你希望 MyBatis 帮你从已配置的数据源获取连接,还是使用自己提供的连接,可以动态创建数据源对象 Connection。

  • 执行器类型:你希望指定某类执行器来创建/执行/预处理语句,可以有普通执行器(SimpleExecutor),或复用执行器(ReuserExecutor)、还是批量执行器(BatchExecutor)等,下面介绍执行器时会详细说明。

  • 事务隔离级别支持:支持 JDBC 的五个隔离级别(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE),对于事务相关的内容,我们后续 《spring 系列全解》 会详细讲,这里简单说明一下就是事务隔离级别主要为了解决例如脏读、不可重复读、幻读等问题,使用不同的事务隔离级别势必会导致不同的数据库执行效率,因此我们再不同的系统/功能中,对隔离级别有不同的需求。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

SqlSessionFactory 一旦被创建就应该在 应用的运行期间 一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是 应用作用域。 最简单的就是使用单例模式或者静态单例模式。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

每个数据库对应一个 SqlSessionFactory 实例。SqlSessionFactory 一旦被创建, 它的生命周期应该与应用的生命周期相同 。所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个;而如果是三个数据库,就需要三个实例,依此类推。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

6、SqlSession -- 会话

SqlSession 是一个接口,有两个实现类:

  • DefaultSqlSession(默认实现)

  • SqlSessionManager (已弃用)

简单来说,通过会话工厂构建出 SqlSession 实例之后,我们就可以进行增删改查了,默认实例 DefaultSqlSession 提供了如此多的方法供用户使用,有超过30个:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

sqlSession 的方法除了 CURD,还提供了事务的控制例如提交/关闭/回滚等、提供了配置对象的获取例如 getConfiguration()、提供了批量语句的执行更新例如 flushStatements()、提供了缓存清除例如 clearCache() 、提供了映射器的使用 getMapper() 等等。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

对于客户端应用层面来说,熟悉 sqlSession 的 API 基本就可以任意操作数据库了,不过我们希望想进一步了解 sqlSession 内部是如何执行 sql 呢?其实 sqlSession 是 Mybatis 中用于和数据库交互的 顶层类,通常将它与本地线程 ThreadLocal 绑定,一个会话使用一个 SqlSession,并且在使用完毕之后进行 关闭。

关注以下图示 sqlSession 红色高亮位置,详细描述了会话的实际执行路径:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

SqlSession 可以理解为一次数据库会话,一次会话当中既可以执行一次 sql ,也允许你批量执行多次,但是一旦会话关闭之后想要再执行 sql,那就必须重新创建会话。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

Spring 集成 Mybatis 之后,通过依赖注入可以创建线程安全的、基于事务的 SqlSession ,并管理他们的生命周期,推荐搭配使用。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

7、Executor -- 执行器

Executor 是一个执行器接口,是 Mybatis 的调度核心,它定义了一组管理 Statement 对象与获取事务的方法,并负责 SQL 语句的生成和一级/二级查询缓存的维护等,SqlSessionFactory 在创建 SqlSession 时会同时创建执行器,并指定执行器类型,默认使用 SimpleExecutor 。执行器接口有5个子孙实现类,其中 BaseExecutor 是抽象类,另外4个子孙实现类分别是:SimpleExecutor 、BatchExecutor、ReuseExecutor、CachingExecutor。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • BaseExecutor:基础执行器(抽象类),对Executor接口进行了基本实现,为下一级实现类执行器提供基础支持。BaseExecutor 有三个子类分别是 SimpleExecutor、ResuseExecutor、BatchExecutor。

  • SimpleExecutor:普通执行器,继承 BaseExecutor 抽象类,是 MyBatis 中 默认 使用的执行器. 每执行一次 update 或 select ,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。(可以是 Statement 或 PrepareStatement 对象)。

  • ReuseExecutor:复用执行器,继承 BaseExecutor 抽象类,这里的复用指的是重复使用 Statement . 它会在内部利用一个 Map 把创建的 Statement 都缓存起来,每次在执行一条 SQL语 句时,它都会去判断之前是否存在基于该 SQL 缓存的 Statement 对象,存在且之前缓存的 Statement 对象对应的 Connection 还没有关闭则会继续使用之前的 Statement 对象,否则将创建一个新的 Statement 对象,并将其缓存起来。因为每一个新的 SqlSession 都有一个新的 Executor 对象,所以我们缓存在 ReuseExecutor 上的 Statement 的作用域是同一个 SqlSession 。

  • BatchExecutor:批处理执行器,继承 BaseExecutor 抽象类,通过批量操作来提高性能,用于将多个 sql 语句一次性输送到数据库执行。由于内部有缓存的实现,所以使用完成后需要调用 flushStatements() 来清除缓存。

  • CachingExecutor : 缓存执行器,继承 BaseExecutor 抽象类,它为 Executor 对象增加了 二级缓存 的相关功能,cachingExecutor 有一个重要属性 delegate,即为委托的执行器对象,可以是 SimpleExecutor、ReuseExecutor、BatchExecutor 中任意一个。CachingExecutor 在执行数据库 update 操作时,它直接调用 委托对象 delegate 的 update 方法;而执行查询时,它会先从缓存中获取查询结果,存在就返回,不存在则委托 delegate 去数据库取,然后存储到缓存 cache 中。

Mybatis 在构建 Configuration 配置类时默认把 ExecutorType.SIMPLE 作为执行器类型,当我们的会话工厂 DefaultSqlSessionFactory 开始生产 SqlSession 会话时,会同时构建执行器,此时就会依据配置类 Configuration 构建时指定的执行器类型来实例具体执行器 ,流程如下:

// 1、Configuration配置类构建时

// 指定了默认执行器类型为:普通执行器

protected ExecutorType defaultExecutorType

= ExecutorType.SIMPLE;

// 2、Configuration配置类中

// 提供方法获取默认执行器类型

public ExecutorType getDefaultExecutorType() {

return defaultExecutorType;

}

// 3、会话工厂创建 SqlSession 实例时

SqlSession session = sqlSessionFactory.openSession();

// 4、openSession 实际逻辑

public SqlSession openSession() {

return

openSessionFromDataSource(

// 这里可就获取了默认执行器

configuration.getDefaultExecutorType(),

null,

false

);

}

这里,肯定有人想知道,我们能否指定其它执行器呢?

答案是:当然可以,有两种方式指定:

  • 第一种方式是通过 Java API 指定,在开启会话 openSession 时进行指定。例如:

// 创建 SqlSession 实例

SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.REUSE)

// ExecutorType是一个枚举

// 有三个值SIMPLE, REUSE, BATCH

  • 另一种通过 Configuration 配置方式来指定默认执行器类型。例如

<settings>

<!--取值范围 SIMPLE, REUSE, BATCH -->

<setting name="defaultExecutorType" value="REUSE"/>

</settings>

对于第二种 settings 的配置方式,其实之前我们在介绍 Mybatis 的配置文件中已经讲过,这里再简单说明一下,像上面配置 settings 中的属性 defaultExecutorType ,基本这些属性都是 Mybatis 额外提供给我们灵活设置的,就算我们不设置 Mybatis 也会有默认值,例如像 defaultExecutorType 的默认值就是 SIMPLE。你看一下 Mybatis 在解析 Configuration 配置时的默认构建,就会明白:

解析 Configuration 的解析器(类与具体方法的代码路径):

org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement

我们截取部分代码逻辑,下面是设置默认执行器类型属性 defaultExecutorType 的内容:

// 配置文件解析器

public class XMLConfigBuilder {

// 最终解析到的 Configuration 对象

protected final Configuration configuration;

// 为 Configuration 对象设置属性

private void settingsElement(Properties props) {

// 设置默认执行器类型,默认是 SIMPLE

configuration.setDefaultExecutorType(

ExecutorType.valueOf(

props.getProperty(

"defaultExecutorType", "SIMPLE")));

// .... 当然这里还有很多属性设置

// .... 只要你在<settings>中配置即可

}

}

注意,到此我们知道可以根据业务需要指定执行器类型,例如 SIMPLE(普通执行器), REUSE(复用执行器), BATCH(批处理执行器)。

但是,有朋友就好奇了,那缓存执行器 CachingExecutor 好像不见说明呢?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

确实如此,因为缓存执行器个其它三个执行器还不太一样,CachingExecutor 是需要我们开启二级缓存才会有,这里大家先不要思考什么是一级缓存,什么二级缓存,后续我们有一文会详细讲缓存整个知识内容。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

大家先了解一个概念即可,就是 Mybatis 的一级缓存是默认开启的,不管你要不要,都会有一级缓存,而二级缓存呢,是默认关闭的,但允许我们手工开启。

对比开启二级缓存前后,执行器执行的区别吧!

  • 不开启二级缓存,执行器执行时:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • 开启二级缓存之后,执行器执行时:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

其实我们实际操作数据库,不会直接接触到执行器 Executor ,不过我们确实可以了解一下基本的执行原理,下面列出了执行器接口提供的众多重载方法,基本用于事务/缓存/数据库管理与访问,可以知道一下:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

到此,对于执行器有了基本的认识,但是实际上,我们知道执行器自身没有去具体执行 SQL 语句,而是分派到语句处理器 StatementHandler ,语句处理器会结合参数处理器 ParameterHandler ,最终进行数据库操作。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

8、StatementHandler -- 语句处理器

StatementHandler 是一个语句处理器接口,它封装了 JDBC Statement 操作,负责对 JDBC Statement 的操作,如 设置参数、结果集映射,是实际跟数据库做交互的一道。StatementHandler 语句处理器实例,是在执行器具体执行 CRUD 操作时构建的,默认使用 PrepareStatementHandler。语句处理器接口有5个子孙实现类,其中 BaseStatementHandler 是抽象类,另外4个子孙实现类分别是:SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler、RoutingStatementHandler。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • BaseStatementHandler:基础语句处理器(抽象类),它基本把语句处理器接口的核心部分都实现了,包括配置绑定、执行器绑定、映射器绑定、参数处理器构建、结果集处理器构建、语句超时设置、语句关闭等,并另外定义了新的方法 instantiateStatement 供不同子类实现以便获取不同类型的语句连接,子类可以普通执行 SQL 语句,也可以做预编译执行,还可以执行存储过程等。

  • SimpleStatementHandler:普通语句处理器,继承 BaseStatementHandler 抽象类,对应 java.sql.Statement 对象的处理,处理普通的不带动态参数运行的 SQL,即执行简单拼接的字符串语句,同时由于 Statement 的特性,SimpleStatementHandler 每次执行都需要编译 SQL (注意:我们知道 SQL 的执行是需要编译和解析的)。

  • PreparedStatementHandler:预编译语句处理器,继承 BaseStatementHandler 抽象类,对应 java.sql.PrepareStatement 对象的处理,相比上面的普通语句处理器,它支持可变参数 SQL 执行,由于 PrepareStatement 的特性,它会进行预编译,在缓存中一旦发现有预编译的命令,会直接解析执行,所以减少了再次编译环节,能够有效提高系统性能,并预防 SQL 注入攻击(所以是系统默认也是我们推荐的语句处理器)。

  • CallableStatementHandler:存储过程处理器,继承 BaseStatementHandler 抽象类,对应 java.sql.CallableStatement 对象的处理,很明了,它是用来调用存储过程的,增加了存储过程的函数调用以及输出/输入参数的处理支持。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • RoutingStatementHandler:路由语句处理器,直接实现了 StatementHandler 接口,作用如其名称,确确实实只是起到了路由功能,并把上面介绍到的三个语句处理器实例作为自身的委托对象而已,所以执行器在构建语句处理器时,都是直接 new 了 RoutingStatementHandler 实例。

// 1、执行器构建语句处理器实例

public StatementHandler newStatementHandler(...) {

// 构建路由语句处理器即可!

StatementHandler statementHandler =

new RoutingStatementHandler(...);

// 其它逻辑忽略...

return statementHandler;

}

// 2、实际构造方法(路由关系)

public RoutingStatementHandler(...) {

// 根据指定类型构造委托实例

switch (ms.getStatementType()) {

case STATEMENT:

delegate = new SimpleStatementHandler(...);

break;

case PREPARED:

delegate = new PreparedStatementHandler(...);

break;

case CALLABLE:

delegate = new CallableStatementHandler(...);

break;

default:

throw new ExecutorException(

"Unknown statement type: "

+ ms.getStatementType());

}

}

我们前面介绍了执行器具体执行 CRUD 操作时,构造的语句处理器默认使用 PrepareStatementHandler ,不过有些好奇的脑袋就想问问,那我们能不能指定语句处理器类型呢?

当然可以,例如我们指定更新用户语句适用预编译处理语句处理器:

<!--取值范围 STATEMENT, PREPARED, CALLABLE -->

<update id="updateUser" statementType="STATEMENT">

update t_user set name = #{newName}

</update>

当 Mybatis 在解析映射器中的每条语句时,会设置语句处理器类型:

// 语句对象解析器

public class XMLStatementBuilder {

// 解析语句节点

public void parseStatementNode() {

// 设置语句处理器类型

// 默认是 PREPARED 类型

StatementType statementType =

StatementType.valueOf(

context.getStringAttribute(

"statementType",

StatementType.PREPARED.toString()

)

);

}

}

所以,语句执行器与数据库的交互过程:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

当然,语句处理器接口 StatementHandler 提供了基本接口,一般我们没必要自定义实现类,所以可以简单看一下即可:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

9、ParamerHandler -- 参数处理器

ParameterHandler 是一个参数处理器接口,它负责把用户传递的参数转换成 JDBC Statement 所需要的参数,底层做数据转换的工作会交给类型转换器 TypeHandler,后面会介绍。

很显然,需要对传入的参数进行转换处理的 StatementHandler 实例只有两个,分别是:

  • PrepareStatementHandler 预编译处理器

  • CallableStatementHandler 存储过程处理器

上面在介绍语句处理器的时候,我们有介绍说基础语句处理器 BaseStatementHandler 在进行实例构建时,会同时构建参数处理器与结果集处理器,所以参数处理器就是在此时被构建。

// 基础语句处理器

public abstract class BaseStatementHandler{

// 构造实例时

protected BaseStatementHandler(...){

// 其它逻辑可忽略...

// 1、构建参数处理器

this.parameterHandler =

conf.newParameterHandler(...);

// 2、构建结果集处理器

this.resultSetHandler =

conf.newResultSetHandler(...);

}

}

对于参数处理器接口,相对简单,只有1个默认实现类 DefaultParameterHandler ,该接口只有两个方法,分别是:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • 1、setParameters 设置参数,发生在 CURD 语句执行时,语句处理器设置参数

// 有2个处理器会使用,分别是:

// 预编译处理器 PreparedStatementHandler

// 存储过程处理器 CallableStatementHandler

public void parameterize(Statement statement) {

//使用ParameterHandler对象来完成对Statement的设值

parameterHandler.setParameters(statement);

}

应用场景例如查询用户对象时,设置姓名,参数处理器结合类型处理器把 name 属性占位符进行赋值。

<select id="queryUSer">

select * from t_user where name = #{name}

</select>

  • 2、getParameterObject 获取参数,发生在结果集返回时,结果集处理器获取对象参数,值得注意的时,该方法只用于存储过程处理器 CallableStatementHandler 。

// 默认结果集处理器

public class DefaultResultSetHandler{

// 处理输出参数

public void handleOutputParameters(...) {

// 获取参数

final Object parameterObject =

parameterHandler.getParameterObject();

// 其它存储过程输出参数处理逻辑...

}

}

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

10、ResultSetHandler -- 结果集处理器

ResultSetHandler 是一个结果集处理器接口,它负责负责将 JDBC 返回的结果集 resultSet 对象转换为 List 类型的集合,是在语句处理器构建实例时被同时创建,底层做数据转换的工作会交给类型转换器 TypeHandler,它有1个默认实现类 DefaultResultSetHandler,该接口有3个方法,分别是:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • handleResultSets:负责结果集处理,完成映射返回结果对象

  • handleCursorResultSets :负责游标对象处理

  • handleOutputParameters :负责存储过程的输出参数处理

结果集处理器对于 JDBC 返回的结果集的基本处理,是先获取我们在映射器 Mapper 中指定 resultType 或 resultMap 映射关系,然后遍历解析结果集中的每一列数据,底层通过 MetaObject 对象做相关的反射处理。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

11、TypeHandler -- 类型转换器

TypeHandler 是一个类型转换器/处理器接口,它负责 Java 数据类型和 JDBC 数据类型之间的映射与转换,当对 Statement 对象设置参数时,由 JavaType 转换为 JdbcType,当对 Statement 返回结果集进行封装映射时,又会将 JdbcType 转换为 JavaType。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

一般,我们可以直接使用 Mybatis 内置的类型处理器,简单看了一下有 65+ 个,当然我们是可以根据业务需要自定义类型处理器的,以便处理复杂类型或非标类型。

具体做法为:

1、实现 org.apache.ibatis.type.TypeHandler 接口;

2、继承 org.apache.ibatis.type.BaseTypeHandler 类。

我们看到接口 TypeHandler 定义了四个方法:

public interface TypeHandler<T> {

// 设置参数

void setParameter(

PreparedStatement ps,

int i, T parameter,

JdbcType jdbcType);

// 根据列名获取转换结果

T getResult(ResultSet rs, String columnName);

// 根据列下标获取转换结果

T getResult(ResultSet rs, int columnIndex);

// 根据列下标获取【存储过程】的输出结果

T getResult(CallableStatement cs, int columnIndex);

}

其实,我之前在介绍 Mybatis 核心配置的时候,有大力介绍过类型处理器,没必要重复写(其实是懒),感兴趣的朋友可以直接看我们之前的文章 《Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解》中对类型处理器 TypeHandler 的介绍。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

12、MappedStatement -- 语句对象

MappedStatement 语句对象,就是我们在映射器 Mapper 中维护的每一条语句,例如 <select|update|delete|insert>,Mybatis 中通过语句构造器 XMLStatementBuilder 对每一个语句进行解析:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

整个解析过程分为4步骤:

  • 1、配置解析器 XMLConfigBuilder 解析映射器:

// Configuration 配置解析器

public class XMLConfigBuilder{

// 解析映射器

private void mapperElement(){

// 创建映射器解析实例

XMLMapperBuilder mapperParser =

new XMLMapperBuilder(...);

// 开始解析

mapperParser.parse();

}

}

2、映射对象解析器 XMLMapperBuilder 解析语句

// 映射对象解析器

public class XMLMapperBuilder{

// 1、解析入口

public void parse() {

// 解析映射器文件

configurationElement(

parser.evalNode("/mapper"));

}

// 2、节点解析

private void configurationElement(XNode context) {

// 构建语句对象

buildStatementFromContext(

context.evalNodes(

"select|insert|update|delete"));

}

// 3、最终调用语句解析器

private void buildStatementFromContext(){

// 创建语句解析实例

XMLStatementBuilder statementParser =

new XMLStatementBuilder();

// 解析语句节点

statementParser.parseStatementNode();

}

}

3、语句解析器 XMLStatementBuilder 解析每一个节点

// 语句解析器

public class XMLStatementBuilder{

// 解析语句节点

public void parseStatementNode() {

// 通过语句辅助类构建语句对象

builderAssistant.addMappedStatement(...)

}

}

4、语句辅助类 MapperBuilderAssistant 添加进语句集合中

// 语句辅助类

public class MapperBuilderAssistant{

// 添加语句对象

public MappedStatement addMappedStatement(

// 最终添加到配置类的语句集合中

configuration.addMappedStatement(statement);

}

}

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

13、SqlSource -- SQL源

SqlSource 是一个 SQL 源接口,它会结合用户传递的参数对象 parameterObject,动态地生成 SQL 语句,并最终封装成 BoundSql 对象。SqlSource 接口有5个实现类,分别是:StaticSqlSource、DynamicSqlSource、RawSqlSource、ProviderSqlSource、VelocitySqlSource (这只是一个测试用例,而非真正模板 Sql 源实现类)。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • StaticSqlSource:静态 SQL 源实现类,所有的 SQL 源最终都会构建 StaticSqlSource 实例,该实现类会生成最终可执行的 SQL 语句供 statement 或 prepareStatement 使用。

  • RawSqlSource:原生 SQL 源实现类,解析构建含有 '#{}' 占位符的 SQL 语句或原生 SQL 语句,解析完最终会构建 StaticSqlSource 实例。

  • DynamicSqlSource:动态 SQL 源实现类,解析构建含有 '${}' 替换符的 SQL 语句或含有动态 SQL 的语句(例如 If/Where/Foreach等),解析完最终会构建 StaticSqlSource 实例。

  • ProviderSqlSource:注解方式的 SQL 源实现类,会根据 SQL 语句的内容分发给 RawSqlSource 或 DynamicSqlSource ,当然最终也会构建 StaticSqlSource 实例。

  • VelocitySqlSource:模板 SQL 源实现类,官方申明这只是一个测试用例,而非真正的模板 Sql 源实现类。

SqlSource 实例在配置类 Configuration 解析阶段就被创建,Mybatis 框架会依据3个维度的信息来选择构建哪种数据源实例:

  • 第一个维度:客户端的 SQL 配置方式:XML 方式或者注解方式。

  • 第二个维度:SQL 语句中是否使用动态 SQL ( if/where/foreach 等 )。

  • 第三个维度:SQL 语句中是否含有替换符 '${}' 或占位符 '#{}' 。

SqlSource 接口只有一个方法 getBoundSql ,就是创建 BoundSql 对象。

public interface SqlSource {

BoundSql getBoundSql(Object parameterObject);

}

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

14、BoundSql -- SQL语句

BoundSql 对象存储了动态生成的 SQL 语句以及相应的参数信息,BoundSql 对象是在执行器具体执行 CURD 时通过实际的 SqlSource 实例所构建。通过 BoundSql 能够获取到实际数据库执行的 SQL 语句,系统可根据 SQL 语句构建 Statement 或者 PrepareStatement 。

public class BoundSql {

//该字段中记录了SQL语句,该SQL语句中可能含有"?"占位符

private final String sql;

//SQL中的参数属性集合

private final List<ParameterMapping> parameterMappings;

//客户端执行SQL时传入的实际参数值

private final Object parameterObject;

//复制 DynamicContext.bindings 集合中的内容

private final Map<String, Object> additionalParameters;

//通过 additionalParameters 构建元参数对象

private final MetaObject metaParameters;

}

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

总结

本文整整2周才基本修整完善,顺着 Mybatis 的数据库核心执行流程,我们大致介绍了 Mybatis 中几个相对核心的 API,我们是一边构建核心架构功能设计图示,一边梳理 API 相关知识脉络,目前基本算是捋清。

其中比较苦恼的问题就是,对于文章内容范围尺度的把控,每篇文章我都希望讲得全面,讲得细致,这两个坚持就注定了文章的内容不可能全是干货,导致略懂的人只能选择跳跃式阅读,对于有营养的知识点需要挑挑拣拣,自我取舍;同时还会导致内容篇幅巨长,导致整体阅读耗时过长,不易快速吸收。

后续尝试改变一下写作方式,尽量输出干货、内容点到为止,对于需要巨细剖析的内容,我们单立文章解读。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

java框架mybatis后端mybatis-plus

阅读 47发布于 今天 00:00

本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议


框架系列全解

有点特别的架构师

avatar

潘潘和他的朋友们

有点特别的架构师

1 声望

2 粉丝

0 条评论

得票时间

avatar

潘潘和他的朋友们

有点特别的架构师

1 声望

2 粉丝

宣传栏

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

2020 年的大疫情,把世界撕成几片。

时至今日,依旧人心惶惶。

很庆幸,身处这安稳国,

兼得一份安稳工。

·

东家常讲的一个词:深秋心态 。

大势时,不跟风、起哄,

萧条时,不放弃播种和耕耘的信心,

热时不燥、冷时不弃,

这就是深秋心态。

·

大疫情,相信只是大自然的规律,

也恰是我们保持深秋心态的时候,

默默播种和耕耘吧,

今年,世界会慢慢复苏,希望都会来临。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

前言

上节我们介绍了 《 Mybatis系列全解(五):全网最全!详解Mybatis的Mapper映射文件 》,经此一文,我们基本能掌握 Mapper 映射器九大顶级元素的基本用法和其中技巧。在本节,我们开始深入,我挑选了 Mybatis 框架中几个比较硬核的 API ,跟大家一起探讨,夯实了这些 API ,有助于你学习理解整个 Mybatis 框架,特别是 Mybatis 核心的数据处理层,你绝对会形成一套清晰的脉络印记,总之,希望大家都能成为 Mybatis King !

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

另外, 我们的 Mybatis 全解系列一直在更新

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

Mybaits系列全解 (持续更新)

  • Mybatis系列全解(一):手写一套持久层框架
  • Mybatis系列全解(二):Mybatis简介与环境搭建
  • Mybatis系列全解(三):Mybatis简单CRUD使用介绍
  • Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解
  • Mybatis系列全解(五):全网最全!详解Mybatis的Mapper映射文件
  • Mybatis系列全解(六):Mybatis最硬核的API你知道几个?
  • Mybatis系列全解(七):全息视角看Dao层两种实现方式之传统方式与代理方式
  • Mybatis系列全解(八):Mybatis的动态SQL
  • Mybatis系列全解(九):Mybatis的复杂映射
  • Mybatis系列全解(十):Mybatis注解开发
  • Mybatis系列全解(十一):Mybatis缓存全解
  • Mybatis系列全解(十二):Mybatis插件开发
  • Mybatis系列全解(十三):Mybatis代码生成器
  • Mybatis系列全解(十四):Spring集成Mybatis
  • Mybatis系列全解(十五):SpringBoot集成Mybatis
  • Mybatis系列全解(十六):Mybatis源码剖析

本文目录

1、Mybatis 架构与核心API

2、Configuration -- 全局配置对象

3、Resources -- 资源辅助类

4、SqlSessionFactoryBuilder -- 会话工厂构建器

5、SqlSessionFactory -- 会话工厂

6、SqlSession -- 会话

7、Executor -- 执行器

8、StatementHandler -- 语句处理器

9、ParamerHandler -- 参数处理器

10、ResultSetHandler -- 结果集处理器

11、TypeHandler -- 类型转换器

12、MappedStatement -- 语句对象

13、SqlSource -- SQL源

14、BoundSql -- SQL语句

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

1、Mybatis 架构与核心API

不出意外的话,在后续源码剖析相关文章中,我们会对 Mybatis 的源码进行一次大扫荡,一起挖掘每一处值得大家深入理解/记忆的知识点。而在本文中,我们主要先把 Mybatis 的架构/层次铺开,俯视 Mybatis 架构的设计全貌,再把几个硬核的 API 详细消化。

整体顺序脉络,希望让你有所期待 ~

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

我们先简单揭开 Mybatis 神秘的源码包,

瞅瞅 Ta 大致目录结构 :

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

看,Mybatis 的源代码包整齐划一排在 org.apache.ibatis 目录下,基本设计用途我简单梳理成上面这张图,方便大家直观理解,当然只看源码包目录结构,难免会显得枯燥无物,所以我们再看一下,其实 Mybatis 的源码包功能上可以是这么划分:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

上图让我们对 Mybatis 的架构有了抽象的理解。

然而,实际上具体的职能分工,核心 API 的场景应用,到底会是怎样一套流程呈现呢?

看下面这幅功能架构设计,或许你能更好的理解。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

根据 Mybatis 功能架构我们划分成三层:

  • 接口层:该层提供一系列接口让用户直接参与使用,包含信息配置与实际数据操作调用。配置方式包括:基于 XML 配置方式、基于 Java API 配置方式两种方式,用户也可以通过接口层 API 对数据库发起增删改查等操作的请求, 本层接口会把接收到的调用请求交给数据处理层的构件去处理。

  • 数据处理层:该层是 Mybatis 的核心层,负责数据处理,主要包括SQL 参数映射解析、SQL 语句的实际执行、执行结果集的映射处理等。

  • 框架支撑层:该层属于 Mybatis 的后勤保障层,包括数据库连接管理、事务把控管理、配置加载与缓存处理、日志处理、异常处理等,提供基础支撑能力,保障上层的数据处理。

但是,好奇的我们其实想知道,Mybatis 核心部分的数据处理在整体流程中,是如何支撑用户请求?同时各个构件之间交互,又是怎样流转呢?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

很巧,我这里有一张图,介绍了整体流程:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

根据以上框架流程图进行讲解:

  • 创建配置并调用API:这一环节发生在应用程序端,是开发人员在实际应用程序中进行的两步操作,第一步创建核心配置文件 Configuration.xml 和映射文件 mapper.xml (通过注解方式也可创建以上两种配置),准备好基础配置和 SQL 语句之后;第二步就是直接调用 Mybatis 框架中的数据库操作接口。

  • 加载配置并初始化:Mybatis 框架会根据应用程序端提供的核心配置文件与 SQL 映射文件的内容,使用资源辅助类 Resources 把配置文件读取成输入流,然后通过对应的解析器解析并封装到 Configuration 对象和 MappedStatement 对象,最终把对象存储在内存之中。

  • 创建会话并接收请求:在 Mybatis 框架加载配置并初始化配置对象之后,会话工厂构建器 SqlSessionFactoryBuilder 同时创建会话工厂 SqlSessionFactory,会话工厂会根据应用程序端的请求,创建会话 SqlSession,以便应用程序端进行数据库交互。

  • 处理请求:SqlSession 接收到请求之后,实际上并没有进行处理,而是把请求转发给执行器 Executor,执行器再分派到语句处理器 StatementHandler ,语句处理器会结合参数处理器 ParameterHandler ,进行数据库操作(底层封装了 JDBC Statement 操作)。

  • 返回处理结果:每个语句处理器 StatementHandler 处理完成数据库操作之后,会协同 ResultSetHandler 以及类型处理器 TypeHandler ,对底层 JDBC 返回的结果集进行映射封装,最终返回封装对象。

针对以上总体框架流程涉及到的这些硬核 API,下面我们逐个展开介绍,但不会详细剖析源码与原理,包括构建细节,因为这些我们在后续的源码剖析章节中都会详细分析。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

2、Configuration -- 全局配置对象

对于 Mybatis 的全局配置对象 Configuration,我相信无论是初学者还是资深玩家,都不会陌生。整个 Mybatis 的宇宙,都围绕着 Configuration 转。Configuration 对象的结构和 config.xml 配置文件的内容几乎相同,涵盖了properties (属性),settings (设置),typeAliases (类型别名),typeHandlers (类型处理器),objectFactory (对象工厂),mappers (映射器)等等,之前我们有专门的一篇文章详细进行介绍,感兴趣的朋友往上翻到目录列表,找到 《Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解》 一文详细品味一番吧。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

配置对象 Configuration 通过解析器 XMLConfigBuilder 进行解析,把全局配置文件 Config.xml 与 映射器配置文件 Mapper.xml 中的配置信息全部构建成完整的 Configuration 对象,后续我们源码分析时详细剖析整个过程。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

3、Resources -- 资源辅助类

我们知道,像 Configuration 和 Mapper 的配置信息存放在 XML 文件中,Mybatis 框架在构建配置对象时,必须先把 XML 文件信息加载成流,再做后续的解析封装,而 Resources 作为资源的辅助类,恰恰干的就是这个活,无论是通过加载本地资源或是加载远程资源,最终都会通过 类加载器 访问资源文件并输出文件流。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

//加载核心配置文件

InputStream resourceAsStream =

Resources.getResourceAsStream("Config.xml");

Resources 实实在在提供了一系列方法分分钟解决你的文件读取加载问题:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

4、SqlSessionFactoryBuilder -- 会话工厂构建器

我们一撞见 xxxBuilder ,就大致能知道它是某类对象的构建器,这里 SqlSessionFactoryBuilder 也是一样,它是 Mybatis 中的一个会话工厂构建器,在资源辅助类 Resources 读取到文件流信息之后,它负责解析文件流信息并构建会话工厂 SqlSessionFactory。(解析的配置文件包含:全局配置 Configuration 与映射器 Mapper)

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

在程序应用端,我们一般使用 SqlSessionFactoryBuilder 直接构建会话工厂:

// 获得sqlSession工厂对象

SqlSessionFactory sqlSessionFactory =

new SqlSessionFactoryBuilder().build(resourceAsStream);

SqlSessionFactoryBuilder 内部通过解析器 XMLConfigBuilder 解析了文件流,同时封装成为配置对象 Configuration ,再把 Configuration 对象进行传递并构建实例。

public SqlSessionFactory build(

InputStream inputStream,

String environment,

Properties properties) {

// 配置解析器解析

XMLConfigBuilder parser =

new XMLConfigBuilder(

inputStream,environment, properties);

// 最终实例会话工厂

return build(parser.parse());

}

最终实例会话工厂,其实 Mybatis 默认实现是 new 了一个DefaultSqlSessionFactory 实例。

// 最终实例会话工厂

public SqlSessionFactory build(Configuration config) {

return new DefaultSqlSessionFactory(config);

}

会话工厂构建器 SqlSessionFactoryBuilder 应用了构建者模式,主要目的就是为了构建 SqlSessionFactory 对象,以便后续生产 SqlSession 对象,这个构造器基本上算是 Mybatis 框架的入口构建器,它提供了一系列多态方法 build(),支持用户使用 XML 配置文件或 Java API (Properties)来构建会话工厂 SqlSessionFactory 实例。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

因此 SqlSessionFactoryBuilder 实例的最佳作用域是 方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactoryBuilder 中灵活构建会话工厂的一系列接口:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

5、SqlSessionFactory -- 会话工厂

会话工厂 SqlSessionFactory 是一个接口,作用是生产数据库会话对象 SqlSession ,有两个实现类:

  • DefaultSqlSessionFactory (默认实现)

  • SqlSessionManager (仅多实现了一个 Sqlsession 接口,已弃用)

在介绍会话工厂构建器 SqlSessionFactoryBuilder 的时候,我们了解到构建器默认创建了 DefaultSqlSessionFactory 实例,并且会话工厂本身会绑定一个重要的属性 Configuration 对象,在生产会话时,最终也会把 Configuration 配置对象传递并设置到会话 SqlSession 上。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

会话工厂可以简单创建 SqlSession 实例:

// 创建 SqlSession 实例

SqlSession session = sqlSessionFactory.openSession();

会话工厂创建 SqlSession 时,会绑定数据源、事务处理、执行器等等,默认会话工厂实现类 DefaultSqlSessionFactory 在创建会话对象时,最终都会调用 openSessionFromDataSource 方法 ,即是如此实现:

// 每一个 openSession 最终都会调用此处

private SqlSession openSessionFromDataSource(

ExecutorType execType,

TransactionIsolationLevel level,

boolean autoCommit) {

// 环境配置

final Environment environment =

configuration.getEnvironment();

// 事务工厂

final TransactionFactory transactionFactory =

getTransactionFactoryFromEnvironment(environment);

// 事务

Transaction tx =

transactionFactory.newTransaction(

environment.getDataSource(),

level,

autoCommit);

// 执行器

final Executor executor =

configuration.newExecutor(tx, execType);

// 最终生成会话

return new DefaultSqlSession(

configuration, executor, autoCommit);

}

另外,会话工厂其实提供了一系列接口来灵活生产会话 SqlSession,你可以指定:

  • 事务处理:你希望在 session 作用域中使用/开启事务作用域(也就是不自动提交事务),还是使用自动提交(auto-commit),sqlSession 默认不提交事务,对于增删改操作时需要手动提交事务。

  • 数据库连接:你希望 MyBatis 帮你从已配置的数据源获取连接,还是使用自己提供的连接,可以动态创建数据源对象 Connection。

  • 执行器类型:你希望指定某类执行器来创建/执行/预处理语句,可以有普通执行器(SimpleExecutor),或复用执行器(ReuserExecutor)、还是批量执行器(BatchExecutor)等,下面介绍执行器时会详细说明。

  • 事务隔离级别支持:支持 JDBC 的五个隔离级别(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE),对于事务相关的内容,我们后续 《spring 系列全解》 会详细讲,这里简单说明一下就是事务隔离级别主要为了解决例如脏读、不可重复读、幻读等问题,使用不同的事务隔离级别势必会导致不同的数据库执行效率,因此我们再不同的系统/功能中,对隔离级别有不同的需求。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

SqlSessionFactory 一旦被创建就应该在 应用的运行期间 一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是 应用作用域。 最简单的就是使用单例模式或者静态单例模式。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

每个数据库对应一个 SqlSessionFactory 实例。SqlSessionFactory 一旦被创建, 它的生命周期应该与应用的生命周期相同 。所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个;而如果是三个数据库,就需要三个实例,依此类推。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

6、SqlSession -- 会话

SqlSession 是一个接口,有两个实现类:

  • DefaultSqlSession(默认实现)

  • SqlSessionManager (已弃用)

简单来说,通过会话工厂构建出 SqlSession 实例之后,我们就可以进行增删改查了,默认实例 DefaultSqlSession 提供了如此多的方法供用户使用,有超过30个:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

sqlSession 的方法除了 CURD,还提供了事务的控制例如提交/关闭/回滚等、提供了配置对象的获取例如 getConfiguration()、提供了批量语句的执行更新例如 flushStatements()、提供了缓存清除例如 clearCache() 、提供了映射器的使用 getMapper() 等等。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

对于客户端应用层面来说,熟悉 sqlSession 的 API 基本就可以任意操作数据库了,不过我们希望想进一步了解 sqlSession 内部是如何执行 sql 呢?其实 sqlSession 是 Mybatis 中用于和数据库交互的 顶层类,通常将它与本地线程 ThreadLocal 绑定,一个会话使用一个 SqlSession,并且在使用完毕之后进行 关闭。

关注以下图示 sqlSession 红色高亮位置,详细描述了会话的实际执行路径:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

SqlSession 可以理解为一次数据库会话,一次会话当中既可以执行一次 sql ,也允许你批量执行多次,但是一旦会话关闭之后想要再执行 sql,那就必须重新创建会话。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

Spring 集成 Mybatis 之后,通过依赖注入可以创建线程安全的、基于事务的 SqlSession ,并管理他们的生命周期,推荐搭配使用。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

7、Executor -- 执行器

Executor 是一个执行器接口,是 Mybatis 的调度核心,它定义了一组管理 Statement 对象与获取事务的方法,并负责 SQL 语句的生成和一级/二级查询缓存的维护等,SqlSessionFactory 在创建 SqlSession 时会同时创建执行器,并指定执行器类型,默认使用 SimpleExecutor 。执行器接口有5个子孙实现类,其中 BaseExecutor 是抽象类,另外4个子孙实现类分别是:SimpleExecutor 、BatchExecutor、ReuseExecutor、CachingExecutor。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • BaseExecutor:基础执行器(抽象类),对Executor接口进行了基本实现,为下一级实现类执行器提供基础支持。BaseExecutor 有三个子类分别是 SimpleExecutor、ResuseExecutor、BatchExecutor。

  • SimpleExecutor:普通执行器,继承 BaseExecutor 抽象类,是 MyBatis 中 默认 使用的执行器. 每执行一次 update 或 select ,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。(可以是 Statement 或 PrepareStatement 对象)。

  • ReuseExecutor:复用执行器,继承 BaseExecutor 抽象类,这里的复用指的是重复使用 Statement . 它会在内部利用一个 Map 把创建的 Statement 都缓存起来,每次在执行一条 SQL语 句时,它都会去判断之前是否存在基于该 SQL 缓存的 Statement 对象,存在且之前缓存的 Statement 对象对应的 Connection 还没有关闭则会继续使用之前的 Statement 对象,否则将创建一个新的 Statement 对象,并将其缓存起来。因为每一个新的 SqlSession 都有一个新的 Executor 对象,所以我们缓存在 ReuseExecutor 上的 Statement 的作用域是同一个 SqlSession 。

  • BatchExecutor:批处理执行器,继承 BaseExecutor 抽象类,通过批量操作来提高性能,用于将多个 sql 语句一次性输送到数据库执行。由于内部有缓存的实现,所以使用完成后需要调用 flushStatements() 来清除缓存。

  • CachingExecutor : 缓存执行器,继承 BaseExecutor 抽象类,它为 Executor 对象增加了 二级缓存 的相关功能,cachingExecutor 有一个重要属性 delegate,即为委托的执行器对象,可以是 SimpleExecutor、ReuseExecutor、BatchExecutor 中任意一个。CachingExecutor 在执行数据库 update 操作时,它直接调用 委托对象 delegate 的 update 方法;而执行查询时,它会先从缓存中获取查询结果,存在就返回,不存在则委托 delegate 去数据库取,然后存储到缓存 cache 中。

Mybatis 在构建 Configuration 配置类时默认把 ExecutorType.SIMPLE 作为执行器类型,当我们的会话工厂 DefaultSqlSessionFactory 开始生产 SqlSession 会话时,会同时构建执行器,此时就会依据配置类 Configuration 构建时指定的执行器类型来实例具体执行器 ,流程如下:

// 1、Configuration配置类构建时

// 指定了默认执行器类型为:普通执行器

protected ExecutorType defaultExecutorType

= ExecutorType.SIMPLE;

// 2、Configuration配置类中

// 提供方法获取默认执行器类型

public ExecutorType getDefaultExecutorType() {

return defaultExecutorType;

}

// 3、会话工厂创建 SqlSession 实例时

SqlSession session = sqlSessionFactory.openSession();

// 4、openSession 实际逻辑

public SqlSession openSession() {

return

openSessionFromDataSource(

// 这里可就获取了默认执行器

configuration.getDefaultExecutorType(),

null,

false

);

}

这里,肯定有人想知道,我们能否指定其它执行器呢?

答案是:当然可以,有两种方式指定:

  • 第一种方式是通过 Java API 指定,在开启会话 openSession 时进行指定。例如:

// 创建 SqlSession 实例

SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.REUSE)

// ExecutorType是一个枚举

// 有三个值SIMPLE, REUSE, BATCH

  • 另一种通过 Configuration 配置方式来指定默认执行器类型。例如

<settings>

<!--取值范围 SIMPLE, REUSE, BATCH -->

<setting name="defaultExecutorType" value="REUSE"/>

</settings>

对于第二种 settings 的配置方式,其实之前我们在介绍 Mybatis 的配置文件中已经讲过,这里再简单说明一下,像上面配置 settings 中的属性 defaultExecutorType ,基本这些属性都是 Mybatis 额外提供给我们灵活设置的,就算我们不设置 Mybatis 也会有默认值,例如像 defaultExecutorType 的默认值就是 SIMPLE。你看一下 Mybatis 在解析 Configuration 配置时的默认构建,就会明白:

解析 Configuration 的解析器(类与具体方法的代码路径):

org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement

我们截取部分代码逻辑,下面是设置默认执行器类型属性 defaultExecutorType 的内容:

// 配置文件解析器

public class XMLConfigBuilder {

// 最终解析到的 Configuration 对象

protected final Configuration configuration;

// 为 Configuration 对象设置属性

private void settingsElement(Properties props) {

// 设置默认执行器类型,默认是 SIMPLE

configuration.setDefaultExecutorType(

ExecutorType.valueOf(

props.getProperty(

"defaultExecutorType", "SIMPLE")));

// .... 当然这里还有很多属性设置

// .... 只要你在<settings>中配置即可

}

}

注意,到此我们知道可以根据业务需要指定执行器类型,例如 SIMPLE(普通执行器), REUSE(复用执行器), BATCH(批处理执行器)。

但是,有朋友就好奇了,那缓存执行器 CachingExecutor 好像不见说明呢?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

确实如此,因为缓存执行器个其它三个执行器还不太一样,CachingExecutor 是需要我们开启二级缓存才会有,这里大家先不要思考什么是一级缓存,什么二级缓存,后续我们有一文会详细讲缓存整个知识内容。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

大家先了解一个概念即可,就是 Mybatis 的一级缓存是默认开启的,不管你要不要,都会有一级缓存,而二级缓存呢,是默认关闭的,但允许我们手工开启。

对比开启二级缓存前后,执行器执行的区别吧!

  • 不开启二级缓存,执行器执行时:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • 开启二级缓存之后,执行器执行时:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

其实我们实际操作数据库,不会直接接触到执行器 Executor ,不过我们确实可以了解一下基本的执行原理,下面列出了执行器接口提供的众多重载方法,基本用于事务/缓存/数据库管理与访问,可以知道一下:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

到此,对于执行器有了基本的认识,但是实际上,我们知道执行器自身没有去具体执行 SQL 语句,而是分派到语句处理器 StatementHandler ,语句处理器会结合参数处理器 ParameterHandler ,最终进行数据库操作。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

8、StatementHandler -- 语句处理器

StatementHandler 是一个语句处理器接口,它封装了 JDBC Statement 操作,负责对 JDBC Statement 的操作,如 设置参数、结果集映射,是实际跟数据库做交互的一道。StatementHandler 语句处理器实例,是在执行器具体执行 CRUD 操作时构建的,默认使用 PrepareStatementHandler。语句处理器接口有5个子孙实现类,其中 BaseStatementHandler 是抽象类,另外4个子孙实现类分别是:SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler、RoutingStatementHandler。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • BaseStatementHandler:基础语句处理器(抽象类),它基本把语句处理器接口的核心部分都实现了,包括配置绑定、执行器绑定、映射器绑定、参数处理器构建、结果集处理器构建、语句超时设置、语句关闭等,并另外定义了新的方法 instantiateStatement 供不同子类实现以便获取不同类型的语句连接,子类可以普通执行 SQL 语句,也可以做预编译执行,还可以执行存储过程等。

  • SimpleStatementHandler:普通语句处理器,继承 BaseStatementHandler 抽象类,对应 java.sql.Statement 对象的处理,处理普通的不带动态参数运行的 SQL,即执行简单拼接的字符串语句,同时由于 Statement 的特性,SimpleStatementHandler 每次执行都需要编译 SQL (注意:我们知道 SQL 的执行是需要编译和解析的)。

  • PreparedStatementHandler:预编译语句处理器,继承 BaseStatementHandler 抽象类,对应 java.sql.PrepareStatement 对象的处理,相比上面的普通语句处理器,它支持可变参数 SQL 执行,由于 PrepareStatement 的特性,它会进行预编译,在缓存中一旦发现有预编译的命令,会直接解析执行,所以减少了再次编译环节,能够有效提高系统性能,并预防 SQL 注入攻击(所以是系统默认也是我们推荐的语句处理器)。

  • CallableStatementHandler:存储过程处理器,继承 BaseStatementHandler 抽象类,对应 java.sql.CallableStatement 对象的处理,很明了,它是用来调用存储过程的,增加了存储过程的函数调用以及输出/输入参数的处理支持。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • RoutingStatementHandler:路由语句处理器,直接实现了 StatementHandler 接口,作用如其名称,确确实实只是起到了路由功能,并把上面介绍到的三个语句处理器实例作为自身的委托对象而已,所以执行器在构建语句处理器时,都是直接 new 了 RoutingStatementHandler 实例。

// 1、执行器构建语句处理器实例

public StatementHandler newStatementHandler(...) {

// 构建路由语句处理器即可!

StatementHandler statementHandler =

new RoutingStatementHandler(...);

// 其它逻辑忽略...

return statementHandler;

}

// 2、实际构造方法(路由关系)

public RoutingStatementHandler(...) {

// 根据指定类型构造委托实例

switch (ms.getStatementType()) {

case STATEMENT:

delegate = new SimpleStatementHandler(...);

break;

case PREPARED:

delegate = new PreparedStatementHandler(...);

break;

case CALLABLE:

delegate = new CallableStatementHandler(...);

break;

default:

throw new ExecutorException(

"Unknown statement type: "

+ ms.getStatementType());

}

}

我们前面介绍了执行器具体执行 CRUD 操作时,构造的语句处理器默认使用 PrepareStatementHandler ,不过有些好奇的脑袋就想问问,那我们能不能指定语句处理器类型呢?

当然可以,例如我们指定更新用户语句适用预编译处理语句处理器:

<!--取值范围 STATEMENT, PREPARED, CALLABLE -->

<update id="updateUser" statementType="STATEMENT">

update t_user set name = #{newName}

</update>

当 Mybatis 在解析映射器中的每条语句时,会设置语句处理器类型:

// 语句对象解析器

public class XMLStatementBuilder {

// 解析语句节点

public void parseStatementNode() {

// 设置语句处理器类型

// 默认是 PREPARED 类型

StatementType statementType =

StatementType.valueOf(

context.getStringAttribute(

"statementType",

StatementType.PREPARED.toString()

)

);

}

}

所以,语句执行器与数据库的交互过程:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

当然,语句处理器接口 StatementHandler 提供了基本接口,一般我们没必要自定义实现类,所以可以简单看一下即可:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

9、ParamerHandler -- 参数处理器

ParameterHandler 是一个参数处理器接口,它负责把用户传递的参数转换成 JDBC Statement 所需要的参数,底层做数据转换的工作会交给类型转换器 TypeHandler,后面会介绍。

很显然,需要对传入的参数进行转换处理的 StatementHandler 实例只有两个,分别是:

  • PrepareStatementHandler 预编译处理器

  • CallableStatementHandler 存储过程处理器

上面在介绍语句处理器的时候,我们有介绍说基础语句处理器 BaseStatementHandler 在进行实例构建时,会同时构建参数处理器与结果集处理器,所以参数处理器就是在此时被构建。

// 基础语句处理器

public abstract class BaseStatementHandler{

// 构造实例时

protected BaseStatementHandler(...){

// 其它逻辑可忽略...

// 1、构建参数处理器

this.parameterHandler =

conf.newParameterHandler(...);

// 2、构建结果集处理器

this.resultSetHandler =

conf.newResultSetHandler(...);

}

}

对于参数处理器接口,相对简单,只有1个默认实现类 DefaultParameterHandler ,该接口只有两个方法,分别是:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • 1、setParameters 设置参数,发生在 CURD 语句执行时,语句处理器设置参数

// 有2个处理器会使用,分别是:

// 预编译处理器 PreparedStatementHandler

// 存储过程处理器 CallableStatementHandler

public void parameterize(Statement statement) {

//使用ParameterHandler对象来完成对Statement的设值

parameterHandler.setParameters(statement);

}

应用场景例如查询用户对象时,设置姓名,参数处理器结合类型处理器把 name 属性占位符进行赋值。

<select id="queryUSer">

select * from t_user where name = #{name}

</select>

  • 2、getParameterObject 获取参数,发生在结果集返回时,结果集处理器获取对象参数,值得注意的时,该方法只用于存储过程处理器 CallableStatementHandler 。

// 默认结果集处理器

public class DefaultResultSetHandler{

// 处理输出参数

public void handleOutputParameters(...) {

// 获取参数

final Object parameterObject =

parameterHandler.getParameterObject();

// 其它存储过程输出参数处理逻辑...

}

}

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

10、ResultSetHandler -- 结果集处理器

ResultSetHandler 是一个结果集处理器接口,它负责负责将 JDBC 返回的结果集 resultSet 对象转换为 List 类型的集合,是在语句处理器构建实例时被同时创建,底层做数据转换的工作会交给类型转换器 TypeHandler,它有1个默认实现类 DefaultResultSetHandler,该接口有3个方法,分别是:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • handleResultSets:负责结果集处理,完成映射返回结果对象

  • handleCursorResultSets :负责游标对象处理

  • handleOutputParameters :负责存储过程的输出参数处理

结果集处理器对于 JDBC 返回的结果集的基本处理,是先获取我们在映射器 Mapper 中指定 resultType 或 resultMap 映射关系,然后遍历解析结果集中的每一列数据,底层通过 MetaObject 对象做相关的反射处理。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

11、TypeHandler -- 类型转换器

TypeHandler 是一个类型转换器/处理器接口,它负责 Java 数据类型和 JDBC 数据类型之间的映射与转换,当对 Statement 对象设置参数时,由 JavaType 转换为 JdbcType,当对 Statement 返回结果集进行封装映射时,又会将 JdbcType 转换为 JavaType。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

一般,我们可以直接使用 Mybatis 内置的类型处理器,简单看了一下有 65+ 个,当然我们是可以根据业务需要自定义类型处理器的,以便处理复杂类型或非标类型。

具体做法为:

1、实现 org.apache.ibatis.type.TypeHandler 接口;

2、继承 org.apache.ibatis.type.BaseTypeHandler 类。

我们看到接口 TypeHandler 定义了四个方法:

public interface TypeHandler<T> {

// 设置参数

void setParameter(

PreparedStatement ps,

int i, T parameter,

JdbcType jdbcType);

// 根据列名获取转换结果

T getResult(ResultSet rs, String columnName);

// 根据列下标获取转换结果

T getResult(ResultSet rs, int columnIndex);

// 根据列下标获取【存储过程】的输出结果

T getResult(CallableStatement cs, int columnIndex);

}

其实,我之前在介绍 Mybatis 核心配置的时候,有大力介绍过类型处理器,没必要重复写(其实是懒),感兴趣的朋友可以直接看我们之前的文章 《Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解》中对类型处理器 TypeHandler 的介绍。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

12、MappedStatement -- 语句对象

MappedStatement 语句对象,就是我们在映射器 Mapper 中维护的每一条语句,例如 <select|update|delete|insert>,Mybatis 中通过语句构造器 XMLStatementBuilder 对每一个语句进行解析:

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

整个解析过程分为4步骤:

  • 1、配置解析器 XMLConfigBuilder 解析映射器:

// Configuration 配置解析器

public class XMLConfigBuilder{

// 解析映射器

private void mapperElement(){

// 创建映射器解析实例

XMLMapperBuilder mapperParser =

new XMLMapperBuilder(...);

// 开始解析

mapperParser.parse();

}

}

2、映射对象解析器 XMLMapperBuilder 解析语句

// 映射对象解析器

public class XMLMapperBuilder{

// 1、解析入口

public void parse() {

// 解析映射器文件

configurationElement(

parser.evalNode("/mapper"));

}

// 2、节点解析

private void configurationElement(XNode context) {

// 构建语句对象

buildStatementFromContext(

context.evalNodes(

"select|insert|update|delete"));

}

// 3、最终调用语句解析器

private void buildStatementFromContext(){

// 创建语句解析实例

XMLStatementBuilder statementParser =

new XMLStatementBuilder();

// 解析语句节点

statementParser.parseStatementNode();

}

}

3、语句解析器 XMLStatementBuilder 解析每一个节点

// 语句解析器

public class XMLStatementBuilder{

// 解析语句节点

public void parseStatementNode() {

// 通过语句辅助类构建语句对象

builderAssistant.addMappedStatement(...)

}

}

4、语句辅助类 MapperBuilderAssistant 添加进语句集合中

// 语句辅助类

public class MapperBuilderAssistant{

// 添加语句对象

public MappedStatement addMappedStatement(

// 最终添加到配置类的语句集合中

configuration.addMappedStatement(statement);

}

}

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

13、SqlSource -- SQL源

SqlSource 是一个 SQL 源接口,它会结合用户传递的参数对象 parameterObject,动态地生成 SQL 语句,并最终封装成 BoundSql 对象。SqlSource 接口有5个实现类,分别是:StaticSqlSource、DynamicSqlSource、RawSqlSource、ProviderSqlSource、VelocitySqlSource (这只是一个测试用例,而非真正模板 Sql 源实现类)。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

  • StaticSqlSource:静态 SQL 源实现类,所有的 SQL 源最终都会构建 StaticSqlSource 实例,该实现类会生成最终可执行的 SQL 语句供 statement 或 prepareStatement 使用。

  • RawSqlSource:原生 SQL 源实现类,解析构建含有 '#{}' 占位符的 SQL 语句或原生 SQL 语句,解析完最终会构建 StaticSqlSource 实例。

  • DynamicSqlSource:动态 SQL 源实现类,解析构建含有 '${}' 替换符的 SQL 语句或含有动态 SQL 的语句(例如 If/Where/Foreach等),解析完最终会构建 StaticSqlSource 实例。

  • ProviderSqlSource:注解方式的 SQL 源实现类,会根据 SQL 语句的内容分发给 RawSqlSource 或 DynamicSqlSource ,当然最终也会构建 StaticSqlSource 实例。

  • VelocitySqlSource:模板 SQL 源实现类,官方申明这只是一个测试用例,而非真正的模板 Sql 源实现类。

SqlSource 实例在配置类 Configuration 解析阶段就被创建,Mybatis 框架会依据3个维度的信息来选择构建哪种数据源实例:

  • 第一个维度:客户端的 SQL 配置方式:XML 方式或者注解方式。

  • 第二个维度:SQL 语句中是否使用动态 SQL ( if/where/foreach 等 )。

  • 第三个维度:SQL 语句中是否含有替换符 '${}' 或占位符 '#{}' 。

SqlSource 接口只有一个方法 getBoundSql ,就是创建 BoundSql 对象。

public interface SqlSource {

BoundSql getBoundSql(Object parameterObject);

}

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

14、BoundSql -- SQL语句

BoundSql 对象存储了动态生成的 SQL 语句以及相应的参数信息,BoundSql 对象是在执行器具体执行 CURD 时通过实际的 SqlSource 实例所构建。通过 BoundSql 能够获取到实际数据库执行的 SQL 语句,系统可根据 SQL 语句构建 Statement 或者 PrepareStatement 。

public class BoundSql {

//该字段中记录了SQL语句,该SQL语句中可能含有"?"占位符

private final String sql;

//SQL中的参数属性集合

private final List<ParameterMapping> parameterMappings;

//客户端执行SQL时传入的实际参数值

private final Object parameterObject;

//复制 DynamicContext.bindings 集合中的内容

private final Map<String, Object> additionalParameters;

//通过 additionalParameters 构建元参数对象

private final MetaObject metaParameters;

}

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

总结

本文整整2周才基本修整完善,顺着 Mybatis 的数据库核心执行流程,我们大致介绍了 Mybatis 中几个相对核心的 API,我们是一边构建核心架构功能设计图示,一边梳理 API 相关知识脉络,目前基本算是捋清。

其中比较苦恼的问题就是,对于文章内容范围尺度的把控,每篇文章我都希望讲得全面,讲得细致,这两个坚持就注定了文章的内容不可能全是干货,导致略懂的人只能选择跳跃式阅读,对于有营养的知识点需要挑挑拣拣,自我取舍;同时还会导致内容篇幅巨长,导致整体阅读耗时过长,不易快速吸收。

后续尝试改变一下写作方式,尽量输出干货、内容点到为止,对于需要巨细剖析的内容,我们单立文章解读。

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

以上是 【Java】Mybatis系列全解(六):Mybatis最硬核的API你知道几个? 的全部内容, 来源链接: utcz.com/a/109371.html

回到顶部