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

手写MyBatis第16弹:泛型魔法应用:MyBatis如何破解List的运行时类型

结果映射的起点:MyBatis如何预知你的返回类型

  1. 《MyBatis未卜先知?解析Mapper方法返回类型的黑科技》

  2. 《从List到POJO:MyBatis结果集类型推断全揭秘》

  3. 《手写MyBatis结果映射第一步:捕获方法返回类型》

  4. 《泛型魔法:MyBatis如何破解List的运行时类型》

  5. 《返回类型决定结果处理:MyBatis的ORM类型系统设计》


结果映射的起点:MyBatis如何预知你的返回类型

引言: 当MyBatis执行selectById方法时,它如何预知应该将结果集转换为User对象还是List?本文将深入解析MyBatis获取方法返回类型的核心机制,以及这对后续结果处理的关键影响。


一、返回类型获取的两种方式

1. 基础类型获取
 // 获取简单返回类型Class<?> returnType = method.getReturnType();​// 示例方法:User selectById(int id);// returnType == User.class
2. 泛型类型解析
 // 获取泛型返回类型Type genericReturnType = method.getGenericReturnType();​// 示例方法:List<User> selectAll();if (genericReturnType instanceof ParameterizedType) {ParameterizedType pt = (ParameterizedType) genericReturnType;Type[] actualTypeArgs = pt.getActualTypeArguments();Class<?> elementType = (Class<?>) actualTypeArgs[0];// elementType == User.class}

二、MappedStatement的类型存储

1. 返回类型封装
public class MappedStatement {private Class<?> resultType;private Class<?> resultMapClass; // 复杂映射private boolean returnsList;public static class Builder {public Builder(Method method) {// 解析返回类型Type returnType = method.getGenericReturnType();// 处理集合类型if (returnType instanceof ParameterizedType && List.class.isAssignableFrom(method.getReturnType())) {this.returnsList = true;Type elementType = ((ParameterizedType)returnType).getActualTypeArguments()[0];this.resultType = (Class<?>) elementType;} // 处理普通类型else {this.resultType = method.getReturnType();}}}}
2. 类型推断流程图


三、六类返回类型的处理策略

返回类型处理方式示例特殊说明
void不处理结果集void deleteById()通常用于更新操作
基本类型单行单列int count()自动拆箱处理
POJO属性映射User selectById()最常见场景
List多行映射List<User> selectAll()自动识别元素类型
Map列名-值对Map<String,Object> selectAsMap()键为列名/别名
Cursor流式处理Cursor<User> scroll()需要手动关闭

四、泛型类型解析的三种技巧

1. 常规泛型解析
 // 解析List<User>Type returnType = method.getGenericReturnType();if (returnType instanceof ParameterizedType) {ParameterizedType pt = (ParameterizedType) returnType;Type rawType = pt.getRawType(); // List.classType[] typeArgs = pt.getActualTypeArguments(); // [User.class]}
2. 嵌套泛型处理
 // 解析Map<String,List<User>>if (typeArgs[1] instanceof ParameterizedType) {ParameterizedType nestedPt = (ParameterizedType) typeArgs[1];Type[] nestedArgs = nestedPt.getActualTypeArguments();}
3. 类型变量处理
// 处理泛型方法<T> T selectById()if (returnType instanceof TypeVariable) {// 通常需要从参数或注解获取实际类型returnType = getActualTypeFromAnnotation(method);}

五、ResultSet元数据的关键作用

元数据与类型系统的协作

元数据的三重价值:
  1. 列名匹配:将user_name列映射到userName字段

  2. 类型转换:将SQL的TIMESTAMP转为Java的Date

  3. 结果验证:检查返回列数与对象属性匹配情况


六、ResultHandler的扩展能力

自定义结果处理示例
public interface ResultHandler<T> {void handleResult(ResultContext<? extends T> resultContext);}​// 示例:批量处理结果public class BatchResultHandler implements ResultHandler<User> {private final List<User> batch = new ArrayList<>();@Overridepublic void handleResult(ResultContext<? extends User> context) {batch.add(context.getResultObject());if (batch.size() >= 1000) {processBatch(batch);batch.clear();}}}
使用场景对比
场景直接返回ResultHandler
大数据集内存压力大流式处理
复杂转换需二次处理实时转换
过程监控无法感知获取处理统计

七、企业级实践建议

1. 安全类型检查
 public void validateReturnType(Method method) {Class<?> type = method.getReturnType();if (Void.TYPE.equals(type) && isSelectStatement(method)) {throw new RuntimeException("查询方法不应返回void");}}
2. 智能类型推导
 public Class<?> inferElementType(Method method) {// 从@ResultType注解获取ResultType anno = method.getAnnotation(ResultType.class);if (anno != null) return anno.value();// 从方法参数推导if (hasTypeParameter(method)) {return extractFromGenericSignature(method);}// 默认Objectreturn Object.class;}
3. 性能优化缓存
 private static final Map<Method, Class<?>> RETURN_TYPE_CACHE = new ConcurrentHashMap<>();​public Class<?> getCachedReturnType(Method method) {return RETURN_TYPE_CACHE.computeIfAbsent(method, m -> {Type type = m.getGenericReturnType();return parseActualType(type);});}

结语: 返回类型的准确获取是MyBatis结果映射的基石,从简单的POJO到复杂的泛型集合,类型系统的完善设计保证了ORM转换的精确性。理解这一机制,不仅能更好地使用MyBatis,也为实现自定义结果处理器打下了坚实基础。

http://www.lryc.cn/news/620470.html

相关文章:

  • C++ 应用场景全景解析:从系统级到AI的跨越式演进
  • 分布式系统架构设计模式:从微服务到云原生
  • 河南萌新联赛2025第(五)场:信息工程大学”(补题)
  • DataHub OPC Gateway:实现OPC UA与OPC DA无缝集成的高性能网关
  • iOS App TF上架全流程实战 高效内测分发与IPA包管理
  • Boost库中Pool 基础内存池(boost::pool<>)的详细用法解析和实战应用
  • Docker 核心技术:Namespace
  • 版本更新!FairGuard-Mac加固工具已上线!
  • 银河麒麟系统部署oceanbase社区版
  • 【入门级-C++程序设计:13、STL 模板:栈(stack)、队 列(queue)、 链 表(list)、 向 量(vector) 等容器】
  • 中介者模式和观察者模式的区别是什么
  • mysql——count(*)、count(1)和count(字段)谁更快?有什么区别?
  • 【React】hooks 中的闭包陷阱
  • 某处卖600的【独角仙】尾盘十分钟短线 尾盘短线思路 手机电脑通用无未来函数
  • coze小白-如何用coze上传本地文件?(对话流使用)
  • 《SeeClick: Harnessing GUI Grounding for Advanced Visual GUI Agents》论文精读笔记
  • 云原生俱乐部-k8s知识点归纳(1)
  • 同创永益 IStorM CNBR云原生业务韧性管理平台 v3.3.0重磅发布:告别备份烦恼,云原生数据保护再升级!
  • 【博客系统测试报告】---接口自动化测试
  • toRefs、storeToRefs实际应用
  • 图书商城小程序怎么做?实体书店如何在微信小程序上卖书?
  • 机器学习 - Kaggle项目实践(3)Digit Recognizer 手写数字识别
  • 20道HTML相关前端面试题及答案
  • 如何通过WiFi将文件从安卓设备传输到电脑
  • 点图:数据分布的可视化利器
  • PostgreSQL——视图
  • 读书笔记:《我看见的世界》
  • 为什么Integer缓存-128 ~ 127
  • 【Linux学习|黑马笔记|Day4】IP地址、主机名、网络请求、下载、端口、进程管理、主机状态监控、环境变量、文件的上传和下载、压缩和解压
  • 编排之神-Kubernetes微服务专题--ingress-nginx及金丝雀Canary的演练