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 配置文件中映射过去的啊,就比如如下例子

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

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

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

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

SimpleExecutor.java

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 的解析过程,以简单执行器为例

像是 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免费下载 权限,欢迎你关注我

MyBatis 核心配置综述之 ParameterHandler的更多相关文章

  1. MyBatis 核心配置综述之StatementHandler

    目录 MyBatis 核心配置综述之StatementHandler MyBatis 四大组件之StatementHandler StatementHandler 的基本构成 StatementHan ...

  2. MyBatis 核心配置综述之Executor

    目录 MyBatis四大组件之 Executor执行器 Executor的继承结构 Executor创建过程以及源码分析 Executor接口的主要方法 Executor 的现实抽象 上一篇我们对Sq ...

  3. MyBatis 核心配置综述之 ResultSetHandler

    目录 ResultSetHandler 简介 ResultSetHandler 创建 ResultSetHandler 处理结果映射 DefaultResultSetHandler 源码解析 我们之前 ...

  4. 1.2(Mybatis学习笔记)Mybatis核心配置

    一.Mybatis核心对象 1.1SqlSeesionFactory SqlSessionFactory主要作用是创建时SqlSession. SqlSessionFactory可通过SqlSessi ...

  5. Mybatis学习——Mybatis核心配置

    MyBatis的核心配置 在使用MyBatis框架时,设计两个核心的d对象:SqlSessionFactory和SqlSession. SqlsessionFactory SqlSessionFact ...

  6. Mybatis的核心配置

    之前了解了Mybatis的基本用法,现在学习一下Mybatis框架中的核心对象以及映射文件和配置文件,来深入的了解这个框架. 1.Mybatis的核心对象 使用MyBatis框架时,主要涉及两个核心对 ...

  7. 笔记:MyBatis XML配置详解

    MyBatis 的配置文件包含了影响 MyBatis 行为甚深的设置(settings)和属性(properties)信息.文档的顶层结构如下: configuration 配置 properties ...

  8. MyBatis核心配置文件详解

    ------------------------siwuxie095                                     MyBatis 核心配置文件详解         1.核心 ...

  9. Java-MyBatis:MyBatis 3 配置

    ylbtech-Java-MyBatis:MyBatis 3 配置 1.返回顶部 1. XML 映射配置文件 MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings) ...

随机推荐

  1. python爬虫之快速对js内容进行破解

    python爬虫之快速对js内容进行破解 今天介绍下数据被js加密后的破解方法.距离上次发文已经过去半个多月了,我写文章的主要目的是把从其它地方学到的东西做个记录顺便分享给大家,我承认自己是个懒猪.不 ...

  2. Codeforces Gym101246G:Revolutionary Roads(DFS+思维)

    http://codeforces.com/gym/101246/problem/G 题意:有一个n个点m条边的有向图,现在可以修改某一条有向边使得其为无向边,问修改哪些边可以使得修改后的强连通分量的 ...

  3. Java开发面试题汇总 -- 精选版(附答案)

    最近事情太多,没太时间写公众号.今天抽空再整理整理面试中的那点事吧,帮助那些正在找工作或想跳槽找工作的兄弟姐妹们. 前面我己写过多篇推文,相信关注此公众号的伙伴们已经了解掌握了不少.从目前流行的开发技 ...

  4. Java编程思想:File类getCanonicalFile()方法,listFile()方法

    import java.io.IOException; import java.util.*; import java.util.regex.Pattern; import java.io.File; ...

  5. app组件跳转到页面

    这段时间根据项目需求,开发一个app的一个页面,这里用到了从组件跳转到index文件下的.vue页面.第一次接触,参考了同事的文档,写出来了,这里记录一下. 文档链接: https://www.yuq ...

  6. CAxWindow

    AtlAxWinInit(); CAxWindow m_wndFlashPlayer; CComPtr<IShockwaveFlash> m_FlashPtr; CComPtr<IU ...

  7. 比赛:小奔与不等四边形solution

    题目: 题目背景 有这样一道经典的数学题:已知一个四边形的边长是四个连续的正整数,求证这个四边形的面积的最大值不为整数.小奔轻松地证明了这个问题,现在问题来了,大奔要求小奔以最快的速度算出给定边长的四 ...

  8. xss magic_quotes_gpc

    ---恢复内容开始--- magic_quotes_gpc函数,在php5.4以上移除了, 但是很奇怪的是  我的5.6版本这边  是可以找到这个选项的. 在php.ini文件里面,默认关闭,如果将此 ...

  9. Mybatis方法入参处理

    1,在单个入参的情况下,mybatis不做任何处理,#{参数名} 即可,甚至连参数名都可以不需要,因为只有一个参数,或者使用 Mybatis的内置参数 _parameter. 2,多个入参: 接口方法 ...

  10. 头部姿态估计 - OpenCV/Dlib/Ceres

    基本思想 通过Dlib获得当前人脸的特征点,然后通过旋转平移标准模型的特征点进行拟合,计算标准模型求得的特征点与Dlib获得的特征点之间的差,使用Ceres不断迭代优化,最终得到最佳的旋转和平移参数. ...