Loading

Mybatis源码5 StatementHandler ,ParameterHandler

Mybatis5 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);
                }
            }
        }
    }
}
posted @ 2022-08-21 12:02  Cuzzz  阅读(264)  评论(0编辑  收藏  举报