首页 > 编程语言 >Mybatis源码5 StatementHandler ,ParameterHandler

Mybatis源码5 StatementHandler ,ParameterHandler

时间:2022-08-21 12:11:35浏览次数:50  
标签:调用 ParameterHandler 源码 参数 Statement Mybatis StatementHandler null

Mybatis源码5 StatementHandler ,ParameterHandler

一丶概述

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

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

执行的流程如下

image-20220315193931579

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

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

  • SimpleExecutor

image-20220315194739642

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

接着准备调用prepareStatement

image-20220315195129070

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

  • ReuseExecutor

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

    image-20220315195843172

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

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

三丶StatementHandler和Configuration创建StatementHandler

image-20220315191852140

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

    image-20220315201839835

    • 设置超时时长 setStatementTimeout

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

    • 设置读取条数 setFetchSize

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

3.RoutingStatementHandler

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

image-20220315202641508

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

4.PrepareStatementHandler

默认的StatementHandler

  • instantiateStatement

image-20220315204327526

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

    本质是调用了PreparedStatement.getUpdateCount 返回

    image-20220315204427622

  • 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

image-20220315205525024

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

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

四丶ParameterHandler

image-20220315205817644

  • 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);
                }
            }
        }
    }
}

标签:调用,ParameterHandler,源码,参数,Statement,Mybatis,StatementHandler,null
From: https://www.cnblogs.com/cuzzz/p/16609752.html

相关文章

  • Mybatis 源码6 结果集映射流程 ,mybatis插件实现原理和基于mybatis插件实现参数化类型T
    Mybatis源码6结果集映射流程,mybatis插件实现原理和基于mybatis插件实现参数化类型TypeHandler一丶前情回顾书接上回,下面是SimpleExecutor执行查询的主要逻辑prepa......
  • Mybatis源码1JDBC->mybatis主要流程->mybatis Excutor简介
    Mybatis源码1JDBC->mybatis主要流程->mybatisExcutor简介一丶mybatis概述MyBatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis免除了几乎......
  • Spring源码学习笔记4——BeanFactoryPostProcessor执行
    一丶BeanFactoryPostProcessor是什么Spring留给我们的一个扩展接口,在BeanDefinition加载注册完之后,并执行一些前置操作(笔记3)之后会反射生产所有的BeanFactoryPostProcesso......
  • Spring源码学习笔记6——Spring bean的实例化
    一丶前言前面我们了解到读取xmlor根据扫描路径生成BeanDefinition并注册到BeanFactory,相当于我们具备了生火做饭的原材料:BeanDefinition,接下来就是Spring最为核心的,根据......
  • Tomcat源码分析--类加载器
    Tomcat类加载器结构上图是Tomcat文档中所展示的Tomcat类加载结构。在这个结构中Bootstartap和System的类加载器由java虚拟机实现。common类加载器由Tomcat容器实现,它对......
  • Mybatis的一级、二级缓存
    一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Sessionflush或close之后,该Session中的所有Cache就将清空,默认打开一级缓存。二级......
  • 大家都能看得懂的源码 - ahooks useSet 和 useMap
    本文是深入浅出ahooks源码系列文章的第十篇,该系列已整理成文档-地址。觉得还不错,给个 star 支持一下哈,Thanks。今天我们来聊聊ahooks中对Map和Set类型进行状态......
  • Mybatis的缓存
    1.Mybatis的一级缓存Mybatis的一级缓存是默认开启的,你只要搭建一个Mybatis框架,就可以直接使用一级缓存。一级缓存是SqlSession级别的,通过SqlSession查询的数据会被缓存,......
  • Mybatis组件介绍
    核心组件SqlSessionFactoryBuilderSqlSessionFactoryBuilder的作用就是通过XML或者Java代码来建造一个工厂(SqlSessionFactory),并且可以通过它建造多个这样的工厂。一旦......
  • spring源码学习笔记1——解析xml生成BeanDefinition的过程解析
    spring源码学习笔记1——解析xml生成BeanDefinition的过程解析一丶Spring解析Xml生成BeanDefinition的流程1.指定xml路径解析xml首先需要知道xml的位置,如下我们构造了Ap......