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

手写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.xdnf.cn/news/19014.html

相关文章:

  • AI赋能前端性能优化:核心技术与实战策略
  • Swift 解法详解 LeetCode 364:嵌套列表加权和 II
  • 713 乘积小于k的子数组
  • git学习 分支管理(branching)合并分支
  • golang13 单元测试
  • Office 2024 长期支持版(Mac中文)Word、Execl、PPT
  • Node.js 多版本管理工具 nvm 的安装与使用教程(含镜像加速与常见坑)
  • 共识算法如何保障网络安全
  • Java全栈开发面试实战:从基础到微服务的深度探索
  • k8s集群Prometheus部署
  • 1 vs 10000:如何用AI智能体与自动化系统,重构传统销售客户管理上限?
  • Wi-Fi数据包发送机制:从物理层到MAC层的深度解析
  • 记录使用ruoyi-flowable开发部署中出现的问题以及解决方法(二)
  • 贴片式TE卡 +北京君正+Rk瑞芯微的应用
  • 直线拟合方法全景解析:最小二乘、正交回归与 RANSAC
  • Transformer实战(15)——使用PyTorch微调Transformer语言模型
  • 了解迁移学习吗?大模型中是怎么运用迁移学习的?
  • 达梦数据库配置文件-COMPATIBLE_MODE
  • 数据结构青铜到王者第七话---队列(Queue)
  • 《websocketpp使用指北》
  • ModuleNotFoundError: No module named ‘dbgpt_app‘
  • Python音频分析与线性回归:探索声音中的数学之美
  • 学习游戏制作记录(存档点和丢失货币的保存以及敌人的货币掉落)8.27
  • 计算机网络——DNS,ARP,RARP,DHCP,ICMP
  • Marin说PCB之包地间距对GMSL2信号阻抗的影响分析--01
  • 【图像算法 - 25】基于深度学习 YOLOv11 与 OpenCV 实现人员跌倒识别系统(人体姿态估计版本)
  • 学习 Android (十七) 学习 OpenCV (二)
  • string::erase
  • Prometheus+Grafana监控安装及配置
  • Python 并行计算进阶:ProcessPoolExecutor 处理 CPU 密集型任务