【Mybatis】Mybatis源码之创建SqlSession对象

602 阅读2分钟

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

时序图

sequenceDiagram
participant A as DefaultSqlSessionFactory
participant B as Configuration

A ->> A : openSessionFromDataSource():DefaultSqlSession
A ->> B : newExecutor
B -->> A : Executor

详细步骤

DefaultSqlSessionFactory#openSessionFromDataSource

/**
 * 从数据源中获取SqlSession对象
 * @param execType 执行器类型,默认是在Configuration中定义的SIMPLE类型
 * @param level 事务隔离级别
 * @param autoCommit 是否自动提交事务
 * @return SqlSession对象
 */
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        // 找出要使用的指定环境
        final Environment environment = configuration.getEnvironment();
        // 从环境中获取事务工厂
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        // 从事务工厂中生产事务
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        // 创建执行器
        final Executor executor = configuration.newExecutor(tx, execType);
        // 创建DefaultSqlSession对象
        return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
        closeTransaction(tx); // may have fetched a connection so lets call close()
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

Configuration#newExecutor

/**
 * 创建一个执行器
 * @param transaction 事务
 * @param executorType 数据库操作类型
 * @return 执行器
 */
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    // 根据数据库操作类型创建市级执行器
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    // 根据配置文件中的 settings 节点cacheEnabled配置项确定是否启用缓存
    if (cacheEnabled) { // 如果配置启用该缓存
        // 使用CachingExecutor装饰实际的执行器
        executor = new CachingExecutor(executor);
    }
    // 为执行器增加拦截器(插件),以启用各个拦截器的功能
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

四种Executor

BatchExecutor

  • 批量执行SQL
  • 调用方法时,将待执行的PreparedStatement通过addBatch方法加入
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
    ...
    // addBatch
    handler.batch(stmt);
    return BATCH_UPDATE_RETURN_VALUE;
}
  • 调用SqlSessioncommit方法时执行批量操作
sequenceDiagram
autonumber
participant A as DefaultSqlSession
participant B as BaseExecutor
participant C as BatchExecutor
participant D as Statement

A ->> B : commit
B ->> B : flushStatements
B ->> C : doFlushStatements
C ->> D : executeBatch:批量操作

SimpleExecutor

  • 每次请求都会重新创建PreparedStatement

ReuseExecutor

  • 做了sql与PreparedStatement的缓存
  • 请求时会判断是否存在缓存,不存在则创建PreparedStatement,否则从缓存中拿PreparedStatement
  • 请求完成后不会关闭PreparedStatement,而是会进行缓存
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    if (hasStatementFor(sql)) { // 判断是否有缓存
        stmt = getStatement(sql);
        applyTransactionTimeout(stmt);
    } else {
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        putStatement(sql, stmt); // 缓存sql与PreparedStatement
    }
    handler.parameterize(stmt);
    return stmt;
}

CachingExecutor

  • 与二级缓存有关,如果开启了二级缓存的全局开关,则会将获取的Executor使用此对象进行包装

以上便是Mybatis创建SqlSession对象的过程,以及对四种Executor的简单介绍。