Mybatis源码 - sqlSessionFactory.openSession()方法解析 创建事务对象 装饰器模式创建执行器

前言

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

正文

ExecutorType

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

java 复制代码
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:是否进行事务的自动提交。
java 复制代码
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    //。。。
  }

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

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

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

java 复制代码
public enum ExecutorType {
    SIMPLE, REUSE, BATCH
}

openSessionFromConnection

这个方法就是真正创建SqlSession对象的方法,下面是它的源码:

java 复制代码
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的创建,源码如下:

java 复制代码
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接口,并且使用一个属性来保存了其它执行器的引用,从结构上,更符合下面这张装饰器模式架构图:

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

DefaultSqlSession

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

java 复制代码
return new DefaultSqlSession(configuration, executor, autoCommit);
相关推荐
why1513 小时前
腾讯(QQ浏览器)后端开发
开发语言·后端·golang
浪裡遊3 小时前
跨域问题(Cross-Origin Problem)
linux·前端·vue.js·后端·https·sprint
声声codeGrandMaster3 小时前
django之优化分页功能(利用参数共存及封装来实现)
数据库·后端·python·django
呼Lu噜4 小时前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
bing_1584 小时前
为什么选择 Spring Boot? 它是如何简化单个微服务的创建、配置和部署的?
spring boot·后端·微服务
学c真好玩4 小时前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia04124 小时前
GenericObjectPool——重用你的对象
后端
Piper蛋窝4 小时前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel5 小时前
招幕技术人员
前端·javascript·后端
盖世英雄酱581365 小时前
什么是MCP
后端·程序员