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

手写MyBatis第60弹: 如何优雅处理各种参数类型,从ParamNameResolver到TypeHandler

🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论

🔥🔥🔥(源码 + 调试运行 + 问题答疑)

🔥🔥🔥  有兴趣可以联系我。

我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。 

目录

正文

一、参数处理器的核心职责

二、ParameterHandler接口设计

三、DefaultParameterHandler实现详解

1. 核心数据结构

2. setParameters方法实现

四、parameterObject的多面性

1. 基本类型和简单对象

2. Map类型参数

3. 多个参数(@Param注解)

4. 复杂POJO对象

五、ParamNameResolver解析机制

1. 参数名解析策略

2. 参数值获取方法

六、类型处理器(TypeHandler)的作用

1. TypeHandler接口

2. 类型处理器的选择策略

七、复杂参数处理场景

1. 嵌套参数解析

2. 集合参数处理

3. 动态参数处理

八、性能优化策略

1. 参数缓存

2. 类型处理器缓存

3. 批量处理优化

九、异常处理与调试

1. 详细的错误信息

2. 参数调试支持

十、扩展与自定义

1. 自定义ParameterHandler

2. 插件扩展

十一、总结


  1. MyBatis参数处理器全解析:揭秘PreparedStatement参数绑定的魔法

  2. 手写MyBatis参数处理器:深入理解参数映射与类型转换

  3. MyBatis参数处理机制深度剖析:从ParamNameResolver到TypeHandler

  4. 全面掌握MyBatis参数处理:单一职责设计与多类型参数解析

  5. MyBatis参数处理器实现原理:如何优雅处理各种参数类型

正文

在MyBatis框架中,参数处理器(ParameterHandler)承担着将Java方法参数转换为JDBC PreparedStatement参数的重要职责。这个看似简单的过程背后,隐藏着复杂的设计哲学和精巧的实现机制。本文将深入剖析MyBatis参数处理器的设计原理、实现细节以及各种参数类型的处理策略。

一、参数处理器的核心职责

ParameterHandler在MyBatis架构中扮演着关键角色,其主要职责包括:

  1. 参数解析:从方法参数中提取需要的数据

  2. 类型转换:将Java类型转换为JDBC类型

  3. 参数绑定:将转换后的值设置到PreparedStatement中

  4. 异常处理:处理参数处理过程中可能出现的各种异常

这种单一职责的设计使得参数处理器可以独立演化,也便于测试和维护。

二、ParameterHandler接口设计

MyBatis的参数处理器接口设计简洁而强大:

 public interface ParameterHandler {/*** 获取参数对象*/Object getParameterObject();/*** 设置参数到PreparedStatement中* @param ps PreparedStatement对象* @throws SQLException 如果设置参数时发生SQL异常*/void setParameters(PreparedStatement ps) throws SQLException;}

这种设计体现了接口隔离原则,只暴露必要的方法,隐藏实现细节。

三、DefaultParameterHandler实现详解

1. 核心数据结构
 public class DefaultParameterHandler implements ParameterHandler {private final TypeHandlerRegistry typeHandlerRegistry;private final MappedStatement mappedStatement;private final Object parameterObject;private final BoundSql boundSql;private final Configuration configuration;// 构造函数public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {this.mappedStatement = mappedStatement;this.configuration = mappedStatement.getConfiguration();this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();this.parameterObject = parameterObject;this.boundSql = boundSql;}}
2. setParameters方法实现
@Overridepublic void setParameters(PreparedStatement ps) throws SQLException {// 获取参数映射列表List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings == null) {return;}// 遍历所有参数映射for (int i = 0; i < parameterMappings.size(); i++) {ParameterMapping parameterMapping = parameterMappings.get(i);try {// 解析参数值Object value = getParameterValue(parameterMapping);// 获取类型处理器TypeHandler typeHandler = parameterMapping.getTypeHandler();if (typeHandler == null) {typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.getJavaType());}// 设置参数if (typeHandler == null) {throw new ExecutorException("No TypeHandler found for parameter: " + parameterMapping.getProperty());}typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());} catch (Exception e) {throw new ExecutorException("Error setting parameter at index " + i + ": " + e.getMessage(), e);}}}

四、parameterObject的多面性

parameterObject是参数处理的核心输入,它可能具有多种形式:

1. 基本类型和简单对象
 // 单个基本类型参数User selectById(int id);​// 单个简单对象参数int insertUser(User user);
2. Map类型参数
 // Map参数List<User> selectByCondition(Map<String, Object> params);
3. 多个参数(@Param注解)
 // 多个参数,使用@Param注解List<User> selectByRange(@Param("start") int start, @Param("end") int end);
4. 复杂POJO对象
 // 复杂对象参数int updateUserProfile(@Param("user") User user, @Param("profile") Profile profile);

五、ParamNameResolver解析机制

ParamNameResolver负责解析各种类型的parameterObject,其核心工作原理如下:

1. 参数名解析策略
 
public class ParamNameResolver {private final SortedMap<Integer, String> names;public ParamNameResolver(Configuration config, Method method) {// 解析方法参数final Class<?>[] paramTypes = method.getParameterTypes();final Annotation[][] paramAnnotations = method.getParameterAnnotations();final SortedMap<Integer, String> map = new TreeMap<>();int paramCount = paramTypes.length;for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {String name = null;// 检查@Param注解for (Annotation annotation : paramAnnotations[paramIndex]) {if (annotation instanceof Param) {name = ((Param) annotation).value();break;}}// 没有@Param注解,使用配置的策略if (name == null) {if (config.isUseActualParamName()) {name = getActualParamName(method, paramIndex);} else {name = String.valueOf(map.size());}}map.put(paramIndex, name);}names = Collections.unmodifiableSortedMap(map);}}
2. 参数值获取方法
public Object getNamedParams(Object[] args) {final int paramCount = names.size();if (args == null || paramCount == 0) {return null;} else if (!hasParamAnnotation && paramCount == 1) {// 单个参数,直接返回return args[names.firstKey()];} else {// 多个参数,构建ParamMapfinal Map<String, Object> param = new ParamMap<>();int i = 0;for (Map.Entry<Integer, String> entry : names.entrySet()) {param.put(entry.getValue(), args[entry.getKey()]);// 添加通用的参数名param.put("param" + (i + 1), args[entry.getKey()]);i++;}return param;}
}

六、类型处理器(TypeHandler)的作用

TypeHandler是参数处理中的关键组件,负责Java类型和JDBC类型之间的转换:

1. TypeHandler接口
public interface TypeHandler<T> {// 设置参数void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;// 获取结果T getResult(ResultSet rs, String columnName) throws SQLException;T getResult(ResultSet rs, int columnIndex) throws SQLException;T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
2. 类型处理器的选择策略

在DefaultParameterHandler中,类型处理器的选择遵循以下优先级:

  1. 显式指定:ParameterMapping中明确指定的TypeHandler

  2. Java类型匹配:根据参数的Java类型选择对应的TypeHandler

  3. Jdbc类型匹配:根据JdbcType选择对应的TypeHandler

  4. 默认处理器:使用UnknownTypeHandler作为兜底方案

七、复杂参数处理场景

1. 嵌套参数解析

对于复杂的POJO对象,MyBatis支持使用点号访问嵌套属性:

// SQL中的参数引用
SELECT * FROM users WHERE department.name = #{department.name}// 对应的Java对象
public class User {private Department department;// getter和setter
}public class Department {private String name;// getter和setter
}
2. 集合参数处理

MyBatis对集合参数有特殊处理:

// IN查询中的集合参数
SELECT * FROM users WHERE id IN #{ids}// 对应的Java方法
List<User> selectByIds(@Param("ids") List<Integer> ids);
3. 动态参数处理

对于动态SQL生成的参数,MyBatis需要特殊处理:

// 动态SQL中的参数
<if test="name != null">AND name = #{name}
</if>

八、性能优化策略

参数处理是数据库操作中的性能关键路径,以下是一些优化策略:

1. 参数缓存

对解析后的参数进行缓存,避免重复解析:

public class CachingParamNameResolver extends ParamNameResolver {private final Map<Object, Object> paramCache = new ConcurrentHashMap<>();@Overridepublic Object getNamedParams(Object[] args) {// 使用缓存避免重复计算}
}
2. 类型处理器缓存

缓存常用的类型处理器实例:

public class TypeHandlerRegistry {private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();// 缓存管理方法
}
3. 批量处理优化

对于批量操作,优化参数设置流程:

public class BatchParameterHandler extends DefaultParameterHandler {@Overridepublic void setParameters(PreparedStatement ps) throws SQLException {// 批量处理优化逻辑}
}

九、异常处理与调试

1. 详细的错误信息

在参数处理失败时提供详细的错误信息:

try {typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
} catch (Exception e) {throw new ExecutorException("Error setting parameter '" + parameterMapping.getProperty() + "' at index " + (i + 1) + ". Value: " + value + ", Type: " + (value != null ? value.getClass().getName() : "null"), e);
}
2. 参数调试支持

提供参数调试信息,便于问题排查:

 public class DebugParameterHandler extends DefaultParameterHandler {@Overridepublic void setParameters(PreparedStatement ps) throws SQLException {// 输出参数调试信息log.debug("Setting parameters: {}", getParameterDebugInfo());super.setParameters(ps);}}

十、扩展与自定义

MyBatis的参数处理器支持扩展和自定义:

1. 自定义ParameterHandler
 public class CustomParameterHandler implements ParameterHandler {// 自定义实现}
2. 插件扩展

通过插件机制扩展参数处理功能:

 @Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})})public class ParameterLoggingPlugin implements Interceptor {// 插件实现}

十一、总结

MyBatis的参数处理器是一个设计精巧的组件,它通过ParamNameResolver、TypeHandler等协同工作,实现了对各种参数类型的统一处理。理解参数处理器的工作原理,对于深入掌握MyBatis框架、优化数据库操作性能、处理复杂参数场景都具有重要意义。

在实际开发中,我们应该根据具体需求选择合适的参数传递方式,理解各种参数类型的处理机制,并在必要时进行扩展和优化,从而构建出高效、稳定的数据访问层。


💖学习知识需费心,
📕整理归纳更费神。
🎉源码免费人人喜,
🔥码农福利等你领!

💖常来我家多看看,
📕我是程序员扣棣,
🎉感谢支持常陪伴,
🔥点赞关注别忘记!

💖山高路远坑又深,
📕大军纵横任驰奔,
🎉谁敢横刀立马行?
🔥唯有点赞+关注成!


文章转载自:

http://EZA3VrXZ.Lxmks.cn
http://IMnylxxH.Lxmks.cn
http://t1X3bZSs.Lxmks.cn
http://FIl8o81R.Lxmks.cn
http://etfdXm2t.Lxmks.cn
http://HfZFXldv.Lxmks.cn
http://dJLXuiUc.Lxmks.cn
http://1mF8dGG3.Lxmks.cn
http://q9dA4mQi.Lxmks.cn
http://9wrJ9PeW.Lxmks.cn
http://9WRilwzb.Lxmks.cn
http://ssZtPoZ3.Lxmks.cn
http://MjW05fpS.Lxmks.cn
http://4X6P9DE0.Lxmks.cn
http://F0JQFEWM.Lxmks.cn
http://oD2Fugba.Lxmks.cn
http://6LLwnTfq.Lxmks.cn
http://8B2ZJBdp.Lxmks.cn
http://eTxmiurh.Lxmks.cn
http://110oIo56.Lxmks.cn
http://0UzT7RNt.Lxmks.cn
http://kMIFb04z.Lxmks.cn
http://4c7STsyk.Lxmks.cn
http://dNdQZZYA.Lxmks.cn
http://HhVXf2C1.Lxmks.cn
http://vqa8Jxff.Lxmks.cn
http://ipzoTsOk.Lxmks.cn
http://VNVL2jgT.Lxmks.cn
http://0Gv0I9ZC.Lxmks.cn
http://cb7jWgb8.Lxmks.cn
http://www.dtcms.com/a/384463.html

相关文章:

  • 【Postman】Postman 自动化测试指南:Token 获取与变量管理实战
  • Java 大视界 -- 基于 Java 的大数据可视化在城市交通拥堵治理与出行效率提升中的应用
  • arcgis中实现四色/五色法制图
  • OpenVLA: An Open-Source Vision-Language-Action Model
  • nvm安装node后出现报错: “npm 不是内部或外部命令,也不是可运行的程序 或批处理文件”
  • iPhone 17 系列与 iPhone Air 对比:硬件
  • Serverless Redis实战:阿里云Tair与AWS MemoryDB深度对比
  • 欢迎来到std::shared_ptr的派对!
  • 计算机操作系统学习(四、文件管理)
  • Open3D-Geometry-15:UV Maps 将2D图像投影到3D模型表面
  • 从pip到UV:新一代包管理器的高效替代方案
  • 基于Matlab的雾霾天气和夜间车牌识别系统
  • 【Unity】高性能的事件分发系统
  • BM3D 图像降噪快速算法的 MATLAB 实现
  • 【pycharm】 ubuntu24.04 搭建uv环境
  • 科普:Python 的包管理工具:uv 与 pip
  • Golang语言入门篇002_安装Golang
  • cemu运行塞尔达传说:旷野之息的闪退问题以及解决方案记录
  • 【面试之Redis篇】主从复制原理
  • MySQL 8.0 在 Ubuntu 22.04 中如何将启用方式改为mysql_native_password(密码认证)
  • 轨道交通绝缘监测—轨道交通安全的隐形防线
  • Golang 语言中的函数类型
  • 《投资-54》数字资产的形式有哪些?
  • leetcode41(对称二叉树)
  • 链表详解:(后续会更新)
  • 光谱相机在半导体缺陷检测中的应用
  • 计算机组成原理-第一章
  • 修改 Windows 10 系统更新暂停天数指南
  • Flutter系统亮度检测完全指南:MediaQuery.platformBrightnessOf() 的妙用
  • flutter鸿蒙:适配app_links插件