mybatis源码解读(六):SqlSession详解以及事务工厂

功能

SqlSession 是使用 MyBatis 的主要 Java 接口,我们可以通过它来执行一些命令,获取映射器示例和管理事务,所以可以将SqlSession看成是管理mybatis的一次会话,不过它也确实是会话的意思,一次api请求维护一个SqlSession。

       //将配置文件通过io流的方式打开
      InputStream inputStream= Resources.getResourceAsStream("org/apache/ibatis/test/MyBatisConfig.xml");
      SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
      SqlSession sqlSession = build.openSession();
      User user = sqlSession.selectOne("org.apache.ibatis.test.UserMapper.selectById", 1);

这是一个最简单的sqlSession获取方法,在 SqlSessionFactoryBuilder.build()方法中,已经将mybatis的所有文件全部都解析完成了,那么接下来就是通过工程去获取session,也就是 build.openSession();
工厂方法去获取session有几个步骤:

  1. 获取到环境(有数据源)
  2. 根据环境配置去开启事务,这里事务一般是 JdbcTransaction
  3. 生成mybatis在执行过程中最重要的一个核心, Executor,他是mybatis执行的贯彻者,默认是 SimpleExecutor(),然后返回一个DefaultSqlSession()。

UML

SqlSessionFactory 工厂默认是有两个的,一个是 DefaultSqlSessionFactory, 另一个是 SqlSessionManager,SqlSessionManage官方没有多做介绍,所以不多做说明,它主要就是通过代理管理sqlSession,实现多数据库的sqlSession的一个统筹。一般我们使用的主要是默认的DefaultSqlSessionFactory。
在这里插入图片描述
事务工厂默认是也是两个,一个是JdbcTransactionFactory,一个是
ManagedTransactionFactory,在这里插入图片描述
由工厂实例化出来的事务也是两个,他们其实都是相互对应的。
在这里插入图片描述

代码解析

在DefaultSqlSessionFactory类的 openSessionFromDataSource() 方法中,是opensession()的主要逻辑,它会获取环境然后根据配置实例化事务工厂,然后生产事务类,然后再根据策略去生成 Executor 执行器,默认是 SimpleExecutor(),最后返回DefaultSqlSession

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);
      // 根据策略生成Executor执行器
      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();
    }
  }

getTransactionFactoryFromEnvironment()方法可以看到,是从 environment 中拿的事务工厂,如果我们在配置环境时没有多加这一行,那么这里返回的就是ManagedTransactionFactory()工厂。

<transactionManager type="JDBC"></transactionManager>
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }

两个事务工厂的 newTransaction()方法其实都是简单的 ,他们只是各自实例化了一下各自的事务,将数据源传入进去。至于事务最重要的openConnection()获取连接的方法,是在execute执行的时候才会触发,这里只是实例化。

--------------------- JdbcTransactionFactory ---------------------
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit);
  }
  --------------------- ManagedTransactionFactory---------------------
  @Override
  public Transaction newTransaction(Connection conn) {
    return new ManagedTransaction(conn, closeConnection);
  }

实例化 execute的方法其实就是根据策略去分别实例化不同类型的执行器,然后将事务给弄进去,这里有个稍微需要注意的地方是,如果允许开启缓存,则会通过CachingExecutor去将executor代理掉来增加缓存层,cacheEnabled默认是true,也就是缓存是开启的。

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);
    }
    // 如果允许缓存,会通过 CachingExecutor 去代理一层,怎还缓存层
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    // 拦截器插件
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

最后就是拦截器插件的拦截了。如果有插件的话,则会通过pluginAll去代理一下,插件之所以可以拦截的实现就是在这里,它最终会调用Plugin 的wrap方法去代理,而Plugin 上有个我们非常熟悉的标志,InvocationHandler ,没错,就是动态代理,拦截器插件的原理就是通过动态代理来实现的。

private final List<Interceptor> interceptors = new ArrayList<>();

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
  
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  
  public class Plugin implements InvocationHandler {

一切都处理完成之后,就是将构造好的 sqlsession返回去就好了,openSession()虽然有个open,但其实只是构建了一下sqlsession,连接并未获取。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值