MyBatis 核心配置综述之 ParameterHandlers
nanshan 2024-11-10 10:12 36 浏览 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 进行参数赋值。
相关推荐
- 0722-6.2.0-如何在RedHat7.2使用rpm安装CDH(无CM)
-
文档编写目的在前面的文档中,介绍了在有CM和无CM两种情况下使用rpm方式安装CDH5.10.0,本文档将介绍如何在无CM的情况下使用rpm方式安装CDH6.2.0,与之前安装C5进行对比。环境介绍:...
- ARM64 平台基于 openEuler + iSula 环境部署 Kubernetes
-
为什么要在arm64平台上部署Kubernetes,而且还是鲲鹏920的架构。说来话长。。。此处省略5000字。介绍下系统信息;o架构:鲲鹏920(Kunpeng920)oOS:ope...
- 生产环境starrocks 3.1存算一体集群部署
-
集群规划FE:节点主要负责元数据管理、客户端连接管理、查询计划和查询调度。>3节点。BE:节点负责数据存储和SQL执行。>3节点。CN:无存储功能能的BE。环境准备CPU检查JDK...
- 在CentOS上添加swap虚拟内存并设置优先级
-
现如今很多云服务器都会自己配置好虚拟内存,当然也有很多没有配置虚拟内存的,虚拟内存可以让我们的低配服务器使用更多的内存,可以减少很多硬件成本,比如我们运行很多服务的时候,内存常常会满,当配置了虚拟内存...
- 国产深度(deepin)操作系统优化指南
-
1.升级内核随着deepin版本的更新,会自动升级系统内核,但是我们依旧可以通过命令行手动升级内核,以获取更好的性能和更多的硬件支持。具体操作:-添加PPAs使用以下命令添加PPAs:```...
- postgresql-15.4 多节点主从(读写分离)
-
1、下载软件[root@TX-CN-PostgreSQL01-252software]#wgethttps://ftp.postgresql.org/pub/source/v15.4/postg...
- Docker 容器 Java 服务内存与 GC 优化实施方案
-
一、设置Docker容器内存限制(生产环境建议)1.查看宿主机可用内存bashfree-h#示例输出(假设宿主机剩余16GB可用内存)#Mem:64G...
- 虚拟内存设置、解决linux内存不够问题
-
虚拟内存设置(解决linux内存不够情况)背景介绍 Memory指机器物理内存,读写速度低于CPU一个量级,但是高于磁盘不止一个量级。所以,程序和数据如果在内存的话,会有非常快的读写速度。但是,内存...
- Elasticsearch性能调优(5):服务器配置选择
-
在选择elasticsearch服务器时,要尽可能地选择与当前业务量相匹配的服务器。如果服务器配置太低,则意味着需要更多的节点来满足需求,一个集群的节点太多时会增加集群管理的成本。如果服务器配置太高,...
- Es如何落地
-
一、配置准备节点类型CPU内存硬盘网络机器数操作系统data节点16C64G2000G本地SSD所有es同一可用区3(ecs)Centos7master节点2C8G200G云SSD所有es同一可用区...
- 针对Linux内存管理知识学习总结
-
现在的服务器大部分都是运行在Linux上面的,所以,作为一个程序员有必要简单地了解一下系统是如何运行的。对于内存部分需要知道:地址映射内存管理的方式缺页异常先来看一些基本的知识,在进程看来,内存分为内...
- MySQL进阶之性能优化
-
概述MySQL的性能优化,包括了服务器硬件优化、操作系统的优化、MySQL数据库配置优化、数据库表设计的优化、SQL语句优化等5个方面的优化。在进行优化之前,需要先掌握性能分析的思路和方法,找出问题,...
- Linux Cgroups(Control Groups)原理
-
LinuxCgroups(ControlGroups)是内核提供的资源分配、限制和监控机制,通过层级化进程分组实现资源的精细化控制。以下从核心原理、操作示例和版本演进三方面详细分析:一、核心原理与...
- linux 常用性能优化参数及理解
-
1.优化内核相关参数配置文件/etc/sysctl.conf配置方法直接将参数添加进文件每条一行.sysctl-a可以查看默认配置sysctl-p执行并检测是否有错误例如设置错了参数:[roo...
- 如何在 Linux 中使用 Sysctl 命令?
-
sysctl是一个用于配置和查询Linux内核参数的命令行工具。它通过与/proc/sys虚拟文件系统交互,允许用户在运行时动态修改内核参数。这些参数控制着系统的各种行为,包括网络设置、文件...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- linux 查询端口号 (58)
- docker映射容器目录到宿主机 (66)
- 杀端口 (60)
- yum更换阿里源 (62)
- internet explorer 增强的安全配置已启用 (65)
- linux自动挂载 (56)
- 禁用selinux (55)
- sysv-rc-conf (69)
- ubuntu防火墙状态查看 (64)
- windows server 2022激活密钥 (56)
- 无法与服务器建立安全连接是什么意思 (74)
- 443/80端口被占用怎么解决 (56)
- ping无法访问目标主机怎么解决 (58)
- fdatasync (59)
- 405 not allowed (56)
- 免备案虚拟主机zxhost (55)
- linux根据pid查看进程 (60)
- dhcp工具 (62)
- mysql 1045 (57)
- 宝塔远程工具 (56)
- ssh服务器拒绝了密码 请再试一次 (56)
- ubuntu卸载docker (56)
- linux查看nginx状态 (63)
- tomcat 乱码 (76)
- 2008r2激活序列号 (65)