【手撕MyBatis源码】JDBC处理器StatementHandler

news2025/1/11 6:30:49

文章目录

  • 回顾MyBatis的执行流程
  • StatementHandler定义与结构
  • PreparedStatementHandler执行流程
  • 参数处理
    • 参数转换
    • 参数映射
    • 参数赋值
  • 结果集处理

回顾MyBatis的执行流程

MyBatis是一个基于JDBC的Dao框架,但前面我们提到的会话、执行器完全没有提到JDBC,原因是MyBatis把所有跟JDBC相关的操作全部都放到了StatementHandler中。

一个SQL请求会经过会话,然后是执行器,最由StatementHandler执行JDBC最终到达数据库。其关系如下图:

在这里插入图片描述

注意:我们前面说过在一次会话中SqlSession、Executer、StatementHandler的比例是1:1:N(这里的N取决于通过会话调用了多少次Sql)。即使在可重用执行器的情况下这个比例也是成立的,因为我们只要执行一次SQL就会对应一个StatementHandler。但是如果走了缓存就会影响,因为我们压根就不会走数据库操作!

StatementHandler定义与结构

StatementHandler定义:

JDBC处理器,基于JDBC构建JDBC Statement,并设置参数,然后执行Sql。每调用会话当中一次SQL,都会有与之相对应的且唯一的Statement实例

StatementHandler结构

在这里插入图片描述

​ StatementHandler接口定义了JDBC操作的相关方法如下:

// 基于JDBC 声明Statement
Statement prepare(Connection connection, Integer transactionTimeout)
    throws SQLException;
// 为Statement 设置方法参数
void parameterize(Statement statement)
    throws SQLException;
// 添加批处理(并非执行)
void batch(Statement statement)
    throws SQLException;
// 执行update操作
int update(Statement statement)
    throws SQLException;
// 执行query操作
<E> List<E> query(Statement statement, ResultHandler resultHandler)
    throws SQLException;

StatementHandler 有三个子类:

  • SimpleStatementHandler
  • PreparedStatementHandler
  • CallableStatementHandler

分别对应JDBC中的Statement、PreparedStatement、CallableStatement。

与三类执行器的频繁使用不同,这里的三种我们绝大多数情况下使用的都是PreparedStatementHandler。

PreparedStatement 有以下几个主要特点:

  1. 预编译 SQL 语句。PreparedStatement 对象代表一条预编译过的 SQL 语句。SQL 语句在被发送到数据库执行之前,先被编译。这样可以提高语句的执行效率。
  2. 实现参数设置。PreparedStatement 允许你通过 setXXX() 方法设置参数,然后执行这条 SQL 语句。这样可以防止 SQL 注入,提高安全性。
  3. 重复使用语句。PreparedStatement 可以安全地被重复执行。它会优化数据库资源,并有效地执行语句。
  4. 批量更新。PreparedStatement 支持通过 addBatch() 方法添加多条 SQL 命令,然后通过 executeBatch() 方法批量执行。这大大提高了更新/插入/删除记录的速度。

下面是一个 PreparedStatement 的示例:

String sql = "INSERT INTO EMP(ID, NAME) VALUES (?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setInt(1, 101);    // 为第一个参数设置 int 类型的值 101  
pstmt.setString(2, "John"); // 为第二个参数设置 String 类型的值 "John"
pstmt.addBatch();

pstmt.setInt(1, 102);   
pstmt.setString(2, "Tom");
pstmt.addBatch();  

pstmt.executeBatch(); // 批量执行命令

这个示例中:

  1. 我们首先预编译一条 SQL 语句,这个语句使用了两个参数。
  2. 然后我们为这两个参数设置具体的值,添加到批处理中。
  3. 最后通过 executeBatch() 方法执行批处理,插入两条记录。

所以,总结来说,PreparedStatement 的主要特点有:

  1. 预编译 SQL 语句,提高执行效率。
  2. 实现参数设置,防止 SQL 注入。
  3. 可以安全重复执行,优化数据库资源。
  4. 支持批量更新,大大提高更新/插入/删除记录的速度。

PreparedStatement 是 JDBC 访问数据库的重要实现,它通过预编译和参数设置等特性,使得数据库访问更加高效和安全。

这三种StatementHandler有如下的共性:

  • 添加批处理参数
  • 设置返回的行数

在这里插入图片描述

所以这个时候我们会抽象出一个BaseStatementHandler来处理这些共性

PreparedStatementHandler执行流程

在这里插入图片描述

我们在上一篇文章中说过,查询数据的本质方法是doQuery(),他被query方法包了一层来处理缓存相关的业务。

在这里插入图片描述

查询数据库的执行过程分为三个阶段:

  • 预处理:这里预处理不仅仅是通过Connection创建Statement,还包括设置参数。
    SimpleExecuter类中:
    在这里插入图片描述

  • 执行:包含执行SQL获取结果集和处理结果映射(后面会说)两部分。
    在这里插入图片描述
    在这里插入图片描述

    我们可以看到获取结果集后,使用ResultSetWrapper包装了一下。

  • 关闭:直接关闭Statement。

参数处理和结果集封装,涉及数据库字段和JavaBean之间的相互映射,相对复杂。所以分别使用ParameterHandler与ResultSetHandler两个专门的组件实现。接下来就一起了解一下参数处理与结果集封装的处理流程。

在 MyBatis 中,PreparedStatementHandler 的执行流程如下:

  1. 调用 Connection.prepareStatement(sql) 创建一个 PreparedStatement 对象。sql 是映射文件中配置的 SQL 语句,可能包含参数 #{property}。
  2. 对SQL语句中的参数进行解析和值设置。
    • 解析 #{property} 获得参数名称和参数值。参数值来源于传入的 Java 对象的属性值。
    • 根据参数类型调用 PreparedStatement 的 setXxx() 方法设置参数值。如 setInt() 设置整数,setString() 设置字符串等。
  3. 调用 PreparedStatement 的 executeUpdate() 执行插入、更新、删除操作,或 executeQuery() 执行查询操作。
  4. 如果执行查询操作,还需要调用 ResultSetHandler 处理结果集。
    • DefaultResultSetHandler 会将 ResultSet 封装为 List 并返回。
    • BeanResultSetHandler 会将 ResultSet 封装为 JavaBean 对象并返回。
  5. 调用 PreparedStatement.close() 和 ResultSet.close() 释放资源。

参数处理

对应下面这一部分:
在这里插入图片描述

参数处理即将Java Bean转换成数据类型。总共要经历过三个步骤:

  • 参数转换
  • 参数映射
  • 参数赋值

参数转换

在MyBatis中参数转换逻辑均在ParamNameResolver中实现

参数转换就是将JAVA 方法中的普通参数,封装转换成Map,以便map中的key和sql的参数引用相对应。

@Select({"select * from users where name=#{name} or age=#{user.age}"})
@Options
User selectByNameOrAge(@Param("name") String name, @Param("user") User user);
  • 单个参数的情况下且没有设置@param注解会直接转换,忽略SQL中的引用名称。
  • 多个参数情况:优先采用@Param中设置的名称,如果没有则用参数序号代替 即"param1、param2…"(作为key)
    • 在JDK1.8之后支持直接通过反射获取变量名称作为key,前提javac编译时设置了 -parameters 编译参数,如果没有设置则转换成arg0,arg1

例如:
在这里插入图片描述

在这里插入图片描述
过程如下:

在这里插入图片描述

参数映射

映射是指Map中的key如何与SQL中绑定的参数相对应。以下这几种情况

  • 单个原始类型:直接映射,勿略SQL中引用名称
  • Map类型:基于Map key映射
  • Object:基于属性名称映射,支持嵌套对象属性访问

在Object类型中,支持通过“.”方式映射属中的属性。如:user.age

@Select({"select * from users where name=#{name} or age=#{param2.age}"})
@Options
User selectByNameOrAge(@Param("name") String name, @Param("user") User user);

参数赋值

通过TypeHandler 为PrepareStatement设置值,通常情况下一般的数据类型MyBatis都有与之相对应的TypeHandler

在这里插入图片描述

结果集处理

在这里插入图片描述

指读取ResultSet数据,并将每一行转换成相对应的对象。用户可在转换的过程当中可以通过ResultContext来控制是否要继续转换。转换后的对象都会暂存在ResultHandler中最后统一封装成list返回给调用方

在这里插入图片描述

结果集转换中99%的逻辑DefaultResultSetHandler 中实现。整个流程可大致分为以下阶段:

  • 读取结果集

  • 遍历结果集当中的行
    在这里插入图片描述

    • 其本质就是调用resultSet.next()遍历行, 并且基于上下文判断是否要继续往下读取,这也就回应了我们前面所说的ResultContext的作用
      在这里插入图片描述
    • 暂存结果对象就是把我们解析好了的对象放在ResultHandler中
      在这里插入图片描述
  • 创建对象:有四种情况:

  1. 使用构造方法创建对象。MyBatis 会调用无参数的构造方法来创建 bean 实例。
    <resultMap id="UserResultMap" type="User">
      <constructor>
        <idArg column="id" />
      </constructor>
    </resultMap>
    
  2. 使用工厂方法创建对象。MyBatis 会调用静态工厂方法来创建 bean 实例。
    <resultMap id="UserResultMap" type="User">
      <factory>
        <constructor>
          <idArg column="id" />
        </constructor>
      </factory>  
    </resultMap>
    
  3. 映射到既有的对象。MyBatis 会设置从结果集中获取的字段值到 bean 实例的属性上。
    <resultMap id="UserResultMap" type="User">
      <id property="id" column="id" />
      <result property="username" column="username"/>
    </resultMap>
    
  4. 使用映射器接口实现映射。映射器接口是一个简单的接口,只包含获取和设置bean属性值的方法。MyBatis 会创建该接口的一个实现,并使用它来设置结果集的值。
    public interface UserMapper {
      void setId(Integer id);
      void setUsername(String username);
    }
    
    <resultMap id="UserResultMap" type="UserMapper">
      <id property="setId" column="id" />
      <result property="setUsername" column="username"/>
    </resultMap> 
    
  • 填充属性
    在这里插入图片描述

    • 自动映射
    • 手动映射:基于ResultMapping 获取属性的值

映射方面会在MyBatis映射体系一文中进行说明

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

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

相关文章

python实现科学计算与自定义手绘风

目标&#xff1a; 1.运用科学计算库进行矩阵分析和数值运算&#xff1b; 2.掌握numpy库的使用。 要点&#xff1a;这是一个使用numpy和PIL库提取图像特征形成手绘效果的实例。 使用PIL库获取了图像的轮廓&#xff0c;虽然提取了轮廓,但这个轮廓缺少立体感&#xff0c;视觉效果不…

STL简介与类模板,函数模板的联系,迭代器的引入与初步简单认识

什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 STL的版本 原始版本&#xff1a;Alexander Stepanov、Meng Lee 在惠普实验室完成的原…

关于宝塔部署jar包和war包

文章目录 前言一、jar包部署二、war包部署1.maven如果打包不了使用命令打包2.安装Tomcat进行访问是否成功2.进入Tomcat目录进行配置war包 一、项目访问方法 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、jar包部署 1.其实jar包没什么讲的&…

开源云原生数仓引擎ByConity 存储计算分离架构和优势

供稿 | ByConity技术团队 出品 | CSDN 云计算 ByConity是一款字节跳动开源的云原生数仓引擎。它的一个重要优势是采用存储计算分离的架构&#xff0c;实现了读写分离和弹性扩缩容。这种架构确保读操作和写操作不会相互影响&#xff0c;使得计算资源和存储资源解耦&#xff0c;…

SpringBoot 结合 MyBatis-plus 进行逻辑删除

一 、逻辑删除的概念 逻辑删除不会在数据库中删除数据&#xff0c;只是通过一个字段用来标识被删除的记录&#xff0c;数据仍然保存在数据库中。在实际的工作当中&#xff0c;因为数据非常重要&#xff0c;为了防止因用户误操作删除数据后无法恢复的问题&#xff0c;我们通常不…

在 Spring Boot 中使用 Spring Cloud Kubernetes 部署应用到 Kubernetes

在 Spring Boot 中使用 Spring Cloud Kubernetes 部署应用到 Kubernetes Kubernetes 是目前最流行的容器编排平台之一&#xff0c;提供了一种灵活的方式来管理容器化应用程序。Spring Cloud Kubernetes 是一个基于 Spring Cloud 的项目&#xff0c;它提供了一种简单的方式来将…

探索可视化大屏:引领信息时代的视觉革命

可视化大屏是一种利用先进的数据可视化技术和交互技术&#xff0c;将大量的数据和信息以直观、易于理解的方式展示在大屏幕上的解决方案。可视化大屏通常由高分辨率的显示屏、强大的计算和处理设备以及专业的可视化软件组成&#xff0c;它通过图表、图形、动画等可视化元素&…

day08 Spring MVC

spring MVC相当于Servlet mvc解释:模型,视图,控制器 **使用该思想的作用:**减少耦合性,提高可维护性 Spring MVC前端控制器 方式1 1.在web.xml中配置前端控制器方式2 ​ 要是用前端控制器,必须在web.xml中配置DidpatcherServlet类 <!--前端控制器--> <servlet&g…

面试经验小结

1、为什么C有重载而C语言没有&#xff1f; C的编译过程中&#xff0c;将函数名后面的数据类型也加入到了编译阶段。 2、用异或完成两个数的数值交换。 x^y&#xff1b; y^x&#xff1b; x^y&#xff1b; 3、数组指针与指针数组&#xff1b;函数指针与指针函数 4、segment …

jdk15至17——sealed密封关键字

sealed关键字是从jdk15开始预览&#xff0c;直到jdk17成为正式版&#xff0c;可以对继承父类和实现接口进行更加细粒度的限制&#xff0c;之前的限制也只有final用于禁止继承&#xff0c;默认包权限限制在同一个包内&#xff0c;sealed密封类/接口可以明确指定哪些类可以进行继…

C# 调用Matlab打包的 DLL文件(傻瓜式操作)

1、准备Matlab代码 2. 打包 在matlab命令行窗口输入deploytool,打开MATLAB Complier,选择Library Compiler 在TYPE中选择.NET Assembly;在EXPORTED FUNCTIONS中选择要打包的文件&#xff1b;可以选择为自己打包的文件自定义NameSpace名称&#xff0c;本例中将NameSpace定义为…

Google Earth Engine(GEE):大数据林业应用

近年来遥感技术得到了突飞猛进的发展&#xff0c;航天、航空、临近空间等多遥感平台不断增加&#xff0c;数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量猛增&#xff0c;遥感数据已经越来越具有大数据特征。遥感大数据的出现为相关研究提供了前所未有的机遇&#xf…

AcrelCloud-9500电瓶车充电桩收费平台 在苏州市某拆迁小区的应用

摘 要&#xff1a;全国各地电瓶车充电引发火灾的事故时有发生&#xff0c;对人民群众的生命财产安全造成了很大的威胁&#xff0c;本文介绍的苏州市某拆迁小区&#xff0c;是海虞镇为创建市级消防安全“331”示范社区选定的社区&#xff0c;该社区采用4G通信方式与服务器进行通…

一篇文章带你看懂5G网络(接入网+承载网+核心网)

通过这张网络简图帮助大家认识一下全网的网络架构&#xff0c;通过对全网架构的了解&#xff0c;将方便您对后面每一块网络细节的理解。 这张图分为左右两部分&#xff0c;右边为无线侧网络架构&#xff0c;左边为固定侧网络架构。 无线侧&#xff1a;手机或者集团客户通过基站…

mysql安装8.**版本

1. 下载MySQL 8.0.22 源码包: wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.22.tar.gz https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.22.tar.gz 2. 解压源码包: tar -zxvf mysql-8.0.22.tar.gz -C /usr/local 3. 创建用于编译的构建目录: …

LDO基本知识

本文引用TI文档 压降 压降电压 VDO 是指为实现正常稳压&#xff0c;输入电压 VIN 必须高出 所需输出电压 VOUT(nom) 的最小压差。 如果 VIN 低于此值&#xff0c;线性稳压器将以压降状态工作&#xff0c;不再调 节所需的输出电压。在这种情况下&#xff0c;输出电压 VOUT(drop…

【Docker】进入docker容器

进入已经在运行的docker容器 1、查看所有在运行的docker容器 docker ps2、进入指定的docker容器 docker attach 容器CONTAINERID docker attach 8ad850b602ff查看log docker logs -f 8ad850b602ff

普通本科机械设计毕业生,如何零基础转行数据分析?

作为本科生转行数据分析还是比较容易的&#xff0c;我们数据分析班里本科生占了一大半&#xff0c;专业也都是非计算机相关的&#xff0c;有机械、机电、会计、金融等专业的应届生&#xff0c;基本对于计算机方面接触都不算多&#xff0c;专业方面肯定都是零基础&#xff0c;会…

【案例教程】R语言在生态环境领域中的实践技术应用

R语言作为新兴的统计软件&#xff0c;以开源、自由、免费等特点风靡全球。生态环境领域研究内容广泛&#xff0c;数据常多样而复杂。利用R语言进行多元统计分析&#xff0c;从复杂的现象中发现规律、探索机制正是R的优势。为此&#xff0c;本课程以鱼类、昆虫、水文、地形等多样…

Scrapy CrawlSpider介绍和使用

一、介绍CrawlSpider CrawlSpider其实是Spider的一个子类&#xff0c;除了继承到Spider的特性和功能外&#xff0c;还派生除了其自己独有的更加强大的特性和功能。其中最显著的功能就是”LinkExtractors链接提取器“。Spider是所有爬虫的基类&#xff0c;其设计原则只是为了爬取…