手写MyBatis第37弹: 深入MyBatis MapperProxy:揭秘SQL命令类型与动态方法调用的完美适配

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

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

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

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

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

目录

一、MyBatis架构回顾与MapperProxy定位

二、SqlSession的多样化设计:为什么需要多种查询方法?

1. 返回结果类型的多样性

2. 性能优化的考虑

3. API设计的清晰性

三、MapperProxy的动态适配机制

1. SQL命令类型识别

2. 返回类型分析与选择策略

3. 参数处理与传递

四、高级特性与优化策略

1. 批量操作的特殊处理

2. 结果处理器集成

3. 缓存策略与延迟加载

五、实战:自定义MapperProxy扩展

1. 性能监控增强

2. 自动重试机制

六、总结与最佳实践


在现代Java开发中,MyBatis作为一款优秀的持久层框架,以其灵活的SQL映射和简洁的API设计深受开发者喜爱。本文将深入剖析MyBatis核心组件之一的MapperProxy,探讨它是如何根据SQL命令类型动态适配SqlSession的CRUD方法,实现Mapper接口方法与数据库操作的无缝对接。

一、MyBatis架构回顾与MapperProxy定位

在深入了解MapperProxy之前,让我们先简要回顾MyBatis的核心架构。MyBatis通过SqlSession提供数据库操作API,而Mapper接口则定义了这些操作的方法签名。MapperProxy作为连接这两者的桥梁,实现了Java动态代理模式,负责将接口方法调用转化为具体的数据库操作。

 public class MapperProxy<T> implements InvocationHandler {private final SqlSession sqlSession;private final Class<T> mapperInterface;@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 方法调用处理逻辑}}

二、SqlSession的多样化设计:为什么需要多种查询方法?

SqlSession提供了selectOneselectListselectMap等多种查询方法,这种设计并非偶然,而是为了满足不同场景下的数据检索需求:

1. 返回结果类型的多样性

  • selectOne:返回单个对象,适用于查询唯一结果场景

  • selectList:返回对象列表,适用于多结果查询

  • selectMap:返回键值对映射,便于基于特定字段快速查找

2. 性能优化的考虑

不同的方法内部实现针对特定场景进行了优化,比如selectOne在检测到多个结果时会抛出异常,避免意外的数据覆盖。

3. API设计的清晰性

明确的方法命名使得代码更易读和维护,开发者能够直观地理解每个方法的用途。

三、MapperProxy的动态适配机制

MapperProxy的核心职责是根据Mapper接口方法的特征,动态选择适当的SqlSession方法。这一过程涉及多个关键判断:

1. SQL命令类型识别

通过MappedStatement获取sqlCommandType,这是决定调用哪个SqlSession方法的首要因素:

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();Class<?> declaringClass = method.getDeclaringClass();String statementId = declaringClass.getName() + "." + methodName;MappedStatement ms = configuration.getMappedStatement(statementId);SqlCommandType sqlCommandType = ms.getSqlCommandType();switch (sqlCommandType) {case SELECT:// 处理查询操作break;case INSERT:return sqlSession.insert(statementId, args);case UPDATE:return sqlSession.update(statementId, args);case DELETE:return sqlSession.delete(statementId, args);default:throw new RuntimeException("Unknown execution method for: " + statementId);}}

2. 返回类型分析与选择策略

对于SELECT操作,需要进一步分析方法的返回类型:

 private Object handleSelect(Method method, String statementId, Object[] args) {Class<?> returnType = method.getReturnType();if (List.class.isAssignableFrom(returnType)) {return sqlSession.selectList(statementId, args);} else if (Map.class.isAssignableFrom(returnType)) {// 处理Map返回类型,可能需要额外的key配置return sqlSession.selectMap(statementId, args, method.getAnnotation(MapKey.class).value());} else if (returnType.isArray()) {// 数组类型处理List<?> result = sqlSession.selectList(statementId, args);return convertToArray(result, returnType.getComponentType());} else {// 默认为selectOne,但需要验证结果数量List<?> result = sqlSession.selectList(statementId, args);if (result.size() == 1) {return result.get(0);} else if (result.size() > 1) {throw new TooManyResultsException("Expected one result (or null) but found: " + result.size());} else {return null;}}}

3. 参数处理与传递

MapperProxy还需要正确处理方法的参数,支持多种参数传递方式:

private Object wrapParameters(Object[] args) {if (args == null || args.length == 0) {return null;} else if (args.length == 1) {return args[0];} else {// 多参数处理,可封装为Map或使用@Param注解Map<String, Object> paramMap = new HashMap<>();for (int i = 0; i < args.length; i++) {paramMap.put("param" + (i + 1), args[i]);}return paramMap;}}

四、高级特性与优化策略

1. 批量操作的特殊处理

对于批量操作,MyBatis提供了特殊的API和优化策略:

 case BATCH:// 批量操作处理if (!sqlSession.isBatching()) {sqlSession.startBatch();}return method.invoke(this, args);

2. 结果处理器集成

支持自定义结果处理器,增强结果集处理的灵活性:

 if (method.isAnnotationPresent(ResultHandler.class)) {ResultHandler<?> handler = (ResultHandler<?>) args[args.length - 1];return sqlSession.select(statementId, wrapParameters(args), handler);}

3. 缓存策略与延迟加载

MapperProxy还需要与MyBatis的缓存机制和延迟加载功能协同工作:

 if (ms.getCache() != null && ms.isUseCache()) {// 缓存命中逻辑Object cached = cache.getObject(statementId);if (cached != null) {return cached;}}

五、实战:自定义MapperProxy扩展

通过扩展MapperProxy,我们可以实现一些高级功能:

1. 性能监控增强

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long startTime = System.currentTimeMillis();try {return super.invoke(proxy, method, args);} finally {long cost = System.currentTimeMillis() - startTime;log.debug("Method {} executed in {} ms", method.getName(), cost);}}

2. 自动重试机制

for (int retry = 0; retry < maxRetries; retry++) {try {return super.invoke(proxy, method, args);} catch (SQLException e) {if (isRetryableException(e) && retry < maxRetries - 1) {continue;}throw e;}
}

六、总结与最佳实践

MapperProxy作为MyBatis的核心组件,通过动态代理技术实现了Mapper接口方法与SqlSessionCRUD操作的无缝对接。其设计巧妙之处在于:

  1. 职责分离:将SQL命令类型判断与具体执行分离,符合单一职责原则

  2. 灵活适配:根据返回类型自动选择最合适的查询方法

  3. 扩展性强:通过配置和扩展支持各种复杂场景

在实际开发中,理解MapperProxy的工作原理有助于:

  • 更好地调试Mapper接口相关问题

  • 根据业务需求选择合适的返回类型

  • 优化数据库操作性能

  • 实现自定义的数据访问逻辑

通过深入理解MapperProxy的设计思想和实现机制,我们不仅能够更好地使用MyBatis,还能够在遇到复杂业务场景时做出更合理的技术决策。


进一步学习建议

  1. 阅读MyBatis官方文档中关于Mapper接口和SqlSession的部分

  2. 深入研究Java动态代理机制

  3. 探索MyBatis-Spring中MapperScannerConfigurer的工作原理

  4. 了解MyBatis的插件开发,实现自定义的拦截器功能

希望本文能够帮助您深入理解MyBatis的核心机制,并在实际项目中更加得心应手地使用这一优秀的持久层框架。


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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/920271.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/920271.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GD32VW553-IOT 测评和vscode开发环境搭建

GD32VW553-IOT 测评和vscode开发环境搭建 1. 背景介绍 iCEasy商城的产品, Firefly Workshop 萤火工厂的样片, 是一款基于GD32VW553 MCU的开源硬件, 这款MCU内置了32bit的RISC-V内核, 支持双模无线WIFI-6和BLE-5.2, 最高主频可达160Mhz. 本人曾在公司参与开发了一款基于RISC-V内…

斯塔克工业技术日志:用基础模型打造 “战甲级” 结构化 AI 功能

引子 在斯塔克工业的地下研发实验室里&#xff0c;弧光反应堆的蓝光映照着布满代码的显示屏&#xff0c;工程师詹姆斯・“罗迪”・罗德斯正对着一堆 AI 生成的杂乱食谱皱眉。 上周他刚搞定基础模型&#xff08;Foundation Models&#xff09;的文本生成&#xff0c;让 AI 能像…

如何解决pip安装报错ModuleNotFoundError: No module named ‘click’问题

【Python系列Bug修复PyCharm控制台pip install报错】如何解决pip安装报错ModuleNotFoundError: No module named ‘click’问题 摘要 在日常Python开发中&#xff0c;pip install 报错 ModuleNotFoundError: No module named click 是一个非常常见的问题&#xff0c;尤其是在…

PLC_博图系列☞基本指令”S_PULSE:分配脉冲定时器参数并启动“

PLC_博图系列☞基本指令”S_PULSE&#xff1a;分配脉冲定时器参数并启动“ 文章目录PLC_博图系列☞基本指令”S_PULSE&#xff1a;分配脉冲定时器参数并启动“背景介绍S_PULSE&#xff1a; 分配脉冲定时器参数并启动说明参数脉冲时序图示例关键字&#xff1a; PLC、 西门子、 …

【大模型】Qwen2.5-VL-3B模型量化以及运行测试,保留多模态能力(实践版)

目录 ■获取原始模型 ■构建llama.cpp ■转换模型到GGUF ▲视觉模块转换 ▲llm模块转换 ▲llm模块量化 ▲推理测试 ■报错处理 以下是几种多模态模型量化方案的简要对比: 特性 llama.cpp GGUF 量化

C语言 | 高级C语言面试题

侧重于内存管理、指针、编译器行为、底层原理和编程实践。 C语言面试 一、核心概念与深度指针题 1. `const` 关键字的深度理解 2. volatile 关键字的作用 3. 复杂声明解析 二、内存管理 4. `malloc(0)` 的行为 5. 结构体内存对齐与大小计算 三、高级技巧与底层原理 6. setjmp()…

【deepseek问答记录】:chatGPT的参数数量和上下文长度有关系吗?

这是一个非常好的问题&#xff0c;它触及了大型语言模型设计的核心。 简单来说&#xff1a;参数数量和上下文长度在技术上是两个独立的概念&#xff0c;但在模型的设计、训练和实际应用中&#xff0c;它们存在着深刻且重要的联系。 我们可以从以下几个层面来理解它们的关系&…

5GNR CSI反馈 TypeI码本

5GNR CSI反馈 TypeI码本 前言 最近孬孬在学习5gnr中的CSI反馈内容&#xff0c;对于目前的5GNR主要是基于码本的隐式反馈机制&#xff0c;在NR中主要是分为 TypeI 和 TypeII&#xff0c;对于TypeI是用于常规精度的&#xff0c;对于TypeII更为复杂&#xff0c;更多的适用于多用户…

使用appium对安卓(使用夜神模拟器)运行自动化测试

环境安装 基本环境安装 安装node.js 下载地址&#xff1a;Node.js — Run JavaScript Everywhere 安装Java JDK 下载地址&#xff1a;JDK Builds from Oracle 安装夜神模拟器 360上找下就能装&#xff0c;安装好后将夜神的bin目录&#xff0c;添加到系统变量的path中。 …

用wp_trim_words函数实现WordPress截断部分内容并保持英文单词完整性

在WordPress中&#xff0c;wp_trim_words函数用于截断字符串并限制单词数量。如果你希望在截断时保持单词的完整性&#xff08;让单词显示全&#xff09;&#xff0c;可以通过自定义函数来实现。 以下是一个示例代码&#xff0c;展示如何修改你的代码以确保截断时显示完整的单…

Codeforces Round 1042 (Div. 3) G Wafu! 题解

Codeforces Round 1042 (Div. 3) G Wafu! 题解 题意&#xff1a;每一次操作删除集合中最小的元素 x&#xff0c;并产生新的 x - 1 个元素值分别为 1 2 3 … x - 1 放入集合之中。 每次操作一个数 x 可以使得最终答案乘上 x&#xff0c;问我们操作 k 次在模 1e9 7 的基础上最终…

APP与WEB测试的区别?

web与app核心区别&#xff1a;一个基于浏览器 &#xff0c;一个基于操作系统这是所有区别的根源&#xff1a;Web测试&#xff1a;测试对象是网站&#xff0c;通过浏览器(Chrome,Firefox等)访问&#xff0c;运行环境核心是浏览器引擎&#xff1b;App测试&#xff1a;测试对象是应…

2.渗透-.WEB运行原理-ZBlog安装(进一步理解数据库)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;微尘网校 上一个内容&#xff1a;1.渗透-.WEB运行原理&#xff08;搭建一个WEB程序&#xff09; 首先把服务运行起来 然后访问下图红框…

MapBox GL地图上绘制圆形区域,在区域中心点添加标记点及文本提示的实现方法

MapBox GL地图上绘制圆形区域&#xff0c;在区域中心点添加标记点及文本提示的实现方法&#xff1a;// 绘制影响区域 const addArea (circle) > {if (!map.current || !circle) return;const areaId circle-area;const epicenterId circle-epicenter;const radiusKm cir…

基于 Docker Compose 的若依多服务一键部署java项目实践

基于Docker Compose的若依多服务一键部署实践 在项目开发中&#xff0c;多服务部署常常让人头疼。环境配置复杂、操作步骤繁琐&#xff0c;稍不注意就容易出错。不过&#xff0c;有了 Docker Compose &#xff0c;这些问题就简单多啦&#xff01;它能帮我们高效编排多个容器&am…

MyBatis-Plus 使用 Wrapper 自定义 SQL 查询

目录 1. 注意事项 2. 示例代码 2.1 实体类 2.2 Mapper 接口 2.3 测试类 3. 运行效果 4. 总结 在实际项目中&#xff0c;虽然 MyBatis-Plus 提供了丰富的内置方法和 QueryWrapper 条件构造器&#xff0c;但有时我们需要 自定义 SQL 来实现更复杂的查询逻辑。 MyBatis-Plu…

NumPy/PyTorch/C char数组内存排布

1. 关于 np.random.randn(2, 3) 的数据存储数据类型 (Data Type)&#xff1a;np.random.randn 默认生成的是 64位&#xff08;8字节&#xff09;双精度浮点数 (numpy.float64)。所以每个数字占 8个字节&#xff0c;而不是8位&#xff08;1字节&#xff09;。这是一个关键区别。…

Elasticsearch精准匹配与全文检索对比

在 Elasticsearch 中&#xff0c;精准匹配检索和全文检索匹配检索是两种核心查询方式&#xff0c;主要区别在于匹配规则、分词处理、适用场景和底层实现逻辑。以下是详细对比&#xff1a;一、核心区别总结特性精准匹配&#xff08;Term Query&#xff09;全文检索&#xff08;M…

【鸿蒙开发001】上下翻页-翻书效果实现【可复用】

先看效果&#xff1a;一、设计思路&#xff1a;根据所需要的最终效果&#xff0c;最终设计如下&#xff1a;&#xff08;1&#xff09;整体设计了4个模块&#xff0c;这里分别标记为&#xff1a;A1&#xff0c;A2&#xff0c;B1&#xff0c;B2。具体说明如下&#xff1a;A模块&…

H20 性能表现之 Qwen3-235B

上期为大家分享了H20性能表现之Qwen3-Coder-480B&#xff08;以下称480B&#xff09;&#xff0c;今天&#xff0c;我为大家继续带来新的评测&#xff0c;这次&#xff0c;介绍的是 Qwen3-235B-A22B-Instruct-2507&#xff08;以下称235B&#xff09;&#xff0c;这也是阿里这阵…