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

【源码解读之 Mybatis】【核心篇】--第篇:ParameterHandler参数处理机制

【源码解读之 Mybatis】【核心篇】–第篇:ParameterHandler参数处理机制

在 MyBatis 的四大核心组件中,ParameterHandler 负责将用户传入的参数对象转换为 JDBC 能够识别的参数形式。它位于 SQL 解析与执行之间,承担着参数绑定与类型转换的关键职责。本文将深入剖析 ParameterHandler 的设计原理、内部实现机制,并通过可运行的代码示例演示其工作流程。—## 一、ParameterHandler 的定位与职责MyBatis 中,SqlSession调用Executor执行 SQL 时,会先通过StatementHandler构建Statement,再通过ParameterHandler将参数设置到PreparedStatement中。ParameterHandler 的核心接口定义如下:javapublic interface ParameterHandler { Object getParameterObject(); void setParameters(PreparedStatement ps) throws SQLException;}-getParameterObject():获取原始参数对象(如 POJO、Map、基础类型等)。-setParameters(PreparedStatement ps):遍历参数映射,调用合适的TypeHandler将参数设置到PreparedStatement中。它的具体实现类是DefaultParameterHandler,内部包含两个重要字段:-TypeHandlerRegistry:注册所有类型处理器。-ParameterMapping:存储参数索引、Java类型、Jdbc类型、TypeHandler等信息。—## 二、核心原理:参数绑定流程### 2.1 参数映射的构建在 MappedStatement 构建阶段,MyBatis 会解析 SQL 中的#{}占位符,生成ParameterMapping列表。例如 SQLINSERT INTO user (name, age) VALUES (?, ?)会生成两个ParameterMapping,每个映射记录了参数位置、JDBC类型(如VARCHAR,INTEGER)、以及对应的 TypeHandler。### 2.2 setParameters 方法执行流程当PreparedStatement准备好后,DefaultParameterHandler.setParameters()会执行以下步骤:1. 获取参数对象的类型(POJO/Map/基础类型)。2. 遍历ParameterMapping列表。3. 对于每个映射,从参数对象中提取对应的值。4. 根据映射中注册的TypeHandler,调用setParameter(ps, i, value, jdbcType)。5.TypeHandler内部调用PreparedStatement.setXxx(i, value)完成 JDBC 级绑定。### 2.3 类型处理器选择机制MyBatis 通过TypeHandlerRegistry管理所有类型处理器的注册。选择过程基于:- 参数的 Java 类型(如String,Integer)。- 可选的 JDBC 类型(如VARCHAR,INTEGER)。如果未显式指定 JDBC 类型,则通过 Java 类型查找默认的 TypeHandler;否则根据 JDBC 类型和 Java 类型组合查找。—## 三、源码关键片段解析以下从org.apache.ibatis.scripting.defaults.DefaultParameterHandler中截取核心代码进行解读:javapublic void setParameters(PreparedStatement ps) { // 获取参数映射列表(每个#{}对应一个映射) List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); // 跳过输出参数(如存储过程OUT参数) if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; // 获取参数名称(如 "name", "age") String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // 处理动态SQL中的额外参数 value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { // 基础类型直接使用参数对象本身 value = parameterObject; } else { // 复杂对象(POJO/Map)通过MetaObject获取属性值 MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } // 获取对应的TypeHandler TypeHandler typeHandler = parameterMapping.getTypeHandler(); // 获取JDBC类型(可能为null) JdbcType jdbcType = parameterMapping.getJdbcType(); // 执行类型转换并设置到PreparedStatement typeHandler.setParameter(ps, i + 1, value, jdbcType); } } }}这段代码展示了参数提取、类型处理器选择、JDBC 类型设置的全过程。—## 四、可运行代码示例### 示例1:自定义 TypeHandler 实现日期格式化以下代码演示如何通过自定义 TypeHandler 将java.util.Date格式化为字符串存入数据库:java// 自定义 TypeHandler:将 Date 转为 "yyyy-MM-dd HH:mm:ss" 格式字符串public class DateStringTypeHandler extends BaseTypeHandler<Date> { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException { // 将 Date 对象格式化为字符串后设置到 PreparedStatement ps.setString(i, sdf.format(parameter)); } @Override public Date getNullableResult(ResultSet rs, String columnName) throws SQLException { String dateStr = rs.getString(columnName); try { return dateStr != null ? sdf.parse(dateStr) : null; } catch (ParseException e) { throw new SQLException(e); } } @Override public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException { // 省略实现 return null; } @Override public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { // 省略实现 return null; }}// 在 MyBatis 配置中注册自定义 TypeHandler// <typeHandlers>// <typeHandler handler="com.example.DateStringTypeHandler" javaType="java.util.Date"/>// </typeHandlers>// 使用示例 Mapper XML// <insert id="insertUser">// INSERT INTO user (name, birthday) VALUES (#{name}, #{birthday})// </insert>// 其中 birthday 为 java.util.Date 类型,会被自动转为字符串存储### 示例2:调试 ParameterHandler 工作过程以下代码模拟 MyBatis 内部调用 ParameterHandler 的过程,用于观察参数绑定细节:javaimport org.apache.ibatis.executor.parameter.DefaultParameterHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.type.TypeHandlerRegistry;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;public class ParameterHandlerDemo { public static void main(String[] args) throws Exception { // 1. 创建模拟的 MyBatis 配置 Configuration configuration = new Configuration(); TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); // 2. 创建参数对象(模拟 POJO) User user = new User(); user.setName("张三"); user.setAge(25); // 3. 模拟 BoundSql(简化版,实际由 SqlSource 生成) // 假设 SQL 为:INSERT INTO user (name, age) VALUES (?, ?) // 此处仅演示参数处理流程,不构建完整 MappedStatement BoundSql boundSql = new BoundSql(configuration, "INSERT INTO user (name, age) VALUES (?, ?)", new ArrayList<>(), user); // 4. 创建 DefaultParameterHandler DefaultParameterHandler handler = new DefaultParameterHandler( (MappedStatement) null, // 此处可忽略,实际需要 MappedStatement 对象 user, boundSql); // 5. 获取数据库连接(示例使用 H2 内存数据库) Connection conn = DriverManager.getConnection("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"); PreparedStatement ps = conn.prepareStatement(boundSql.getSql()); // 6. 调用 setParameters 方法(核心参数绑定) handler.setParameters(ps); // 7. 查看绑定后的参数(通过打印 ps 的参数值) System.out.println("参数1 (name): " + ps.getString(1)); // 应输出 "张三" System.out.println("参数2 (age): " + ps.getInt(2)); // 应输出 25 // 清理资源 ps.close(); conn.close(); } // 模拟 POJO 类 static class User { private String name; private int age; // getter/setter 省略 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }}运行上述代码,输出结果应为:参数1 (name): 张三参数2 (age): 25这表明 ParameterHandler 成功从User对象中提取属性值并绑定到PreparedStatement中。—## 五、总结ParameterHandler 是 MyBatis 参数处理的核心枢纽,它通过以下机制实现灵活的参数绑定:1.参数映射机制:将 SQL 中的#{}占位符与 Java 对象属性建立映射关系。2.类型处理器(TypeHandler):桥接 Java 类型与 JDBC 类型,支持自定义扩展。3.动态参数提取:支持 POJO、Map、基础类型等多种参数形式。4.MetaObject 反射:对复杂对象使用 MetaObject 进行属性值获取。理解 ParameterHandler 的工作原理,能帮助开发者:- 在需要特殊类型转换时编写自定义 TypeHandler。- 调试参数绑定异常(如类型不匹配、null 值处理等)。- 深入理解 MyBatis 的 SQL 执行链路。ParameterHandler 的设计体现了策略模式与工厂模式的结合,通过将类型转换策略抽象为 TypeHandler,使系统具备良好的扩展性。这正是 MyBatis 能够灵活支持多种数据库和数据类型的关键所在。

http://www.jsqmd.com/news/1257291/

相关文章:

  • 广州资质代办服务企业做GEO服务商怎么选?2026靠谱选型指南与五家服务商深度测评 - 科技快讯
  • 2026广州艺考培训服务企业做GEO服务商怎么选?本地靠谱选型指南与五家服务商深度测评 - 子柔传媒
  • 如何用 AI 绘制宏大的战争/科幻背景,且不失细节?
  • 大道至简观后感
  • 如何在数字化课堂中实现自主控制权:技术解决方案深度解析
  • subprocess.run函数介绍
  • 2026 年当下,苍南有实力的短视频打造机构格局重塑与选型新思路,没人告诉你,它居然能让素人7天涨粉破10万? - 企业信息推荐【官方】
  • ScriptCat脚本猫终极指南:高效浏览器自动化与个性化改造的完整方案
  • 南平甲醛检测怎么选?2026新政后CMA实验室的标准、流程、价格与避坑指南——南平闽环测研甲醛检测中心 - 衡境测研
  • 广州工商注册服务企业做GEO服务商怎么选?2026年靠谱选型指南与五家服务商深度测评 - 科技快讯
  • Python自动化抢票:从原理到实战的三步配置法
  • HarmonyOS开发实战:小分享-深浅色主题切换——基于 resources/dark 适配
  • Figma中文汉化插件终极指南:3分钟实现全界面中文翻译
  • WaveTools鸣潮工具箱:游戏性能优化与账号管理的一站式解决方案
  • Apache Doris 实战教程:手把手实现 ClickHouse 表结构迁移与数据校验
  • 广州GEO优化服务怎么选?2026年企业GEO服务商靠谱选型指南与深度测评 - 科技快讯
  • 2026上海购宠终极攻略|避雷指南+深度测评+选宠干货!3000㎡CKU认证繁育基地,三区连锁零套路 - 同城大型猫犬舍
  • 新一代 Ai coding 工程进阶系列-前言
  • TLV320ADC3101 ADC数字信号处理与抽取滤波器配置实战指南
  • 共享充电宝机柜物联网卡后台不显示设备在线状态?假性离线根源解析与根治方案
  • 模型网关不是多接几家 API 当接口人——给厨房配个会比价、会兜底、会记账的采购总管
  • 无货源铺货软件怎么用?抖音小店与微信小店批量铺货流程、FAQ和抖掌柜教程 - 电商分享
  • AIGC检测工具选型陷阱大全,92%团队踩坑的4个致命误区:从BERT到LLM-Detector实战评测报告
  • 器,生成复指数信号与本振信号相乘,在ip核设置的过程中主要由三个模式 BYPASS 这个又叫直通模式,即不进行任何数字混频,基带信号 ...
  • 广州服饰品牌企业做GEO服务商怎么选?2026年五家服务商深度测评与靠谱选型指南 - 企业新闻快传
  • 大模型推理引擎vLLM(30): 参考sglang代码,重构vllm021中EP高吞吐代码,消除空泡问题:400us减小到25us
  • 广州招商加盟服务企业做GEO服务商怎么选?2026年五家机构深度对比与靠谱选型指南 - 企业新闻快传
  • 智能快递柜格口监控物联网卡在老旧小区弱网环境下多运营商切换组网方案
  • 2026北京香奈儿回收哪家口碑好?尚典CF/2.55鉴定实力过硬,这份本地测评榜单给出答案 - 奢品流通笔谈
  • Unity集成Gaussian Splatting:5分钟实现实时三维点云渲染