MyBatis源码分析_Executor组件及3个火枪手(6)

news2024/11/24 19:33:23

目录

1. 前提

2. Executor执行器

3. 总结

4. 三个火枪手

5. StatementHandler生成Statement

6. ParameterHandler 参数解析

7. BoundSql的数据结构

8. 总结


1. 前提

在Mybatis源码分析_事务管理器 (5)_chen_yao_kerr的博客-CSDN博客一文中,我们已经对模板设计模式进行了介绍。下面强调一下概念以及Executor组件是如何使用模板设计模式的。

模板模式(Template Pattern)

一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定实现;

2. Executor执行器

Executor是MyBaits核心接口之一,定义了数据库操作最基本的方法,SqlSession的功能都是基于它来实现的。

 BaseExecutor是一个抽象基类,实现了executor接口的大部分方法,主要提供了缓存管理和事务管理的能力,其他子类需要实现的抽象方法为:doUpdate,doQuery等方法

比如,关闭事务的close方法、query方法、commit提交方法、缓存key生成的 createCacheKey方法等,都是在这个BaseExecutor基类中重写的。

1. update 方法调用doUpdate方法,这个doUpdate方法是个抽象方法,延迟到子类进行实现;2. queryFromDatabase方法调用了doQuery 方法,而这个doQuery也是延迟到子类实现的。

这些都是典型的 模板设计模式。

SimpleExecutor :默认配置,使用PrepareStatement对象访问数据库,每次访问都要创建新的PrepareStatement对象,用完后关闭。

ReuseExecutor:可重用执行器,会将statement存入map中。使用预编译PrepareStatement对象访问数据库,访问时,会重用缓存map中的statement对象;

BatchExecutor:实现批量执行多条SQL语句的能力,基于jdbc的batch实现批处理

ClosedExecutor这个执行器我并没有接触过,没看见过谁使用过

CachingExecutor: 使用装饰器模式,对真正提供数据库查询的 Executor 增强了二级缓存
的 能 力 ; 二 级 缓 存 初 始 化 位 置 DefaultSqlSessionFactory.openSessionFromDataSource(ExecutorType,
TransactionIsolationLevel, boolean);

思考:SimpleExecutor 和 ReuseExecutor 区别是重用缓存statement对象,那么ReuseExecutor是如何设计的呢?

思考:既然有这么多的执行器,那么我们如何选择想要使用的执行器呢?

 其实,我们在mybatis的全局xml文件中,直接配置就可以了,如下图:

3. 总结

1. SqlSessionFactory负责生产SqlSession

2. SqlSession查询数据库,其实底层是依赖Executor组件完成的。而具体使用哪一个子类型的Executor可以通过全局xml进行配置。

3. Executor执行器,其实是一个指挥官。它在调度三个小弟StatementHandler、ParameterHandler、ResultSetHandler进行工作

4. 三个火枪手

此时,我们就引出来了支持Statement的三个火枪手了。分别是StatementHandler、ParameterHandler 和 ResultSetHandler。

StatementHandler:它的作用是使用数据库的Statement或PrepareStatement执行操作,启承上启下作用;

ParameterHandler:对预编译的SQL语句进行参数设置,SQL语句中的的占位符“?”都对应BoundSql.parameterMappings集合中的一个元素,在该对象中记录了对应的参数名称以及该参数的相关属性

ResultSetHandler:对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型;

他们的运行关系如下:

 StatementHandler、ParameterHandler、ResultSetHandler都只是接口。StatementHandler完成Mybatis最核心的工作,也是Executor实现的基础;功能包括:创建statement对象,为sql语句绑定参数,执行增、删、改、查等SQL语句、将结果映射集进行转化;

BaseStatementHandler:所有子类的抽象父类,定义了初始化statement的操作顺序,由子类实现具体的实例化不同的statement(模板模式);

RoutingStatementHandler:Excutor组件真正实例化的子类,使用静态代理模式,根据上下文决定创建哪个具体实体类;

SimpleStatmentHandler :使用statement对象访问数据库,无须参数化; PreparedStatmentHandler :使用预编译PrepareStatement对象访问数据库; CallableStatmentHandler :调用存储过程

5. StatementHandler生成Statement

首先,我们的执行器Executor需要依赖全局遍历Configuration的newStatementHandler 方法来创建具体的StatementHandler对象。见下图:

 而进入Configuration对象,我们发现它就是new了一个 RoutingStatementHandler对象

 而我们new出来的 RoutingStatementHandler 才是决定我们具体使用哪一个子类型的StatementHandler的核心。它负责根据我们的mybatis配置信息,生成不同的具体的StatmentHandler,这边是静态代理 + 简单工厂的改造版本,不好具体定义到底是哪个设计模式

而我们可以为每一个sql语句指定不同的StatementType类型。默认使用的是PREPARED,即预编译模式。这样就和上方的代码一一对应起来了。

那么,拿到了RoutingStatementHandler 以后能干嘛呢?

先看传统的JDBC访问数据库:

此处,我们以SimpleExecutor为例子,接着往下看:

 进入方法以后,我们发现,它和传统的JDBC几乎一模一样

a. 生成Connection对象

b. 生成Statement对象

c. 处理占位符以及参数设置,最终返回Statement

 继续跟进,看看它具体是如何生成Statement的:

动态代理技术,反射调用

进入基类 BaseStatementHandler。 模板设计模式的体现

最终,调到 PreparedStatementHandler, 生成预编译的PreparedStatement

其实,参数设置也是利用动态代理技术,进行参数设置的

6. ParameterHandler 参数解析

上面已经提到了参数解析了,它是封装到Statement对象中的。本节将对于参数解析进行更加详细的分析。

/*
 *    Copyright ${license.git.copyrightYears} the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.scripting.defaults;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;

import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeException;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class DefaultParameterHandler implements ParameterHandler {

//typeHandler注册中心
  private final TypeHandlerRegistry typeHandlerRegistry;
  //对应的sql节点的信息
  private final MappedStatement mappedStatement;
  //用户传入的参数
  private final Object parameterObject;
  //SQL语句信息,其中还包括占位符和参数名称信息
  private final BoundSql boundSql;
  private final Configuration configuration;

  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }

  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    //从boundSql中获取sql语句的占位符对应的参数信息
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    //遍历这个参数列表,把参数设置到PreparedStatement中
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);//获得入参的形参名字,javatype,jdbctype等信息
        if (parameterMapping.getMode() != ParameterMode.OUT) {//对于存储过程中的参数不处理
          Object value;//绑定的实参值
          String propertyName = parameterMapping.getProperty();//参数的名字
          // 获取对应的实参值
          if (boundSql.hasAdditionalParameter(propertyName)) { //是否为附加参数
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {//是否为空
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {//是否要进行指定的类型转换
            value = parameterObject;
          } else {//大部分情况走这段
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();//从parameterMapping中获取typeHandler对象
          JdbcType jdbcType = parameterMapping.getJdbcType();//获取参数对应的jdbcType
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
        	 //为statment中的占位符绑定参数
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

}
ParameterHandler相对而言比较简单,它就2个方法。一个是获取参数值,一个是设置参数值。获取参数值比较简单,下面我们把重点放在设置参数值上进行分析。 

在Mybatis源码分析_解析大流程梳理_补充 (4)_chen_yao_kerr的博客-CSDN博客一文中,我们重点说过SqlSource对象,它是负责封装mybatis中具体的SQL语句以及参数的。而通过SqlSource我们就可以获取到BoundSql对象,它就是负责存储SQL语句的。

 下面看代码,在我们执行查询的时候,我们是根据第一阶段封装的MappedStatement获取到BoundSql对象的。

 而MappedStatement对象内部又封装了SqlSource对象,我们是直接从SqlSource对象获取的BoundSql对象。具体获取过程如下:

Ok,有了BoundSql对象以后,我们就是执行上面的PreparedStament生成操作了。但是,生成PreparedStatement以后,我们是要进行预编译和参数绑定的。其实上面已经说过了,下面我再帖一遍代码:

 通过上图我们知道,参数绑定是通过StatementHandler对象的parameterize方法完成的。进入以后是放射调用,并且调用的是 RoutingStatementHandler 的 parameterize方法,而RoutingStatementHandler 此时内部持有Statement对象,准确的说是 PreparedStatementHandler的对象(为什么是PreparedStatementHandler对象,上面已经解释过)。

 因此,它必然进入PreparedStatementHandler的parameterize方法中。 而PreparedStatementHandler则调用了ParameterHandler的setParameters方法。如下图:

 因此,最终会进入ParameterHandler的setParameters方法

7. BoundSql的数据结构

 也就是说,只要获取到正确的BoundSql就可以搞定一切。而参数值,是我们在执行查询的时候传递进去的,Executor调度MappedStatement获取BoundSql传入进入的,而实际上是传递给MappedStatement内部的SqlSource对象的,由SqlSource对象来生成BoundSql对象的。

8. 总结

1. 在我们生成PreparedStatement的时候,我们会进行预处理并且设置参数

2. 设置参数的过程是,调用第一阶段生成的MappedStatement对象的SqlSource来生成的

3. 在我们实际调用的阶段,参数值会传递给MappedStatement对象的SqlSource,有它来负责生成BoundSql对象

最后,就是根据BoundSql对象进行数据库的查询。 下一章我们说返回值ResultSetHandler

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/752382.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网关微服务简单配置

导入一下网关的基本依赖 <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>com.alibaba.cloud<…

直播商城系统源码的威力:开启直播购物新时代

随着科技的不断进步和人们对互动性购物体验的追求&#xff0c;直播购物正成为电商行业的热门趋势。直播商城系统源码的威力在这一潮流中愈发显现&#xff0c;为商家和消费者提供了无限的机会和便利。 下面是一个简单的示例&#xff0c;展示了如何利用直播商城系统源码创建一个…

深度强化学习:深度解析 MADDPG

深度强化学习:深度解析 MADDPG 学习强化学习,码代码的能力必须要出众,要快速入门强化学习 搞清楚其中真正的原理,读源码是一个最简单的最直接的方式。最近创建了一系列该类型文章,希望对大家有多帮助。 另外,我会将所有的文章及所做的一些简单项目,放在 1.MADDPG 原理…

JS脚本 - 批量给所有指定标签追加Class属性

JS脚本 - 批量给所有指定标签追加Class属性 前言一. 脚本二. 测试运行 前言 公司里我们有个应用引入了UBT埋点&#xff0c;记录了页面上所有的点击操作以及对应的点击按钮。但是我们看下来发现&#xff0c;我们需要给每个按钮加一个唯一标识做区分&#xff0c;并且这个ID是给U…

选读SQL经典实例笔记07_日期处理(下)

1. 一个季度的开始日期和结束日期 1.1. 以yyyyq格式&#xff08;前面4位是年份&#xff0c;最后1位是季度序号&#xff09;给出了年份和季度序号 1.2. DB2 1.2.1. sql select (q_end-2 month) q_start,(q_end1 month)-1 day q_endfrom (select date(substr(cast(yrq as c…

Linux系统编程(信号处理 sigacation函数和sigqueue函数 )

文章目录 前言一、sigaction二、sigqueue函数三、代码示例总结 前言 本篇文章我们来介绍一下sigacation函数和sigqueue函数。 一、sigaction sigaction 是一个用于设置和检查信号处理程序的函数。它允许我们指定信号的处理方式&#xff0c;包括指定一个函数作为信号处理程序…

AsyncImage, BackgroundMaterials, TextSelection, ButtonStyles 的使用

1. AsyncImage 异步加载图片 1.1 实现 /*case empty -> No image is loaded.case success(Image) -> An image succesfully loaded.case failure(Error) -> An image failed to load with an error.*/ /// iOS 15 开始的 API 新特性示例 /// 异步加载图片 struct As…

Ae 效果:CC Plastic

风格化/CC Plastic Stylize/CC Plastic CC Plastic&#xff08;CC 塑料&#xff09;效果用于创建具有塑料质感的图像或视频效果&#xff0c;它模拟了塑料材质的外观特性&#xff0c;包括光照反射、表面凹凸以及光泽效果等。 ◆ ◆ ◆ 效果属性说明 Surface Bump 表面凹凸 通过…

IoT 场景下 TDengine 与老牌时序数据库怎么选?看看这份TSBS报告

上周一&#xff0c;TDengine 正式发布了 IoT 场景下基于 TSBS 的时序数据库&#xff08;Time Series Database&#xff0c;TSDB&#xff09;性能基准测试报告。该报告模拟虚拟货运公司车队中一组卡车的时序数据&#xff0c;预设了五种卡车规模场景&#xff0c;在相同的 AWS 云环…

[Lesson 01] TiDB数据库架构概述

目录 一 章节目标 二 TiDB 体系结构 1 TiDB Server 2.1 TiKV 2.2 TiFlash 3 PD 参考 一 章节目标 理解TiDB数据库整体架构了解TiDB Server TiKV tiFlash 和 PD的主要功能 二 TiDB 体系结构 了解这些体系结构是如何实现TiDB的核心功能的 1 TiDB Server TiDB Serve…

记录--你知道Vue中的Scoped css原理么?

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 追忆Scoped 偶然想起了一次面试&#xff0c;二面整体都聊完了&#xff0c;该做的算法题都做出来了&#xff0c;该背的八股文也背的差不多了&#xff0c;面试官频频点头&#xff0c;似乎对我的基础和项…

云计算的学习(四)

四、云计算中的存储基础知识 1.云计算虚拟化中的存储架构 ①虚拟化存储 在虚拟化存储架构中&#xff0c;最底层为物理磁盘。 底层的硬件组成存储池&#xff0c;存储池分为NAS存储和SAN存储&#xff1b;NAS存储需要文件系统&#xff1b;SAN存储需要对存储池进行逻辑划分产生逻…

【VSCode | 使用技巧集锦】中文插件突然失效、配置单个工程(工作区)编码

目录 ✨技巧一&#xff1a;中文插件失效的解决办法✨技巧二&#xff1a;配置单个工程(工作区)编码 ✨技巧一&#xff1a;中文插件失效的解决办法 问题描述&#xff1a;VSCode之前安装了中文插件&#xff0c;可以正常汉化&#xff0c;用了一段时间都没问题&#xff0c;今天打开v…

springboot+webscoket通信功能

1. 背景 项目上需要对某个页面的设计功能&#xff08;低代码&#xff09;进行最简单的多人协同&#xff0c;有以下需求点&#xff1a; &#xff08;1&#xff09;第一个进入该设计页面的人给编辑权限&#xff0c;后进入的所有人给在线&#xff08;可申请编辑&#xff09;权限 …

使用MQTTX和前端vue进行通讯

需求&#xff1a;根据后端给的接口&#xff0c;前端实现消息订阅和消息加密连接操作&#xff0c;不走后端直接和硬件设备进行操作 1.下载mqttx 官网链接&#xff1a;MQTTX: Your All-in-one MQTT Client Toolbox 根据自己电脑选择不同的操作系统&#xff0c;默认下载后是英文…

金鸣表格识别中何时应勾选“手写”选项?

在金鸣表格文字识别系统的表格识别模块中&#xff0c;有个“手写”的复选框可供用户选择性使用。这里的“手写”是手写识别的简称&#xff0c;设置此项的目的是为了让用户更准确地识别手写的表格图片中的文字。为何要单独设置这个选项而不是由程序全自动地进行处理呢&#xff1…

【GitOps系列】K8s极简实战

文章目录 示例应用介绍部署应用到k8s 如何使用命名空间隔离团队及应用环境&#xff1f;如何为业务选择最适合的工作负载类型&#xff1f;如何解决服务发现问题&#xff1f;如何迁移应用配置&#xff1f;如何将集群的业务服务暴露外网访问&#xff1f;如何保障业务资源需求和自动…

JavaWeb(3)——HTML、CSS、JS 快速入门

一、JavaScript 运算符 • 赋值运算符&#xff08; &#xff09; 赋值运算符执行过程&#xff1f; 将等号右边的值赋予给左边, 要求左边必须是一个容器 出现是为了简化代码, 比如让 let age 18 &#xff0c;age 加 2 怎么写呢 let age 18age 2console.log(age)age * 2con…

html+JavaScript实现一个好看的颜色码查询器,支持查询、转换、颜色选择器和颜色码对照表

前言 相信大家平时工作的时候应该会经常用到颜色码吧&#xff0c;比如说想找个好看的颜色&#xff0c;或者有个颜色码但是不知道这个码是什么颜色的&#xff0c;这个时候我们就可以用颜色码对照表或者颜色码查询来查看了。 当然也可以用截图软件或者取色器或者PS来查看&#…

如何有效检测、识别和管理 Terraform 配置漂移?

作者&#xff5c;Krishnadutt Panchagnula 翻译&#xff5c;Seal软件 链接&#xff5c;https://betterprogramming.pub/detecting-identifying-and-managing-terraform-state-drift-997366a74537 在理想的 IaC 世界中&#xff0c;我们所有的基础设施实现和更新都是通过将更新的…