StatementHandler 是四大组件中最重要的一个对象,负责操作 Statement 对象与数据库进行交流,在工作时还会使用 ParameterHandler 和 ResultSetHandler 对参数进行映射,对结果进行实体类的绑定

MyBatis 四大组件之StatementHandler

我们在搭建原生JDBC的时候,会有这样一行代码

Statement stmt = conn.createStatement(); //也可以使用PreparedStatement来做

这行代码创建的 Statement 对象或者是 PreparedStatement 对象就是由StatementHandler进行管理的。

StatementHandler 的基本构成

来看一下StatementHandler中的主要方法:

  • prepare: 用于创建一个具体的 Statement 对象的实现类或者是 Statement 对象
  • parametersize: 用于初始化 Statement 对象以及对sql的占位符进行赋值
  • update: 用于通知 Statement 对象将 insert、update、delete 操作推送到数据库
  • query: 用于通知 Statement 对象将 select 操作推送数据库并返回对应的查询结果

StatementHandler的继承结构

有没有感觉和 Executor 的继承体系很相似呢?最顶级接口是四大组件对象,分别有两个实现类 BaseStatementHandler 和 RoutingStatementHandler ,BaseStatementHandler 有三个实现类, 他们分别是 SimpleStatementHandler、PreparedStatementHandler 和 CallableStatementHandler。

RoutingStatementHandler: RoutingStatementHandler 并没有对 Statement 对象进行使用,只是根据StatementType 来创建一个代理,代理的就是对应Handler的三种实现类。在MyBatis工作时,使用的StatementHandler 接口对象实际上就是 RoutingStatementHandler 对象。我们可以理解为

StatementHandler statmentHandler = new RountingStatementHandler();
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// 根据 statementType 创建对应的 Statement 对象switch (ms.getStatementType()) {case STATEMENT:delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case PREPARED:delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case CALLABLE:delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;default:throw new ExecutorException("Unknown statement type: " + ms.getStatementType());}
}

BaseStatementHandler: 是 StatementHandler 接口的另一个实现类.本身是一个抽象类.用于简化StatementHandler 接口实现的难度,属于适配器设计模式体现,它主要有三个实现类

  • SimpleStatementHandler: 管理 Statement 对象并向数据库中推送不需要预编译的SQL语句
  • PreparedStatementHandler: 管理 Statement 对象并向数据中推送需要预编译的SQL语句,
  • CallableStatementHandler:管理 Statement 对象并调用数据库中的存储过程

StatementHandler 对象创建以及源码分析

StatementHandler 对象是在 SqlSession 对象接收到命令操作时,由 Configuration 对象中的newStatementHandler 负责调用的,也就是说 Configuration 中的 newStatementHandler 是由执行器中的查询、更新(插入、更新、删除)方法来提供的,StatementHandler 其实就是由 Executor 负责管理和创建的。

SimpleExecutor.java

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {// 获取环境配置Configuration configuration = ms.getConfiguration();// 创建StatementHandler,解析SQL语句StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());// 由handler来对SQL语句执行解析工作return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);}}

由图中可以看出,StatementHandler 默认创建一个 RoutingStatementHandler ,这也就是 StatementHandler 的默认实现,由 RoutingStatementHandler 负责根据 StatementType 创建对应的StatementHandler 来处理调用。

prepare方法调用流程分析

prepare 方法的调用过程是这样的,在上面的源码分析过程中,我们分析到了执行器 Executor 在执行SQL语句的时候会创建 StatementHandler 对象,进而经过一系列的 StatementHandler 类型的判断并初始化。再拿到StatementHandler 返回的 statementhandler 对象的时候,会调用其prepareStatement() 方法,下面就来一起看一下 preparedStatement() 方法(我们以简单执行器为例,因为创建其 StatementHandler 对象的流程和执行 preparedStatement() 方法的流程是差不多的):

SimpleExecutor.java

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {// 获取环境配置Configuration configuration = ms.getConfiguration();// 创建StatementHandler,解析SQL语句StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());// 由handler来对SQL语句执行解析工作return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);stmt = handler.prepare(connection, transaction.getTimeout());handler.parameterize(stmt);return stmt;
}
// prepare方法调用到 StatementHandler 的实现类RoutingStatementHandler,再由RoutingStatementHandler调用BaseStatementHandler中的prepare 方法
// RoutingStatementHandler.java
@Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {return delegate.prepare(connection, transactionTimeout);}
// BaseStatementHandler.java@Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {statement = instantiateStatement(connection);setStatementTimeout(statement, transactionTimeout);setFetchSize(statement);return statement;} ...

其中最重要的方法就是 instantiateStatement() 方法了,在得到数据库连接 connection 的对象的时候,会去调用 instantiateStatement() 方法,instantiateStatement 方法位于 StatementHandler 中,是一个抽象方法由子类去实现,实际执行的是三种 StatementHandler 中的一种,我们还以 SimpleStatementHandler 为例

protected Statement instantiateStatement(Connection connection) throws SQLException {if (mappedStatement.getResultSetType() != null) {return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);} else {return connection.createStatement();}}

从上面代码我们可以看到,instantiateStatement() 最终返回的也是Statement对象,经过一系列的调用会把statement 对象返回到 SimpleExecutor 简单执行器中,为 parametersize 方法所用。也就是说,prepare 方法负责生成 Statement 实例对象,而 parameterize 方法用于处理 Statement 实例多对应的参数。

parametersize 方法调用流程分析

parametersize 方法看的就比较畅快了,也是经由执行器来管理 parametersize 的方法调用,这次我们还想以SimpleStatementHandler 为例但是却不行了?为什么呢?因为 SimpleStatementHandler 是个空实现了,为什么是null呢?因为 SimpleStatementHandler 只负责处理简单SQL,能够直接查询得到结果的SQL,例如:

select studenname from Student

而 SimpleStatementHandler 又不涉及到参数的赋值问题,那么参数赋值该在哪里进行呢?实际上为参数赋值这步操作是在 PreparedStatementHandler 中进行的,因此我们的主要关注点在 PreparedStatementHandler 中的parameterize 方法

public void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);
}

我们可以看到,为参数赋值的工作是由一个叫做 parameterHandler 对象完成的,都是这样的吗?来看一下CallableStatementHandler

public void parameterize(Statement statement) throws SQLException {registerOutputParameters((CallableStatement) statement);parameterHandler.setParameters((CallableStatement) statement);
}

上面代码可以看到,CallableStatementHandler 也是由 parameterHandler 进行参数赋值的。

那么这个 parameterHandler 到底是什么呢?这个问题能想到说明老兄你已经上道了,这也就是我们执行器的第三个组件。这个组件我们在下一节进行分析

update 方法调用流程分析

用一幅流程图来表示一下这个调用过程:

简单描述一下update 方法的执行过程:

  1. MyBatis 接收到 update 请求后会先找到 CachingExecutor 缓存执行器查询是否需要刷新缓存,然后找到BaseExecutor 执行 update 方法;
  2. BaseExecutor 基础执行器会清空一级缓存,然后交给再根据执行器的类型找到对应的执行器,继续执行 update 方法;
  3. 具体的执行器会先创建 Configuration 对象,根据 Configuration 对象调用 newStatementHandler 方法,返回 statementHandler 的句柄;
  4. 具体的执行器会调用 prepareStatement 方法,找到本类的 prepareStatement 方法后,再有prepareStatement 方法调用 StatementHandler 的子类 BaseStatementHandler 中的 prepare 方法
  5. BaseStatementHandler 中的 prepare 方法会调用 instantiateStatement 实例化具体的 Statement 对象并返回给具体的执行器对象
  6. 由具体的执行器对象调用 parameterize 方法给参数进行赋值。

续上上面的 parameter方法,具体交给 ParameterHandler 进行进一步的赋值处理

Query 查询方法几乎和 update 方法相同,这里就不再详细的举例说明了.

原文:主页 - 头条号
来源:今日头条
作者:Java架构师CAT

mybatis核心配置_MyBatis 核心配置综述之StatementHandler相关推荐

  1. 未设置服务器核心文件,[问题3] dhcpd.conf是DHCP服务器的配置的核心,每次启动DH..._考试资料网...

    问答题在一个基于TCP/IP协议的网络中,每台主机都有会有一个IP地址(如:Internet).Inter- net上的每台主机都有一个惟一的IP地址,根据获得IP址的方式不同,可以分为静态IP,动态 ...

  2. hibernate教程--常用配置和核心API详解

    一.Hibernate的常用的配置及核心API. 1.1 Hibernate的常见配置: 1.1.1.核心配置: 核心配置有两种方式进行配置:  1)属性文件的配置: * hibernate.prop ...

  3. hibernate教程--常用配置和核心API

    一.Hibernate的常用的配置及核心API. 1.1Hibernate的常见配置: 1.1.1.核心配置: 核心配置有两种方式进行配置: 1)属性文件的配置: * hibernate.proper ...

  4. SpringBoot核心原理:自动配置、事件驱动、Condition

    点击关注公众号,实用技术文章及时了解 来源:blog.csdn.net/l6108003/article/ details/106966386 前言 SpringBoot是Spring的包装,通过自动 ...

  5. 部门WIFI配置-防火墙-核心交换机和POE交换机

    最近做的一个比较简单的wifi的配置,负责的是一台防火墙,一台核心交换机,一台poe交换机.AC和AP都不是我们负责. 先说一下防火墙的配置: 常规的开局就不说了 防火墙的G1/0/0 与运营商相连, ...

  6. 更换公司核心路由器案例配置---1.console初始配置-2.用户配置-3.telnet登录配置-4.NAT端口映射配置-5.内网服务器配置-6.链路聚合配置-7.DHCP和DNS配置

    一.场景 因公司核心路由器老旧,运行不稳定,对旧路由器进行更换,要在路由器上实现以下功能. 1.模拟新机console初始配置:         2.用户配置:         3.模拟通过telne ...

  7. mybatis 遍历数组_Mybatis中别名、插件与数据源配置

    上一篇介绍了Configuration的properties和settings,接下来继续. 设置别名 别名也是mybatis中最重要的配置之一,可以通过很简单的一个字符串来代替一个Class,它可以 ...

  8. java web核心编程_JavaWeb核心编程之(三)Servlet配置

    Servlet配置 1.配置Serlvet加载时机 2.配置多映射 配置加载时机 新建项目config->创建包com.xiaoan.config->创建类FirstServlet imp ...

  9. 深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)

    上篇文章<深入浅出Mybatis系列(三)---配置详解之properties与environments(mybatis源码篇)> 介绍了properties与environments, ...

最新文章

  1. debian php mysql 包_Linux+Varnish+Apache+MySQL+PHP一键包For Ubuntu/Debian
  2. Microsoft Dynamics CRM4.0 Data Auditing and Restore (数据审核和恢复)
  3. ISA 2006 允许使用QQ
  4. dell 远程访问管理卡iDRAC 7
  5. wpf开发仿真3d软件_web 3d 与仿真
  6. SpringBoot聚合项目总结
  7. win10 安装mysql-5.7.19-winx64
  8. oracle关键字 bulk,oracle和sqlserver的一些保留关键字
  9. Eclipse/MyEclipse注释模板和格式化模板的使用
  10. Linux下高速缓存DNS的配置
  11. Java模拟实现一个基于文本界面的《家庭记账软件》
  12. 多线程-Thread.join()的运用
  13. springboot-jpa-querydsl
  14. 数据库和数据库管理系统的区别
  15. GDOI2017小结
  16. 软件测试判定表测试用例,黑盒测试用例设计方法之判定表法
  17. 调查显示:SD-WAN部署迅猛增长,MPLS不会消失
  18. deepstream运行TAO模型
  19. 几何光学学习笔记(12)- 3.9几种典型系统的理想光学系统性质 3.10 矩阵运算在几何光学中的应用
  20. 区分event对象中的[clientX,offsetX,screenX,pageX]

热门文章

  1. python逐行读取数据时出现错误_Python利用逐行读取readline()打印出现空行的解决办法...
  2. 数据自治开放应用平台设计与实践
  3. 作者:Anjaneyulu Passala, 男,印度理工学院计算机科学与工程学院博士,印孚瑟斯技术有限公司主任研究科学家。...
  4. 客座编辑:杜小勇(1963‒),中国人民大学信息学院教授,博士生导师。
  5. 【JSP】web.xml配置JavaWeb项目首页
  6. 需求分析——掌握UML建模语言的用例图
  7. Python3 标准库及相关内容
  8. vue组件之间8种组件通信方式总结
  9. MySQL数据库----触发器
  10. 解决HP ProLiant DL380 G5的Centos 7安装与启动不能识别硬盘问题