Mybatis 源码解读-SqlSession 会话源码和Executor SQL操作执行器源码

作者源码阅读笔记主要采用金山云文档记录的,所有的交互图和代码阅读笔记都是记录在云文档里面,本平台的文档编辑实在不方便,会导致我梳理的交互图和文档失去原来的格式,所以整理在文档里面,供大家阅读交流.

【金山文档 | WPS云文档】 SqlSession 会话源码和Executor SQL操作执行器源码

SqlSession 是框架与数据库交互的核心接口。

核心功能

  1. SQL 操作执行

‌提供 insert()update()delete()select() 等方法直接执行 SQL 语句,支持参数绑定与结果映射‌

  1. 事务管理

‌通过 commit()rollback() 控制事务提交与回滚,需手动调用(非自动提交模式)‌

  1. Mapper 接口代理

通过 getMapper(Class<T> type) 动态生成 Mapper 接口实现类,实现面向对象式数据库操作‌

  1. 缓存管理

‌默认启用一级缓存(会话级),缓存相同 SQL 查询结果,通过 clearCache() 可手动清空‌。‌

Executor 包是 MyBatis 执行 SQL 操作的核心引擎,位于 org.apache.ibatis.executor 包下,负责协调 SQL 执行全流程(包括缓存管理、事务控制、参数处理、结果映射等)

Executor 包是 MyBatis 的 ‌SQL 执行中枢‌,通过多态实现支持基础操作、缓存优化、批量处理等场景,并协调四大组件完成从 SQL 解析到结果映射的全链路操作‌

‌核心接口与实现

Executor 接口

定义 SQL 执行的标准方法:

复制代码
public interface Executor {
    int update(MappedStatement ms, Object parameter);         // 执行更新操作
    <E> List<E> query(...);                                  // 执行查询操作
    void commit(boolean required);                           // 提交事务
    void rollback(boolean required);                         // 回滚事务
    CacheKey createCacheKey(...);                            // 创建缓存键
    boolean isCached(MappedStatement ms, CacheKey key);      // 检查缓存
}

基础实现类(BaseExecutor 子类)

|---------------------|-----------------------------------------------------------|-------------|
| 类型 | ‌特点‌ | ‌适用场景‌ |
| SimpleExecutor‌ | 默认执行器,每次执行创建新 Statement,执行后立即关闭 | 常规单条 SQL 操作 |
| ReuseExecutor‌ | 复用 Statement 对象,缓存相同 SQL 的 Statement 以减少重复编译开销 | 高频重复 SQL |
| BatchExecutor‌ | 批量执行 SQL,通过 addBatch() 缓存操作,调用 flushStatements() 统一提交 | 批量插入/更新 |

增强实现类 CachingExecutor

  • 二级缓存代理‌:装饰器模式,在基础执行器外层添加二级缓存逻辑‌
  • 工作流程‌:
  1. 优先查询二级缓存(MappedStatement 级别);
  2. 缓存未命中时委托底层执行器(如 SimpleExecutor)查询数据库

‌协作组件

Executor 通过组合模式调用其他三大组件:

|------------------------|--------------------------------|-------------------------|
| ‌组件‌ | ‌职责‌ | ‌依赖关系‌ |
| ‌StatementHandler‌ | 处理 Statement 创建与参数绑定 | Executor 调用其执行 SQL |
| ParameterHandler‌ | 转换参数类型并填充到 PreparedStatement | 被 StatementHandler 调用 |
| ResultSetHandler‌ | 封装结果集到 Java 对象 | 被 StatementHandler 调用 |

相关重要类介绍说明

SqlSessionManager 类

|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----|
| 名称 | 描述 | 默认值 |
| sqlSessionFactory | ‌核心作用‌ * 作为底层工厂实例,用于创建原生 SqlSession 对象(通过 openSession() 方法)‌ * 代理模式下(调用 startManagedSession() 后),通过动态代理自动管理会话生命周期,此时 sqlSessionFactory 负责在后台创建线程绑定的 SqlSession‌ | |
| sqlSessionProxy | sqlSessionProxy 字段是一个关键动态代理对象 #### 核心功能 * ‌动态代理拦截‌ 作为 SqlSession 的代理实例,拦截所有 Mapper 方法调用,并路由到当前线程绑定的 SqlSession 或新建会话 ‌通过 InvocationHandler 实现方法调用的统一处理(如事务提交、异常回滚)‌ * ‌线程安全会话管理 ‌优先从 ThreadLocallocalSqlSession)获取会话,避免重复创建,确保线程隔离‌ 若未绑定会话(未调用 startManagedSession()),则临时创建并自动关闭会话(非托管模式)‌ | |
| localSqlSession | localSqlSession 字段是一个关键线程安全控制组件 #### 核心作用 * 线程隔离存储 通过 ThreadLocal<SqlSession> 实现线程绑定的会话管理,确保每个线程操作独立的 SqlSession 实例 避免多线程环境下会话冲突(如事务交叉)‌‌ * ‌托管模式标志 当调用 startManagedSession() 后,新创建的 SqlSession 会存入此字段,标记当前线程进入托管模式‌ ‌未调用时字段值为 null,代理对象每次临时创建新会话‌ | |

  • sqlSessionProxy代理对象创建:

    this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),
    new Class[] { SqlSession.class }, new SqlSessionInterceptor());

  • SqlSessionInterceptor 拦截器

每次调用代理对象 sqlSessionProxy 方法都会去执行invoke方法

复制代码
private class SqlSessionInterceptor implements InvocationHandler {
    public SqlSessionInterceptor() {
      // Prevent Synthetic Access
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
      if (sqlSession != null) {
        try {
          return method.invoke(sqlSession, args);
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
      }
      try (SqlSession autoSqlSession = openSession()) {
        try {
          final Object result = method.invoke(autoSqlSession, args);
          autoSqlSession.commit();
          return result;
        } catch (Throwable t) {
          autoSqlSession.rollback();
          throw ExceptionUtil.unwrapThrowable(t);
        }
      }
    }
  }

SqlSessionFactory sqlSession 工厂类

负责创建和管理 SqlSession 实例‌

|---------------|--------|-----|
| 名称 | 描述 | 默认值 |
| configuration | 全局配置实例 | |

DefaultSqlSession 默认会话类

|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------|
| 名称 | 描述 | 默认值 |
| configuration | 全局配置实例 | |
| executor | sql执行器 | |
| autoCommit | 控制事务提交机制的核心参数 | 默认值为 false(需手动提交事务)‌ |
| dirty | 用于标记当前会话是否存在未提交数据变更的关键状态标识 #### 核心功能 * 更新操作触发:执行 insert/update/delete****操作时自动置为 true**,表示有未提交的修改‌** * ‌事务提交后重置:调用 commit()** rollback()后重置为 false,标识数据已同步‌ * ‌连接关闭兜底处理:若未提交事务时关闭会话(** close()****),自动触发回滚并重置 dirty****‌ | |
| cursorList | 用于管理流式查询游标的关键资源集合 | |

所有具体执行器父类 BaseExecutor抽象类

|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----|
| 名称 | 描述 | 默认值 |
| transaction | 管理数据库事务的核心组件 | |
| wrapper | BaseExecutorwrapper 字段是其自身实例的引用,核心作用在于‌实现装饰器模式‌,为执行器提供动态扩展能力。 若未配置装饰器,wrapper 指向自身,直接执行 BaseExecutor 的逻辑;若配置装饰器(如 CachingExecutor),则调用装饰器的增强方法‌ | |
| deferredLoads | BaseExecutor 的 ‌deferredLoads‌ 字段是实现 MyBatis ‌延迟加载(懒加载)‌ 机制的核心组件,用于管理需要延迟加载的关联对象 deferredLoads 字段通过封装延迟加载任务队列,实现了 MyBatis 高效的懒加载机制,是优化复杂对象查询性能的关键组件‌ | |
| localCache | BaseExecutor 的 ‌
localCache
‌ 字段是 MyBatis ‌一级缓存(本地缓存)‌ 的核心实现。 localCache 是 MyBatis 一级缓存的核心载体,通过会话级缓存优化重复查询性能。 #### 典型问题与使用建议 1. ‌脏读风险 ‌对于不同的sqlsession A与B,A做update操作,只能刷新A自己的一级缓存,无法刷新B的一级缓存。所以如果A与B操作同一条记录,就会有脏读。 SqlSessionB第一查询时进行了缓存,第二次查询时从缓存获取数据(脏数据),之后进行逻辑处理以及更新 ‌ | |
| localOutputParameterCache | BaseExecutor 的 ‌localOutputParameterCache‌ 字段是 MyBatis 用于‌存储过程输出参数缓存‌的核心组件 | |
| configuration | 全局配置 | |
| queryStack | 控制嵌套查询与延迟加载‌的关键计数器 #### 典型场景示例 1. ‌嵌套查询‌: 主查询 User 触发关联对象 Order 的懒加载时,queryStack 会从 0→1→0 变化,确保关联查询仅在顶层触发‌ 1. ‌循环依赖规避‌: 若 UserOrder 互相引用,queryStack 机制会阻断循环加载,避免栈溢出 | |
| closed | 标识执行器(Executor)是否已被关闭‌的关键状态标记 | |

基于SQL语句复用的执行器 ReuseExecutor 类

通过内部维护的 Map<String, Statement> 缓存(键为 SQL 语句,值为对应的 Statement 对象),避免相同 SQL 的重复编译与创建开销。适用于高频执行‌相同结构 SQL‌(如循环中参数化查询)的场景

|--------------|---------------------------------------------------------------------------------------------|-----|
| 名称 | 描述 | 默认值 |
| statementMap | statementMap‌ 字段是其实现 ‌Statement****对象复用的核心缓存容器‌,通过减少 JDBC 语句编译与创建开销提升高频重复 SQL 的执行效率 | |

批量操作执行器 ‌BatchExecutor 类

BatchExecutor 是 MyBatis 中‌专为批量数据库操作优化的执行器实现‌,通过延迟执行与批量提交机制显著提升大批量数据操作的性能

|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----|
| 名称 | 描述 | 默认值 |
| statementList | statementList‌ 字段是其实现批量操作的核心数据结构,用于缓存待执行的 Statement 对象以实现批量提交优化 #### 工作流程 1. ‌缓存阶段 * 执行增删改操作时,将相同 SQL 结构的 Statement 存入 statementList,并通过 addBatch() 添加参数‌ * 若 SQL 结构变化(如切换表或操作类型),则创建新 Statement 并加入列表‌ 1. ‌批量提交 * 调用 flushStatements() 或事务提交时,遍历 statementList 执行批量操作‌ * 提交后清空列表并关闭所有 Statement‌ | |
| batchResultList | batchResultList‌ 字段是其实现批量操作结果跟踪的核心数据结构,用于记录每条批量操作的执行状态和参数信息 | |
| currentSql | currentSql‌ 字段是其实现 SQL 批量优化的关键标识,用于动态跟踪当前正在处理的 SQL 语句以实现批量操作的智能分组 | |
| currentStatement | currentStatement‌ 字段是用于跟踪当前批量操作关联的 MappedStatement 对象的核心标识,其设计旨在实现 SQL 执行的上下文一致性管理 | |

二级缓存执行器 CachingExecutor 类

CachingExecutor是一个基于装饰者模式实现的执行器类,主要用于增强二级缓存功能

|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----|
| 名称 | 描述 | 默认值 |
| delegate | delegate 字段是其实现装饰器模式的关键组件,用于承载实际的数据库操作执行器 delegateCachingExecutor 连接具体执行器的桥梁,使缓存功能可叠加于不同执行策略之上,体现了装饰器模式在 MyBatis 中的灵活应用 | |
| tcm | tcm 字段是实现二级缓存事务性提交的核心组件,全称为 TransactionalCacheManager #### 核心职责 1. ‌事务性缓存管理 ‌确保二级缓存的数据仅在事务成功提交后生效,避免脏读问题。当执行写操作(如 update)时,缓存变更不会立即同步到二级缓存,而是暂存在 TransactionalCache 中,直至事务提交‌ 1. ‌多命名空间协调 ‌管理多个映射语句(MappedStatement)对应的 TransactionalCache 实例,维护不同 Cache 对象的隔离性与一致性 这一设计解决了多 SqlSession 并发场景下二级缓存的数据一致性问题。‌ | |

TransactionalCacheManager 二级缓存事务管理器

MyBatis 的 TransactionalCacheManager 是二级缓存事务管理的核心组件,主要负责在事务提交时协调多个 TransactionalCache 的缓存同步操作

核心职责

  • 事务性缓存控制

‌管理多个 TransactionalCache 实例,确保二级缓存的更新仅在事务提交后生效,避免事务未提交时其他会话读取到脏数据‌

  • 多缓存空间隔离

‌通过 Map<String, TransactionalCache> 结构维护不同命名空间(namespace)的缓存实例,实现缓存区域的隔离与协同‌

|---------------------|-------------------------------------------------------------------------------------------|----------------------------------|
| 名称 | 描述 | 默认值 |
| transactionalCaches | 维护底层真实缓存(如 PerpetualCache)与事务缓存代理(TransactionalCache)的映射关系,确保每个 Cache 对象都有独立的事务暂存区‌ | Map<Cache, TransactionalCache> |

TransactionalCache 二级事务缓存类

TransactionalCache 是二级缓存事务管理的核心代理类,负责在事务提交前拦截缓存操作,确保数据一致性

核心职责

  • 事务暂存区

‌代理真实的 Cache 对象(如 PerpetualCache),在事务提交前将写操作(putObject)暂存于内存,避免未提交事务污染二级缓存‌

  • 脏数据隔离

通过 entriesToAddOnCommit(待提交数据)和 entriesMissedInCache(未命中记录)两个集合,隔离事务内外的缓存状态

|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------|
| 名称 | 描述 | 默认值 |
| delegate | delegate 字段是二级缓存实现的核心代理对象,其设计体现了装饰器模式的应用 delegate 字段是 MyBatis 二级缓存事务机制与底层存储的桥梁,其设计完美体现了‌装饰器模式 ‌和‌单一职责原则核心职责 1、‌真实缓存代理 ‌:持有底层缓存实现(如 PerpetualCache),所有缓存操作最终委托给该对象执行‌ 2、‌功能扩展基础 ‌:通过装饰器模式(如 LruCacheScheduledCache)对基础缓存增强,而 delegate 是装饰链的最终执行者‌‌‌ | |
| clearOnCommit | 用于控制事务提交时是否清空底层缓存 | 默认值为 false,可通过特定操作(如更新语句)动态修改状态‌ |
| entriesToAddOnCommit | entriesToAddOnCommit 字段是二级缓存事务管理的核心数据结构,其设计实现了‌事务隔离性 ‌与‌批量提交优化 核心职责 * ‌事务暂存区 ‌:临时存储事务内所有 putObject 操作的数据,直到事务提交时批量写入底层缓存(delegate)‌ * ‌脏写隔离‌:避免未提交事务的中间状态污染二级缓存,确保其他会话读取的数据一致性‌ | Map<Object, Object> entriesToAddOnCommit |
| entriesMissedInCache | 用于追踪‌二级缓存未命中记录‌的关键字段,其设计目的主要在于优化并发场景下的缓存一致性与阻塞控制; 记录事务执行过程中‌缓存查询未命中的键(Key) 该字段是 MyBatis 二级缓存应对‌高并发穿透问题‌的核心设计组件之一。 | Set<Object> entriesMissedInCache |

‌CacheKey 缓存key类

缓存key生成逻辑代码:

复制代码
@Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    MetaObject metaObject = null;
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (parameterMapping.hasValue()) {
          value = parameterMapping.getValue();
        } else if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else {
          ParamNameResolver paramNameResolver = ms.getParamNameResolver();
          if (paramNameResolver != null
              && typeHandlerRegistry.hasTypeHandler(paramNameResolver.getType(paramNameResolver.getNames()[0]))
              || typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            if (metaObject == null) {
              metaObject = configuration.newMetaObject(parameterObject);
            }
            value = metaObject.getValue(propertyName);
          }
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

影响key生成的因素:

  1. Mapper的Id,即Mapper命名空间与<select|update|insert|delete>标签的Id组成的全局限定名
  2. 查询结果的偏移量及查询的条数
  3. 具体的SQL语句及SQL语句中需要传递的所有参数
  4. MyBatis主配置文件中,通过<environment>标签配置的环境信息对应的Id属性值

相关流程图整理

元数据简略图

执行流程图

一级缓存、二级缓存设计与查询流程分析图

  • 一级缓存设计-类图
  • 一级缓存工作流程图
  • 二级缓存设计-类图
  • 二级缓存工作流程图
相关推荐
昵称为空C2 天前
SpringBoot 实现DataSource接口实现多租户数据源切换方案
后端·mybatis
isyangli_blog2 天前
(2-10-1)MyBatis的基础与基本使用
java·开发语言·mybatis
_码农121382 天前
Mybatis简单练习注解sql和配置文件sql+注解形式加载+配置文件加载
mybatis
期待のcode2 天前
Maven
java·spring·maven·mybatis
独泪了无痕2 天前
一文搞懂MyBatis中的TypeHandler
数据库·后端·mybatis
Easocen2 天前
Mybatis学习笔记(十)
mybatis
千睢3 天前
Mybatis实现页面增删改查
mybatis
Warren983 天前
Java后端面试题(含Dubbo、MQ、分布式、并发、算法)
java·开发语言·分布式·学习·算法·mybatis·dubbo
羊锦磊3 天前
[ Mybatis 多表关联查询 ] resultMap
java·开发语言·数据库·mysql·mybatis