百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

MyBatis 核心配置综述之 ParameterHandlers

nanshan 2024-11-10 10:12 33 浏览 0 评论

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 进行参数赋值。

相关推荐

三种自建KMS激活系统自动激活windows方法

第一种:在windows服务器上搭建主要针对vol版本(win7、win10、win20xx、win2012等等)平台:我自己搭建的windows虚拟机,windows2016的操作系统软件:...

重装系统被收98元?避开Windows付费陷阱的实用指南

重装系统被收98元?避开Windows付费陷阱的实用指南有网友反映,在重装Windows系统后,屏幕突然弹出“激活系统需支付98元服务费”的提示,疑惑自己是不是遭遇了付费陷阱。事实上,微软官方的Wi...

Windows Server2012远程桌面服务配置和授权激活

安装:注意:安装完毕之后需手动重启一下计算机配置终端服务管理工具---远程桌面服务---RD授权诊断程序,查看当前服务器有没有授权授权:运行—>gpedit.msc->计算机配置---管理...

新书速览|Windows Server 2022 系统与网站配置实战

讲述桌面体验、ServerCore/NanoServer,容器与云系统的配置1本书内容《WindowsServer2022系统与网站配置实战》秉持作者一贯理论兼具实践的写作风格,以新版的Wi...

Windows激活全攻略:KMS神钥与专业工具的完美结合!

对于许多Windows用户来说,系统的激活是一个必经的过程。虽然Windows操作系统在未经激活的状态下也可以使用一段时间,但长期来看,未激活的系统会限制某些功能并频繁提示用户激活。以下是两种流行的激...

微软Win9全新激活技术曝光(微软系统激活有什么用)

2014-07-0905:46:00作者:徐日俄罗斯Wzor日前披露了更多关于Windows9的最新消息,据悉,Windows9将会在今年秋季亮相,其宣传口号是“想要开始按钮和开始菜单?如你所...

快速激活Windows 10/11:CMD命令详细教程

#记录我的2024#激活Windows操作系统是确保系统功能和安全更新正常运行的重要步骤。本文将为您分享如何使用命令提示符(CMD)在Windows10和Windows11上进行激活的详细步骤。...

Wndows 2019 RDS应用发布部署(rds的安装和应用程序的发布)

安装前的准备1、需要提供服务器作为应用中心,应用中心的推荐配置如下表所示。规格建议1-10人11-20人21-50人51-100人100+人CPU4核8核16核内存8GB16GB32GB64GB系统盘...

解决 Windows 系统激活难题(如何解决windows激活问题)

今天,一位朋友给我说,他手头有三台电脑,均同时弹出系统未激活的提示。他对此毫无头绪,便急忙将电脑上出现的激活提示信息一股脑发给了我。我看到其中一台显示的是“Windows10企业版LTSC尚...

自建KMS激活服务器(自建kms激活服务器的风险)

自建KMS激活服务器Win10和office安装后,都需要激活才可以使用,一般可以输入购买的MAK激活码进行在线激活,也可以通过KMS激活,网上也有很多激活工具,但这些工具一般都含有病毒或木马程序,容...

30秒免费激活windows和office亲测有效!

“第三方工具有病毒?”“KMS服务器激活总失效?”今天给大家分享一个开源激活工具——MicrosoftActivationScripts(MAS),无需密钥、不装软件,30秒永久激活Window...

「操作系统」Windows 10 LTSC 2019 企业版C大集成更新版

Windows10LTSC企业版CHIANNET集成更新优化整合多镜像版,CHIANNET,是USBOS超级PE维护盘工具箱作者,长久以来一直默默的更新着,USBOSPE软件,电脑城装机及...

一文看懂Windows激活:自查方法+授权类型科普(Win7/Win10通用)

一、如何判断Windows是否永久激活?无论是Win7还是Win10,均可通过以下方法快速验证:命令提示符法(通用):按下Win+R,输入slmgr.vbs/xpr并按回车键运行即可查看是否...

部分Windows Server 2019/2022用户反馈无法运行微软Teams应用

IT之家7月2日消息,科技媒体borncity今天(7月2日)发布博文,报道称在多个WindowsServer版本上,MicrosoftTeams应用近期出现了运行故障。用...

这种Windows激活方式已有20年...(windows现在激活)

2006年微软正式发布WindowsVista,随之而来引入了一项新的激活机制「OEM激活」,这项机制在Vista和Win7上最为流行。其实WindowsServer自2008开始至2025版本一...

取消回复欢迎 发表评论: