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

手写MyBatis第40弹:手写MyBatis框架阶段性总结,你的ORM框架已达生产级雏形

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

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

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

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

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

目录

一、当前框架状态:我们实现了什么?

二、核心组件回顾:齿轮如何啮合?

三、架构演进思考:下一步迈向何方?

四、总结


  1. 从零构建MyBatis:我们的轮子已经能跑了!核心架构全解析与未来展望

  2. 手写MyBatis完结篇?不,是新征程的起点:回顾CRUD实现,展望插件与缓存

  3. 深入理解MyBatis架构:通过手写实践,彻底掌握其核心组件的职责与协作

  4. 造轮子的艺术:手写MyBatis框架阶段性总结,你的ORM框架已达生产级雏形


在经历了前面数篇从解析配置、创建代理、执行SQL到处理结果集的深入剖析与实践后,我们亲手打造的“迷你MyBatis”框架已经完成了它的第一次蜕变。它不再是一个简单的概念验证(Proof of Concept),而是一个具备了完整CRUD操作能力的ORM框架雏形。此刻,让我们暂时停下编码的脚步,站在一个更高的视角来审视我们的作品,回顾其核心架构与数据流,并思考它未来的演进方向。

一、当前框架状态:我们实现了什么?

我们的框架已经支持了两种经典的数据操作方式,覆盖了完整的CRUD功能:

  1. 优雅的Mapper代理接口方式(面向接口编程): 这是MyBatis推荐的用法。用户只需定义一个Java接口,我们的框架通过动态代理技术(MapperProxy)自动生成其实现。开发者获得了完全的类型安全性和IDE的支持,体验如同调用本地方法一样自然。

     UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = userMapper.selectUserById(1);userMapper.updateUser(user);
  2. 灵活的直接SqlSession API方式: 这种方式提供了更直接的控制力。用户通过传入Statement ID(通常是namespace.id的形式)和参数来执行操作,虽然牺牲了一些类型安全,但在某些需要动态构建SQL的场景下更为灵活。

     User user = sqlSession.selectOne("com.example.mapper.UserMapper.selectUserById", 1);int count = sqlSession.update("com.example.mapper.UserMapper.updateUser", user);

这两种方式虽然在入口上有所不同,但最终都汇入了同一套核心执行流程,共享着相同的底层组件。

二、核心组件回顾:齿轮如何啮合?

我们的框架之所以能够运转起来,全靠内部各个核心组件各司其职、精密协作。它们共同构成了框架的骨架与神经。

  • SqlSessionFactoryBuilder: 工厂的建造者。

    • 职责: 生命周期始于配置解析,终于工厂创建。它读取XML配置文件或代码配置,构建出庞大的Configuration对象,并以此创建唯一的SqlSessionFactory,功成身退。

  • SqlSessionFactory: 会话的工厂。

    • 职责: 作为应用程序生命周期内的单例,是生成SqlSession的“车间”。它持有全局唯一的Configuration对象,根据执行器类型(SIMPLE, BATCH等)创建相应的SqlSession

  • SqlSession: 面向用户的API门户。

    • 职责: 是用户与框架交互的主要接口,代表了一次数据库会话。它封装了增删改查等所有数据库操作方法,其内部将工作委托给Executor执行。同时,它还管理着事务的提交与回滚。

  • Executor: SQL执行的大脑。

    • 职责: 是真正的执行调度中心。它定义了queryupdate等核心方法。我们实现了两种策略:

      • SimpleExecutor:最简单的实现,每次执行都会创建一个新的PreparedStatement

      • BatchExecutor:用于批处理,会将多个操作缓存起来,最后统一执行,极大提升性能。

    • 它协调StatementHandlerParameterHandlerResultSetHandler共同完成一次数据库操作。

  • MapperProxy: 动态代理的魔术师。

    • 职责: 是JDK动态代理的InvocationHandler实现。当调用Mapper接口方法时,它会拦截调用,将方法名、参数等信息转换为对底层SqlSession方法的调用(如将selectUserById转换为sqlSession.selectOne(...))。

  • MappedStatement: SQL指令的封装。

    • 职责: 是映射文件(或注解)中一条SQL语句(如<select>)的内存等价物。它被存储在Configuration中,包含了SQL源码、执行类型、参数类型、结果类型等所有关键信息,是执行操作的“作战计划”。

  • Configuration: 全局配置的仓库。

    • 职责: 框架的“宇宙中心”,一个贯穿始终的核心对象。它包含了所有的配置信息:数据库环境、Mapper注册表、所有MappedStatement、类型处理器等,是框架的全局上下文。

  • ParameterHandler & ResultSetHandler & TypeHandler: 数据处理的工匠。

    • 职责: 这三个组件共同负责Java世界与数据库世界的数据类型转换。

    • ParameterHandler:负责将JavaBean参数设置到PreparedStatement的占位符?上。

    • ResultSetHandler:负责将JDBC返回的ResultSet结果集转换为我们想要的Java对象(List、Map或单个POJO)。

    • TypeHandler:是前两者的基础,负责单个Java类型与JDBC类型之间的相互转换。

三、架构演进思考:下一步迈向何方?

一个基本可用的框架已经建成,但这远非终点。要将其打造成一个生产级可用的成熟框架,我们还需要考虑以下几个关键方向的扩展:

  1. 扩展性:插件机制(Interceptor)

    • 思考: 如何在不修改核心代码的情况下,允许用户在任何一次数据库操作的关键节点(如执行SQL前、处理参数后、返回结果前)插入自定义逻辑?这就需要设计一个强大的插件拦截机制,这是MyBatis如此灵活的灵魂所在。

  2. 性能:缓存(Cache)与连接池(Pool)

    • 思考: 如何避免重复查询?引入一级缓存(SqlSession级别)二级缓存(Mapper/Namespace级别) 是必然选择。同时,数据库连接的创建和销毁是昂贵的,集成成熟的数据库连接池(如HikariCP, Druid)来管理连接资源,是提升性能和生产环境健壮性的必备条件。

  3. 健壮性:事务管理(Transaction)

    • 思考: 我们虽然引入了Transaction接口解耦了连接管理,但事务的传播行为(如REQUIRED, REQUIRES_NEW)、隔离级别等高级特性还需要进一步设计。一个健壮的事务管理器是保证数据一致性的基石。

  4. 灵活性:动态SQL(Dynamic SQL)

    • 思考: 目前我们的SQL是静态的。但在实际业务中,SQL往往需要根据参数条件动态变化(if, where, foreach等)。实现一套动态SQL功能,能够极大解放生产力,避免在Java代码中拼接SQL字符串的繁琐与危险。

四、总结

通过这一系列的手写实践,我们不仅仅是在“造轮子”,更是在进行一场深入的MyBatis源码之旅。我们亲手实现了它的核心脉络,对其中每一个组件的职责、每一次数据的流动都有了刻骨铭心的理解。

这个我们亲手搭建的框架,虽然简陋,但已然具备了现代ORM框架的核心骨架。它是对接口驱动、反射、动态代理、设计模式(如建造者、工厂、代理、模板方法)、资源管理、事务控制等一系列Java高级编程技术的综合运用与深刻体现。

回顾来时路,繁花已似锦;展望未来途,星晨正可期。接下来,就让我们带着这些思考,继续深入,为我们的框架注入插件、缓存、事务的灵魂,让它变得更加强大和优雅。征程,还在继续。


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

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

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

http://www.dtcms.com/a/357320.html

相关文章:

  • 【nvidia-B200】生产报错common.h:14:10: fatal error: mpi.h: No such file or directory
  • (论文速读)RADIOv2.5:聚合式视觉基础模型
  • 美摄科技受邀参加2025中关村论坛年会,以超高清车载影像技术赋能智慧出行新体验!
  • 【报错】RuntimeError: HIP error: invalid device function
  • Python计算点云的均值、方差、标准差、凸点(顶点)、质心和去中心化
  • week5-[二维数组]对角线
  • idea2025.1.5安装+pj
  • 计算机视觉第一课opencv(四)保姆级教学
  • 构建稳定和可扩展云基础设施的首选服务:AWS的EC2实例
  • 【三维渲染技术讨论】Blender输出的三维文件里的透明贴图在Isaac Sim里会丢失, 是什么原因?
  • 2024年09月 Python(四级)真题解析#中国电子学会#全国青少年软件编程等级考试
  • JVM_JMM
  • Java ThreadLocal为什么要用弱引用
  • Vue2 和 Vue3 里的防抖:简单说清楚怎么用
  • 【C语言入门级教学】sizeof和strlen的对⽐
  • 数据存储——数据库
  • 并发编程——07 深入理解AQS之独占锁ReentrantLock源码分析
  • 编程设计模式
  • 【系列02】端侧AI:构建与部署高效的本地化AI模型 第1章:为什么是端侧AI?
  • 【LINUX】常用基本指令(1)
  • go 使用rabbitMQ
  • 神经网络|(十六)概率论基础知识-伽马函数·中
  • Hugging Face入门指南:AI创客的数字游乐场
  • 解析json
  • LeetCode 142.环形链表 II
  • 【前端教程】JavaScript 数组对象遍历与数据展示实战
  • 动态规划01背包
  • 解锁Libvio访问异常:从故障到修复的全攻略
  • 从“Where”到“Where + What”:语义多目标跟踪(SMOT)全面解读
  • C# 日志写入loki