第四章-Mybatis源码解析-执行工具准备

前面几章已经把SQL和执行环境与配置准备好了,那么用什么执行呢,本章将介绍所有执行工具的创建和使用,具体工具包括SqlSession、事务管理器(工厂)、事务(Transaction)、执行器(Executor)。

先看它们长什么样吧,上代码

SqlSession

SqlSession被定义成一个接口,从接口提供的方法,可以看出,主要就是提供CRUD的操作、事务的提交和回滚操作、会话关闭操作等,这里先简单描述,后续讲使用时再具体分析

public interface SqlSession extends Closeable {

  <T> T selectOne(String statement);
  <T> T selectOne(String statement, Object parameter);
  <E> List<E> selectList(String statement);
  <E> List<E> selectList(String statement, Object parameter);
  <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
  <K, V> Map<K, V> selectMap(String statement, String mapKey);
  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
  <T> Cursor<T> selectCursor(String statement);
  <T> Cursor<T> selectCursor(String statement, Object parameter);
  <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
  void select(String statement, Object parameter, ResultHandler handler);
  void select(String statement, ResultHandler handler);
  void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
  int insert(String statement);
  int insert(String statement, Object parameter);
  int update(String statement);
  int update(String statement, Object parameter);
  int delete(String statement);
  int delete(String statement, Object parameter);
  void commit();
  void commit(boolean force);
  void rollback();
  void rollback(boolean force);
  List<BatchResult> flushStatements();
  @Override
  void close();
  void clearCache();
  Configuration getConfiguration();
  <T> T getMapper(Class<T> type);
  Connection getConnection();
}

Transaction

Transaction 也是一样接口,定义了所能做的操作

public interface Transaction {
  // 事务要建立在数据库连接的基础上,所以需要获取连接
  Connection getConnection() throws SQLException;
  // 事务提交
  void commit() throws SQLException;
  // 事务回滚
  void rollback() throws SQLException;
  // 关闭
  void close() throws SQLException;
  // 超时
  Integer getTimeout() throws SQLException;

}

TransactionFactory

TransactionFactory又是一个接口

public interface TransactionFactory {
   // default 表示,默认在接口中实现,而不需要实现类去实现
    default void setProperties(Properties props) {
        // NOP
    }
    // 创建事务
    Transaction newTransaction(Connection conn);
    // 创建事务
    Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

}

Executor

Executor 执行器也是一个接口,这个就是操作数据库的接口,这就是在jdbc的基础再包装了一层,用过jdbc操作的,对这块理解会比较顺畅

public interface Executor {

    ResultHandler NO_RESULT_HANDLER = null;

    int update(MappedStatement ms, Object parameter) throws SQLException;

    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

    <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

    List<BatchResult> flushStatements() throws SQLException;

    void commit(boolean required) throws SQLException;

    void rollback(boolean required) throws SQLException;

    CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);

    boolean isCached(MappedStatement ms, CacheKey key);

    void clearLocalCache();

    void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);

    Transaction getTransaction();

    void close(boolean forceRollback);

    boolean isClosed();

    void setExecutorWrapper(Executor executor);

}

介绍完了几个执行工具后,接下来准备介绍它们的创建

4.1 SqlSession的创建

官网对创建SqlSession的过程有描述,步骤如下:

1、new 一个 SqlSessionFactoryBuilder 对象

2、SqlSessionFactoryBuilder 可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例

3、从 SqlSessionFactory 中获取 SqlSession

另外三者的作用域官网也有描述:

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。

接下来,直接看源码,进到 DefaultSqlSessionFactory 类的 openSession方法,里面包括很多个openSession,用途都一样,无非是传参有差异

// 没有任何参数,一切都用默认的
public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

// autoCommit 自动提交标志
public SqlSession openSession(boolean autoCommit) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}

// execType 执行类型:SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新
public SqlSession openSession(ExecutorType execType) {
    return openSessionFromDataSource(execType, null, false);
}

// level 事务级别
public SqlSession openSession(TransactionIsolationLevel level) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
}

// 执行类型 + 事务级别
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
    return openSessionFromDataSource(execType, level, false);
}

// 执行类型 + 自动提交标志
public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
    return openSessionFromDataSource(execType, null, autoCommit);
}

// 带 数据库连接
public SqlSession openSession(Connection connection) {
    return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
}

// 执行类型 + 数据库连接
public SqlSession openSession(ExecutorType execType, Connection connection) {
    return openSessionFromConnection(execType, connection);
}

继续进到 openSessionFromConnection 方法

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        // 从配置对象中取出环境对象 environment,并且设置为final,不让修改
        final Environment environment = configuration.getEnvironment();
        // 获取事务管理器(工厂)transactionFactory,获取方式详见章节`4.2`
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        // 创建事务,详见章节`4.3`
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        // 创建执行器,详见章节`4.4`
        final Executor executor = configuration.newExecutor(tx, execType);
        // 最终创建SqlSession,并以 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();
    }
}

4.2 TransactionFactory创建

private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    // 这个方法很简单,就是判断 environment 有没有配置 TransactionFactory ,没有的话就用 ManagedTransactionFactory,有的话就用配置的,Mybatis帮我实现了2个,Spring帮我们实现1个,如`图4-1`,当然也可以自定义
    if (environment == null || environment.getTransactionFactory() == null) {
        return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
}

图4-1
在这里插入图片描述

4.3 Transaction创建

因为Mybatis有2个TransactionFactory的实现,分别 ManagedTransactionFactory 和 JdbcTransactionFactory ,需要分别介绍下这2种工厂如何创建 Transaction 对象

ManagedTransactionFactory 方式

官网原文介绍:MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为

public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new ManagedTransaction(ds, level, closeConnection);
}

// 只截取部分代码
public class ManagedTransaction implements Transaction {

    private DataSource dataSource; // 数据源
    private TransactionIsolationLevel level; // 事务级别
    private Connection connection; // 连接
    private final boolean closeConnection;  

    public ManagedTransaction(Connection connection, boolean closeConnection) {
        this.connection = connection;
        this.closeConnection = closeConnection;
    }

    public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
        this.dataSource = ds;
        this.level = level;
        this.closeConnection = closeConnection;
    }
}

JdbcTransactionFactory 方式

官网原文介绍:JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域

public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit);
}

// 只截取部分代码
public class JdbcTransaction implements Transaction {

    protected Connection connection;  // 连接
    protected DataSource dataSource;  // 数据源
    protected TransactionIsolationLevel level; // 事务级别
    protected boolean autoCommit; // 自动提交标志 

    public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
        dataSource = ds;
        level = desiredLevel;
        autoCommit = desiredAutoCommit;
    }
}

4.4 Executor创建

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // 先拿配置的执行类型
    executorType = executorType == null ? defaultExecutorType : executorType;
    // 没有配置,就用默认的 SIMPLE 
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    // 根据类型不同,创建不同的执行器,BATCH/REUSE/SIMPLE,三个执行器的描述,在章节`4.1`中有描述
    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);
    }
    // cacheEnabled	全局性地开启或关闭所有映射器配置文件中已配置的任何缓存
    if (cacheEnabled) {
        // 开启全局缓存开关,就要用 CachingExecutor 执行器了
        executor = new CachingExecutor(executor);
    }
    // 插件执行,这块留待讲解插件时来讲
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

后续将通过抖音视频/直播的形式分享技术,由于前期要做一些准备和规划,预计2024年6月开始,欢迎关注,如有需要或问题咨询,也可直接抖音沟通交流。
在这里插入图片描述

  • 33
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多栖码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值