【Mybatis】【SQL执行过程】【三】Mybatis源码解析-SqlSession、Executor的创建
1 前言
上节我们看到 MapperMethod 执行的前奏,看到其实都是调用的 SqlSession 去执行的,而 SqlSession 又是调用其内部的 Executor 来进行执行的,那么这节我们先来看下回顾这两者的创建过程,方便下节讲执行哈。
2 SqlSession 的创建
起源是通过我们的 SqlSessionFactory 来进行创建的我们来看下:
// DefaultSqlSessionFactory 默认不自动提交 public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } // DefaultSqlSessionFactory // autoCommit 是否开启自动提交 public SqlSession openSession(boolean autoCommit) { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit); }
我们看到是内部调用 openSessionFromDataSource 来完成创建的,我们跟进去看下:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 环境变量 final Environment environment = configuration.getEnvironment(); // 事务工厂 JdbcTransactionFactory(自动帮你) ManagedTransactionFactory(自己管理事务) final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 根据我们的事务工厂获取一个事务对象 每个事务对象里边都有数据源 连接也是从其数据源中获取的 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 创建我们的执行器 final Executor executor = configuration.newExecutor(tx, execType); // 创建 SqlSession 对象 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(); }
可以看到创建 SqlSession 的大致步骤:
- 从事务工厂获取一个事务连接
- 创建执行器
- 创建SqlSession
那我们分别来看下这三者的具体执行。
2.1 获取连接
关于事务的我们之前说过了哈,我们这里简单回顾下,就是根据我们的环境配置的事务管理器信息以及数据源信息,根据 type 值去创建对应的事务工厂,然后从事务工厂获取连接,事务工厂其实也是从数据源中获取,我画个图简单回顾下哈:
2.2 执行器的创建
那我们再继续看下执行器的创建,执行器就是负责执行我们的语句的,执行器的类型大致也分三种,如果开启了二级缓存的话会把执行器外边再包一层缓存对象,我们来具体看下:
// 默认的执行器类型 simple 的 protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE; public Executor newExecutor(Transaction transaction, ExecutorType executorType) { // 最终获取一个执行器的类型 executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; /** * 根据类型创建不同的执行器 * 每个的实例化都很简单都是直接调用父类的实例化 BaseExecutor */ 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; }
下边三个执行器的实例化方法都会调用 BaseExecutor 的实例化,我们看下:
// SimpleExecutor 实例化 public SimpleExecutor(Configuration configuration, Transaction transaction) { super(configuration, transaction); } // BatchExecutor 实例化 public BatchExecutor(Configuration configuration, Transaction transaction) { super(configuration, transaction); } // ReuseExecutor 实例化 public ReuseExecutor(Configuration configuration, Transaction transaction) { super(configuration, transaction); }
那我们进入 BaseExecutor,看一下实例化都做了哪些操作:
// BaseExecutor protected BaseExecutor(Configuration configuration, Transaction transaction) { // 事务对象也是连接对象,里边有连接 this.transaction = transaction; // 延迟加载相关的 this.deferredLoads = new ConcurrentLinkedQueue<>(); // 一级缓存 this.localCache = new PerpetualCache("LocalCache"); this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache"); this.closed = false; this.configuration = configuration; this.wrapper = this; }
可以看到执行器的实例化会初始化一级缓存、连接等信息。
2.3 SqlSession的实例化
那么对于 SqlSession 的实例化就比较简单了,我们来看下:
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { // 配置 this.configuration = configuration; // 核心大哥执行器 我们增删改查都是通过这里的执行器进行的 this.executor = executor; // 是否脏了 this.dirty = false; // 自动提交标志 this.autoCommit = autoCommit; }
默认的创建的是 DefaultSqlSession 以及创建的执行器、配置等信息。
3 小困惑
我们的自动提交最后是给了谁了,谁给我们把事务自动提交的呢,我在调试的时候有这个困惑,发现我的事务管理器也是 JDBC 类型的,但是事务就是没提交,是怎么回事呢?
并且我的数据源是 Druid 的,获取的连接默认是开启事务的:
那么当我获取连接的时候:
// 获取连接 public Connection getConnection() throws SQLException { if (connection == null) { openConnection(); } return connection; } protected void openConnection() throws SQLException { if (log.isDebugEnabled()) { log.debug("Opening JDBC Connection"); } connection = dataSource.getConnection(); if (level != null) { connection.setTransactionIsolation(level.getLevel()); } // 这里会判断自动提交的意愿,就是你要不要自动提交呢 setDesiredAutoCommit(autoCommit); } protected void setDesiredAutoCommit(boolean desiredAutoCommit) { try { /** * connection.getAutoCommit() 是 true,默认是开启自动提交的 * desiredAutoCommit 我们openSession的时候没有传自动开启提交,那么就是 false 所以两者不一样 */ if (connection.getAutoCommit() != desiredAutoCommit) { if (log.isDebugEnabled()) { log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]"); } // 就会把连接的自动提交关闭为 false 所以事务不会自动提交了 connection.setAutoCommit(desiredAutoCommit); } } catch (SQLException e) { // Only a very poorly implemented driver would fail here, // and there's not much we can do about that. throw new TransactionException("Error configuring AutoCommit. " + "Your driver may not support getAutoCommit() or setAutoCommit(). " + "Requested setting: " + desiredAutoCommit + ". Cause: " + e, e); } }
不知道你有没有这个困惑哈,总之我们的自动提交最终是设置给数据库连接的,默认的连接时开启自动事务的,当我们openSession没有开启自动提交的话,就会将连接中的自动提交关闭,所以事务不会自动提交了。
4 小结
这节我们简单回顾了下 SqlSession 以及内部的 Executor的创建,默认的 SqlSession 的类型是 DefaultSqlSession ,执行器的话默认的创建的是 SimpleExecutor 类型的,当开启了二级缓存的话(默认是开启的),我们的执行器外边会包装成 CachingExecutor,那么下节我们就具体看一下执行过程。