手写MyBatis第31弹-用工厂模式重构MyBatis的SqlSession创建过程

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

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

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

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

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

  1. MyBatis核心工厂揭秘:SqlSessionFactory如何打造高效的数据库会话生产线

  2. 设计模式之美:用工厂模式重构MyBatis的SqlSession创建过程

  3. 深入MyBatis源码:解析SqlSessionFactory的重量级身份与创建逻辑

  4. 手写MyBatis工厂层:打造可配置化的SqlSession生产体系

  5. 架构师视角:为什么SqlSessionFactory是MyBatis中最重量级的组件?


正文

在前两篇文章中,我们实现了SqlSession的CRUD方法并重构了MapperProxy,建立了清晰的执行链路。然而,一个关键问题尚未解决:SqlSession实例应该如何被创建和管理?今天,我们将引入MyBatis架构中的另一个核心组件------SqlSessionFactory,通过工厂模式来优雅地解决这个问题。

一、为何需要SqlSessionFactory:创建逻辑的封装与复用

直接使用new DefaultSqlSession(configuration, executor)来创建会话虽然可行,但存在诸多问题:

  1. 创建逻辑复杂 :一个完整的SqlSession创建需要组装ConfigurationExecutor以及可能的事务管理器和连接对象,这些细节不应该暴露给客户端代码。

  2. 配置一致性 :确保所有SqlSession实例使用相同的配置基础,避免因配置不一致导致的难以排查的问题。

  3. 资源管理 :集中管理Executor的创建和配置,便于实现执行器类型的可配置化。

  4. 扩展性 :为未来支持不同类型的SqlSession(如批处理会话、管理型会话)预留扩展点。

工厂模式正是解决这些问题的利器。它通过提供一个专门的工厂类来封装对象的创建逻辑,让客户端无需关心具体的创建细节。

二、SqlSessionFactory的架构设计与实现

让我们通过一个架构图来全面了解SqlSessionFactory在整体框架中的位置和作用:

从上图可以看出,SqlSessionFactory作为整个框架的配置中心和生产中心,处于承上启下的关键位置。

第一步:定义SqlSessionFactory接口

java 复制代码
public interface SqlSessionFactory {
     
     /**
      * 打开一个SqlSession,使用默认的配置
      */
     SqlSession openSession();
     
     /**
      * 指定是否自动提交事务
      */
     SqlSession openSession(boolean autoCommit);
     
     /**
      * 指定连接对象,用于与现有事务集成
      */
     SqlSession openSession(Connection connection);
     
     /**
      * 指定事务隔离级别
      */
     SqlSession openSession(TransactionIsolationLevel level);
     
     /**
      * 获取配置信息
      */
     Configuration getConfiguration();
 }

第二步:实现DefaultSqlSessionFactory

java 复制代码
 public class DefaultSqlSessionFactory implements SqlSessionFactory {
     
     private final Configuration configuration;
     
     public DefaultSqlSessionFactory(Configuration configuration) {
         this.configuration = configuration;
     }
     
     @Override
     public SqlSession openSession() {
         return openSession(false); // 默认非自动提交
     }
     
     @Override
     public SqlSession openSession(boolean autoCommit) {
         // 创建事务
         Transaction tx = null;
         try {
             Environment environment = configuration.getEnvironment();
             TransactionFactory transactionFactory = environment.getTransactionFactory();
             tx = transactionFactory.newTransaction(environment.getDataSource(), autoCommit);
             
             // 创建执行器
             Executor executor = new SimpleExecutor(configuration, tx);
             
             // 创建SqlSession
             return new DefaultSqlSession(configuration, executor);
         } catch (Exception e) {
             // 关闭事务如果创建失败
             if (tx != null) {
                 tx.close();
             }
             throw new RuntimeException("Error opening session. Cause: " + e);
         }
     }
     
     @Override
     public SqlSession openSession(Connection connection) {
         // 基于现有连接创建事务和执行器
         Transaction tx = new ManagedTransaction(connection);
         Executor executor = new SimpleExecutor(configuration, tx);
         return new DefaultSqlSession(configuration, executor);
     }
     
     @Override
     public SqlSession openSession(TransactionIsolationLevel level) {
         // 实现略:创建指定隔离级别的事务
         return openSession(false);
     }
     
     @Override
     public Configuration getConfiguration() {
         return configuration;
     }
 }
三、为什么SqlSessionFactory是重量级的?

从上面的实现可以看出,SqlSessionFactory的重量级体现在以下几个方面:

  1. 配置信息承载者 :它持有Configuration对象,这个对象包含了MyBatis运行所需的所有配置信息:数据源、映射器、类型处理器、插件等。这些配置在应用生命周期内通常不会改变。

  2. 资源初始化成本高Configuration的初始化过程需要解析XML配置、扫描注解、注册映射器等,这些都是相对耗时的操作。

  3. 线程安全要求 :作为共享组件,SqlSessionFactory必须是线程安全的,这增加了其实现的复杂性。

  4. 长生命周期 :通常一个应用对应一个SqlSessionFactory实例,伴随应用的整个生命周期。

正因为这些特性,我们应该将SqlSessionFactory设计为单例,在应用启动时创建,在整个运行期间重复使用。

四、Executor的类型配置与选择

Executor是MyBatis执行SQL的核心组件,不同的执行器类型适应不同的场景:

java 复制代码
 public enum ExecutorType {
     SIMPLE,    // 简单执行器:每次执行都会创建新的PreparedStatement
     REUSE,     // 复用执行器:复用PreparedStatement
     BATCH      // 批处理执行器:批量执行更新操作
 }

SqlSessionFactory中,我们可以通过配置来决定创建哪种类型的执行器:

java 复制代码
 // 在openSession方法中根据配置创建不同类型的执行器
 Executor executor;
 if (ExecutorType.BATCH == configuration.getDefaultExecutorType()) {
     executor = new BatchExecutor(configuration, tx);
 } else if (ExecutorType.REUSE == configuration.getDefaultExecutorType()) {
     executor = new ReuseExecutor(configuration, tx);
 } else {
     executor = new SimpleExecutor(configuration, tx);
 }
 ​
 // 还可以支持在openSession时指定执行器类型
 public SqlSession openSession(ExecutorType execType) {
     Transaction tx = createTransaction();
     Executor executor = createExecutor(tx, execType);
     return new DefaultSqlSession(configuration, executor);
 }
五、SqlSessionFactory的构建者:SqlSessionFactoryBuilder

为了进一步分离关注点,我们引入SqlSessionFactoryBuilder来专门负责SqlSessionFactory的构建:

java 复制代码
 public class SqlSessionFactoryBuilder {
     
     public SqlSessionFactory build(Configuration configuration) {
         return new DefaultSqlSessionFactory(configuration);
     }
     
     public SqlSessionFactory build(InputStream inputStream) {
         // 解析XML配置,构建Configuration,然后创建SqlSessionFactory
         XMLConfigBuilder parser = new XMLConfigBuilder(inputStream);
         Configuration config = parser.parse();
         return build(config);
     }
     
     public SqlSessionFactory build(Reader reader) {
         // 类似上面,从Reader读取配置
         // ...
     }
 }

这种构建者模式的优势在于:

  1. 分离配置解析与工厂创建:让每个类职责更加单一

  2. 支持多种配置源:可以支持XML、Properties、编程式配置等多种方式

  3. 灵活的构建过程:可以在构建过程中添加验证、优化等逻辑

六、总结与最佳实践

通过引入SqlSessionFactory,我们完成了MyBatis核心架构的又一个重要模块。工厂模式的应用让我们的框架更加专业和灵活:

  1. 创建逻辑封装 :将复杂的SqlSession创建过程封装在工厂中,客户端代码更加简洁

  2. 配置集中管理 :通过SqlSessionFactory统一管理所有配置,确保一致性

  3. 灵活的会话控制 :支持创建具有不同特性的SqlSession实例

  4. 更好的资源管理 :集中管理Executor的创建和生命周期

在实际应用中,我们应该遵循以下最佳实践:

  • SqlSessionFactory作为应用级单例管理

  • 根据具体场景选择合适的ExecutorType(批处理操作使用BATCH,高并发查询考虑REUSE

  • 通过SqlSessionFactoryBuilder统一构建工厂实例,确保配置的一致性

下次当你使用MyBatis时,不妨思考一下:每一个简单的getMapper()调用背后,是整个工厂体系在默默支撑。这种精妙的分层设计和职责划分,正是优秀框架的迷人之处。


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

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

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

相关推荐
程序员清风10 分钟前
程序员代码有Bug别怕,人生亦是如此!
java·后端·面试
就是帅我不改18 分钟前
告别996!高可用低耦合架构揭秘:SpringBoot + RabbitMQ 让订单系统不再崩
java·后端·面试
用户61204149221331 分钟前
C语言做的区块链模拟系统(极简版)
c语言·后端·敏捷开发
Mintopia41 分钟前
🎬《Next 全栈 CRUD 的百老汇》
前端·后端·next.js
hqxstudying1 小时前
MyBatis 和 MyBatis-Plus对比
java·数据库·mysql·mybatis
CF14年老兵1 小时前
深入浅出 Python 一等函数:一份友好的全面解析
后端·python·trae
whitepure1 小时前
万字详解常用数据结构(Java版)
java·数据结构·后端
天天摸鱼的java工程师1 小时前
你们公司的 QPS 是怎么统计出来的?这 5 种常见方法我踩过一半的坑
java·后端·面试
guojl1 小时前
Gateway使用手册
后端·微服务
BingoGo1 小时前
PHP 内存管理 深入理解 PHP 的引用和垃圾回收
后端·php