掘金 后端 ( ) • 2024-03-13 15:01

前言

在Mybatis源码中,SqlSession是一个非常重要的类,它是数据库操作执行的开始。它的创建是通过接口SqlSessionFactory的实现类来创建的,默认是DefaultSqlSessionFactory。今天就来总结一下SqlSession对象的创建方法:openSession的执行流程与细节,自己进行复习的同时,也希望可以帮到大家。

正文

ExecutorType

因为SqlSessionFactory默认使用的子类是DefaultSqlSessionFactory,所以先来看一下它的源码:

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private final Configuration configuration;
    
    @Override
    public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }
}

可以看出openSession继续调用一个本类方法openSessionFromDataSource,顾名思义从数据源获取SqlSession,先看一下这个方法的入参,有下面三个

  1. ExecutorType:执行器类型
  2. TransactionIsolationLevel:事务隔离级别。
  3. boolean autoCommit:是否进行事务的自动提交。
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    //。。。
  }

ExecutorType的赋值,调用了全局配置对象Configuration的getDefaultExecutorType()方法,点开可以发现,其实就是获取了defaultExecutorType这个属性的值,它的默认值是ExecutorType.SIMPLE

public class Configuration {
    //省略其它代码...
    protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
    
    public ExecutorType getDefaultExecutorType() {
        return defaultExecutorType;
    }
}

ExecutorType其实是一个枚举类,里边定义了执行器的类型

public enum ExecutorType {
    SIMPLE, REUSE, BATCH
}

openSessionFromConnection

这个方法就是真正创建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);
        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();
    }
}

第4-6行:先从全局配置对象获取了环境配置信息,然后通过调用getTransactionFactoryFromEnvironment方法从环境配置中获取了事务工厂对象,这个地方获取的事务工厂对象,跟xml中声明transactionManager时配置的别名type有关,获取到事务工厂对象之后,通过newTransaction方法创建了事务对象。

第7行通过调用全局配置对象的newExecutor方法完成了执行器Executor的创建,源码如下:

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);
    }
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

第5-11行:根据ExecutorType的值,创建了不同类型的执行器。

第12-14行:当框架开启了缓存的时候,返回的执行器为CachingExecutor,这里应用了一个装饰器模式,没有去修改原有执行的代码,而是包装了一层,在这里动态拓展了缓存相关的功能。

装饰器模式,在Mybatis加载配置文件时,对类加载器的获取的使用等,也应用了装饰器模式(可见 Mybatis源码 - 初始化流程 加载解析配置文件),加载配置文件时对类加载器的包装,并未去直接继承类加载器(ClassLoader),只是封装了一些加载资源时,加载器的获取、加载过程、判断、异常处理等过程,用来简化使用。

这里的应用略有不同,这里是使用了一个实现类CachingExecutor实现了Executor接口,并且使用一个属性来保存了其它执行器的引用,从结构上,更符合下面这张装饰器模式架构图:

image.png

第15行:是和Mybatis插件相关的代码,如果当前有配置的插件,这里会通过pluginAll方法,来创建一个代理对象,后续可以使用代理对象来实现插件的逻辑。

DefaultSqlSession

方法的最后,创建了DefaultSqlSession对象并返回,这就是openSession方法最后返回的实例。创建的同时,也将全局配置对象、执行器对象,是否自动提交标识进行了传递封装。

return new DefaultSqlSession(configuration, executor, autoCommit);