当前位置: 首页 > news >正文

MyBatis 核心配置综述之 ParameterHandler

目录

    • ParameterHandler 简介
    • ParameterHandler 创建
    • ParameterHandler 中的参数从何而来
    • ParameterHandler 解析

MyBatis 四大核心组件我们已经了解到了两种,一个是 Executor ,它是MyBatis 解析SQL请求首先会经过的第一道关卡,它的主要作用在于创建缓存,管理 StatementHandler 的调用,为 StatementHandler 提供 Configuration 环境等。StatementHandler 组件最主要的作用在于创建 Statement 对象与数据库进行交流,还会使用 ParameterHandler 进行参数配置,使用 ResultSetHandler 把查询结果与实体类进行绑定。那么本篇就来了解一下第三个组件 ParameterHandler。

ParameterHandler 简介

ParameterHandler 相比于其他的组件就简单很多了,ParameterHandler 译为参数处理器,负责为 PreparedStatement 的 sql 语句参数动态赋值,这个接口很简单只有两个方法

/**
 * A parameter handler sets the parameters of the {@code PreparedStatement}
 * 参数处理器为 PreparedStatement 设置参数
 */
public interface ParameterHandler {

  Object getParameterObject();

  void setParameters(PreparedStatement ps)
      throws SQLException;

}

ParameterHandler 只有一个实现类 DefaultParameterHandler , 它实现了这两个方法。

  • getParameterObject: 用于读取参数
  • setParameters: 用于对 PreparedStatement 的参数赋值

ParameterHandler 创建

参数处理器对象是在创建 StatementHandler 对象的同时被创建的,由 Configuration 对象负责创建

BaseStatementHandler.java

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  this.configuration = mappedStatement.getConfiguration();
  this.executor = executor;
  this.mappedStatement = mappedStatement;
  this.rowBounds = rowBounds;

  this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  this.objectFactory = configuration.getObjectFactory();

  if (boundSql == null) { // issue #435, get the key before calculating the statement
    generateKeys(parameterObject);
    boundSql = mappedStatement.getBoundSql(parameterObject);
  }

  this.boundSql = boundSql;

  // 创建参数处理器
  this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
  // 创建结果映射器
  this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}

在创建 ParameterHandler 时,需要传入SQL的mappedStatement 对象,读取的参数和SQL语句

注意:一个 BoundSql 对象,就代表了一次sql语句的实际执行,而 SqlSource 对象的责任,就是根据传入的参数对象,动态计算这个 BoundSql, 也就是 Mapper 文件中节点的计算,是由 SqlSource 完成的,SqlSource 最常用的实现类是 DynamicSqlSource

Configuration.java

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  // 创建ParameterHandler
  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  return parameterHandler;
}

上面是 Configuration 创建 ParameterHandler 的过程,它实际上是交由 LanguageDriver 来创建具体的参数处理器,LanguageDriver 默认的实现类是 XMLLanguageDriver,由它调用 DefaultParameterHandler 中的构造方法完成 ParameterHandler 的创建工作

public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
}

public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  this.mappedStatement = mappedStatement;
  this.configuration = mappedStatement.getConfiguration();
  // 获取 TypeHandlerRegistry 注册
  this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
  this.parameterObject = parameterObject;
  this.boundSql = boundSql;
}

上面的流程是创建 ParameterHandler 的过程,创建完成之后,该进行具体的解析工作,那么 ParameterHandler 如何解析SQL中的参数呢?SQL中的参数从哪里来的?

ParameterHandler 中的参数从何而来

你可能知道 Parameter 中的参数是怎么来的,无非就是从 Mapper 配置文件中映射过去的啊,就比如如下例子

1515111-20190807231123882-227673915.png

参数肯定就是图中标红的 1 ,然后再传到XML对应的 SQL 语句中,用 #{} 或者 ${} 来进行赋值啊,

1515111-20190807231133268-1219817070.png

嗯,你讲的没错,可是你知道这个参数是如何映射过来的吗?或者说你知道 Parameter 的解析过程吗?或许你不是很清晰了,我们下面就来探讨一下 ParameterHandler 对参数的解析,这其中涉及到 MyBatis 中的动态代理模式

在MyBatis 中,当 deptDao.findByDeptNo(1) 将要执行的时候,会被 JVM 进行拦截,交给 MyBatis 中的代理实现类 MapperProxy 的 invoke 方法中,这也是执行 SQL 语句的主流程。

1515111-20190807231142981-835042774.png

然后交给 Executor 、StatementHandler进行对应的参数解析和执行,因为是带参数的 SQL 语句,最终会创建 PreparedStatement 对象并创建参数解析器进行参数解析

SimpleExecutor.java

1515111-20190807231153862-1689775186.png

handler.parameterize(stmt) 最终会调用到 DefaultParameterHandler 中的 setParameters 方法,我在源码上做了注释,为了方便拷贝,我没有采用截图的形式

public void setParameters(PreparedStatement ps) {
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  // parameterMappings 就是对 #{} 或者 ${} 里面参数的封装
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if (parameterMappings != null) {
    // 如果是参数化的SQL,便需要循环取出并设置参数的值
    for (int i = 0; i < parameterMappings.size(); i++) {
      ParameterMapping parameterMapping = parameterMappings.get(i);
      // 如果参数类型不是 OUT ,这个类型与 CallableStatementHandler 有关
      // 因为存储过程不存在输出参数,所以参数不是输出参数的时候,就需要设置。
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        // 得到#{}  中的属性名
        String propertyName = parameterMapping.getProperty();
        // 如果 propertyName 是 Map 中的key
        if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
          // 通过key 来得到 additionalParameter 中的value值
          value = boundSql.getAdditionalParameter(propertyName);
        }
        // 如果不是 additionalParameters 中的key,而且传入参数是 null, 则value 就是null
        else if (parameterObject == null) {
          value = null;
        }
        // 如果 typeHandlerRegistry 中已经注册了这个参数的 Class对象,即它是Primitive 或者是String 的话
        else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          // 否则就是 Map
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        // 在通过SqlSource 的parse 方法得到parameterMappings 的具体实现中,我们会得到parameterMappings的typeHandler
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        // 获取typeHandler 的jdbc type
        JdbcType jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        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 解析

我们在 MyBatis 核心配置综述之 StatementHandler 一文中了解到 Executor 管理的是 StatementHandler 对象的创建以及参数赋值,那么我们的主要入口还是 Executor 执行器

下面用一个流程图表示一下 ParameterHandler 的解析过程,以简单执行器为例

1515111-20190807231207153-238300434.png

像是 doQuery,doUpdate,doQueryCursor等方法都会先调用到

// 生成 preparedStatement 并调用 prepare 方法,并为参数赋值
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;
}

然后在生成 preparedStatement 调用DefaultParameterHandler进行参数赋值。

公众号提供 优质Java资料 以及CSDN免费下载 权限,欢迎你关注我
1515111-20190807231446837-350627522.png

转载于:https://www.cnblogs.com/cxuanBlog/p/11318502.html

相关文章:

  • webbrowser
  • maven多模块profiles的石使用
  • 史上最强maven配置详情
  • vue-preview 缩略图
  • Redis 学习笔记(篇八):事件
  • Spring Boot使用Liquibase最佳实践
  • Hibernate相关概念及序列化和持久化的区别
  • Oracle常用指令
  • leetcode 25. Reverse Nodes in k-Group
  • python异常处理
  • 极客时间-左耳听风-程序员攻略-UI/UX设计
  • 贪心训练题
  • idea新建maven项目后生成web.xml方法和添加到tomcat方法
  • db mysql / mysql cluster 5.7.19 / reboot / devops
  • JAVA:用户从键盘只能输入整数,程序输出这些整数的乘积。
  • CentOS 7 防火墙操作
  • FineReport中如何实现自动滚屏效果
  • Flex布局到底解决了什么问题
  • JavaWeb(学习笔记二)
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • SwizzleMethod 黑魔法
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • vue--为什么data属性必须是一个函数
  • 对超线程几个不同角度的解释
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 免费小说阅读小程序
  • 排序算法学习笔记
  • 前端js -- this指向总结。
  • 前端存储 - localStorage
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • 使用docker-compose进行多节点部署
  • 微信公众号开发小记——5.python微信红包
  • 湖北分布式智能数据采集方法有哪些?
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #Java第九次作业--输入输出流和文件操作
  • (C++17) std算法之执行策略 execution
  • (分类)KNN算法- 参数调优
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (强烈推荐)移动端音视频从零到上手(下)
  • (一)插入排序
  • (转)Linux整合apache和tomcat构建Web服务器
  • .NET 8.0 中有哪些新的变化?
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .Net 高效开发之不可错过的实用工具
  • .net获取当前url各种属性(文件名、参数、域名 等)的方法
  • .Net中的集合
  • @hook扩展分析
  • @RequestParam详解
  • [30期] 我的学习方法
  • [C# 开发技巧]实现属于自己的截图工具
  • [CTO札记]盛大文学公司名称对联
  • [Fri 26 Jun 2015 ~ Thu 2 Jul 2015] Deep Learning in arxiv
  • [HDU5685]Problem A