Mybatis5 StatementHandler ,ParameterHandler

一丶概述

前面我们总结了SqlSession--->CachingExecutor--->BaseExector---->Excutor子类 doQuery,doUpdate的执行流程,mybatis操作数据库总归是基于JDBC的,再和数据库打交道之前必定需要构建statement并且把参数映射到sql语句上,然后请求数据库,然后从Result中拿到数据映射到java 对象上。Excutor执行这些操作其实是调用下面三大对象完成的:

  • StatementHandler:作用是使用数据库的Statement(默认使用PreparedStatement)执行操作,它是四大对象的核心,起到承上启下的作用,许多重要的插件都是通过拦截它来实现的。
  • ParameterHandler:是用来处理SQL参数的。
  • ResultSetHandler:是进行数据集(ResultSet)的封装返回处理的

执行的流程如下

二丶Excutor是如何得到应该使用的StamentHandler的

1.Exectuor准备StatmentHandler并且调用的逻辑

  • SimpleExecutor

当真正需要请求数据库时,从Configuration大管家中创造一个新的StatementHandler(ReuseExecutor 使用了Map缓存sql和对应的StatmentHanler 并不一定会去new 一个StatementHandler)

接着准备调用prepareStatement

主要是获取连接(这个获取的连接被mybatis进行了动态代理,提供了打印日志的功能)然后调用StamentHandler的prepare方法创建一个Statment并且调用StamentHandler的parameterize 将参数映射到sql中,后面就是执行器调用Statment的 query or update 方法操作数据

  • ReuseExecutor

    大体逻辑和SimpleExector相同,准备Statment 的逻辑不一样

    从缓存map中根据sql 拿Statment 这是ReuseExecutor 为什么叫 ReuseExecutor的原因,但是不是拿到Statment即可,还要确保statement的数据库连接没有关闭了(这里个人觉得有点瑕疵,既然数据库连接关闭了,而且这个map中的Statement也不会被继续使用了,为什么不将这个Statement也关闭)
  • BatchExecutor

    对应StatmentHandler的操作和SimpleExecutor一样,只是BatchExecutor的doUpdate方法会调用StatmentHandler的batch方法,而且会使用list存储每一个Statement和对应的BachResult,调用doFlushStatements会进行批量提交

三丶StatementHandler和Configuration创建StatementHandler

1.StatemenHanlder接口

定义了StatementHandler的基本功能

//准备语句  子类可以实现返回不同的Statement子类
Statement prepare(Connection connection)
throws SQLException;
//参数化
void parameterize(Statement statement)
throws SQLException;
//批处理
void batch(Statement statement)
throws SQLException;
//update
int update(Statement statement)
throws SQLException;
//select-->结果给ResultHandler
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
//得到绑定sql
BoundSql getBoundSql();
//得到参数处理器
ParameterHandler getParameterHandler();

2.BaseStatementHandler

模板方法设计模式,提取公共的操作到父类,具体不同的地方抽成抽象方法交由子类实现

  • prepare

    • 设置超时时长 setStatementTimeout

      优先选择mapperstatement中的超时时长(对xml TimeOut指定的内容)后选择Configuration中指定的超时时长,根本是调用 stmt.setQueryTimeout

    • 设置读取条数 setFetchSize

    获取mappedStatement中设置的FetchSize设置(起始我们如果数据库数据过多可以设置最多获取多少条,避免oom)根本是调用stmt.setFetchSize

3.RoutingStatementHandler

主要是适配多个StatmentHandler的实现,有点装饰器,有点工厂,又有点适配器的意思

后续具体方法的实现都是调用delegate对应的方法,相当于RoutingStatementHandler 只是做了一个根据MappedStatement中的StatementType配置创建不同的StatmentHandler

4.PrepareStatementHandler

默认的StatementHandler

  • instantiateStatement

  • 为什么新增更新删除可以返回影响的行数

    本质是调用了PreparedStatement.getUpdateCount 返回

  • parameterize 调用parameterHandler 进行参数映射

  • query调用 resultSetHandler的handleResultSets进行参数映射

5.SimpleStatementHandler

  • instantiateStatement
connection.createStatement生成Statement

6.CallableStatementHandler

  • instantiateStatement

    connection.prepareCall 生成Statement
  • update 和 query方法会调用resultSetHandler 处理存储过程的输出参数

  • parameterize相比PrepareStatementHandler多了将存储过程的输出参数注册到 CallableStatement

7.Configuration创建StatementHandler

默认创建的是RoutingStatementHandler 或许我们可以把 RoutingStatementHandler 构造方法内,根据StatementType船舰不同实现的逻辑移到这里也是一样的

interceptorChain.pluginAll 使用责任链和动态代理对每一个StatementHandler进行动态代理

四丶ParameterHandler

  • ParameterHandler接口定义了如何获取参数,和如何如何设置参数的两个方法
  • DefaultParameterHandler 是ParameterHandler接口的唯一实现

1.DefaultParameterHandler 是如何设置参数的

//设置参数
@Override
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//1.获取sql语句的参数,ParameterMapping里面包含参数的名称类型等详细信息,还包括类型处理器
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
//2.遍历依次处理
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
//3.OUT类型参数不处理
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
//4.获取参数名称
String propertyName = parameterMapping.getProperty();
//5.如果propertyName是动态参数,就会从动态参数中取值。(当使用<foreach>的时候,MyBatis会自动生成额外的动态参数)
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
//6.如果参数是null,不管属性名是什么,都会返回null。
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
//7.判断类型处理器是否有参数类型,如果参数是一个简单类型,或者是一个注册了typeHandler的对象类型,就会直接使用该参数作为返回值,和属性名无关。
value = parameterObject;
} else {
//8.这种情况下是复杂对象或者Map类型,通过反射方便的取值。通过MetaObject操作
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
//9.获取对应的数据库类型
JdbcType jdbcType = parameterMapping.getJdbcType();
//空类型
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
//10.对PreparedStatement的占位符设置值(类型处理器可以给PreparedStatement设值)
try {
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);
}
}
}
}
}

Mybatis源码5 StatementHandler ,ParameterHandler的更多相关文章

  1. Mybatis源码分析-StatementHandler

    承接前文Mybatis源码分析-BaseExecutor,本文则对通过StatementHandler接口完成数据库的CRUD操作作简单的分析 StatementHandler#接口列表 //获取St ...

  2. mybatis源码之StatementHandler

    /** * @author Clinton Begin */ public interface StatementHandler { Statement prepare(Connection conn ...

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

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

  4. MyBatis源码分析-SQL语句执行的完整流程

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

  5. MyBatis源码分析(2)—— Plugin原理

    @(MyBatis)[Plugin] MyBatis源码分析--Plugin原理 Plugin原理 Plugin的实现采用了Java的动态代理,应用了责任链设计模式 InterceptorChain ...

  6. MyBatis 源码分析 - 插件机制

    1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...

  7. MyBatis 源码分析 - SQL 的执行过程

    * 本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析 ...

  8. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  9. Mybatis源码分析

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

  10. Mybatis源码正确打开方式

    精心挑选要阅读的源码项目: 饮水思源——官方文档,先看文档再看源码: 下载源码,安装到本地,保证能编译运行: 从宏观到微观,从整体到细节: 找到入口,抓主放次,梳理核心流程: 源码调试,找到核心数据结 ...

随机推荐

  1. Python中的可迭代对象和迭代器

    1.可迭代对象 1.1.可迭代对象概念 可迭代对象,最直观的感觉就是可以使用for来循环迭代每一个元素.例如Python内置的类型:str.list.tuple.dict等类型的对象,都是可迭代对象. ...

  2. 软件开发人员 Kubernetes 入门指南|Part 1

    Kubernetes 是一个用于部署和管理容器的编排系统.使用 Kubernetes,用户可以通过自动执行管理任务(例如在跨节点间扩展容器并在容器停止时重新启动任务),在不同环境中可靠地运行容器. K ...

  3. 挑战程序设计竞赛 2.2 poj 3040 Allowance 贪心

    https://vjudge.csgrandeur.cn/problem/POJ-3040 /* 作为创纪录的牛奶产量的奖励,约翰决定每周给贝西一小笔零用钱.FJ拥有一组N(1 <= N < ...

  4. k8s-单节点升级为集群(高可用)

    单master节点升级为高可用集群 对于生产环境来说,单节点master风险太大了. 非常有必要做一个高可用的集群,这里的高可用主要是针对控制面板来说的,比如 kube-apiserver.etcd. ...

  5. P5445 [APIO2019] 路灯 题解

    题目链接 题目描述 给你一个 01 串,有 \(q\) 个时刻,每个时刻要么把一位取反,要么问你在过去的所有时刻中有多少个时刻 \(a\) 和 \(b-1\) 之间都为 1. 题目分析 观察题目,我们 ...

  6. 关于PaddleOCR识别时中文路径导致报错/没输出结果

    此处只做学习PaddleOCR时遇到的一些坑 一.Python版本与PaddleOCR兼容性问题 如果你在Python11的环境下安装PaddlePaddle,使用 paddleocr --image ...

  7. 使用visualvm远程监控JVM

    参考:http://blog.sina.com.cn/s/blog_4e90b3ba0100muco.html 1.首先要修改JDK中JMX服务的配置文件,以获得相应的权限: 在jdk-1.6.0_2 ...

  8. OpenGL 着色器详解

    1. GLSL语言 glsl语言是用来编写着色器的,通过一段一段包含main函数的程序片段,告诉渲染引擎怎么去渲染内容. glsl语言的语法有点类似c语言风格,只是增加了一些特有的关键字来修饰变量,下 ...

  9. SQL改写案例4(开窗函数取中位数案例)

    周总找我问个报表SQL实现逻辑的案例,废话不说给他看看. 原SQL: SELECT d.tname 姓名, d.spname 岗位, d.sum_cnt 报单单量, d.min_cnt 放款单量, d ...

  10. SNN_LIF模型

    LIF模型 Leaky integrity-Fire(LIF)模型 输入信号直接影响神经元的状态,即神经元膜电位,只有当膜电位上升到阈值的时候,才会产生输出信号. 膜电位:细胞膜两侧的电位差.只有当膜 ...