• MyBatis 源码分析之 SqlSession 创建


    • 三哥

    内容来自【自学星球】

    欢迎大家来了解我的星球,和星主(也就是我)一起学习 Java ,深入 Java 体系中的所有技术。我给自己定的时间是一年,无论结果如何,必定能给星球中的各位带来点东西。

    想要了解更多,欢迎访问👉:自学星球

    --------------SSM系列源码文章及视频导航--------------

    创作不易,望三连支持!

    SSM源码解析视频

    👉点我

    Spring

    1. Spring 中注入 Bean 的各种骚操作做
    2. Spring 中Bean的生命周期及后置处理器使用
    3. Spring 中容器启动分析之refresh方法执行之前
    4. Spring refresh 方法分析之一
    5. Spring refresh 方法之二 invokeBeanFactoryPostProcessors 方法解析
    6. Spring refresh 方法分析之三
    7. Spring refresh 方法之四 finishBeanFactoryInitialization 分析
    8. Spring AOP源码分析一
    9. Spring AOP源码分析二
    10. Spring 事务源码分析

    SpringMVC

    1. SpringMVC 启动流程源码分析
    2. SpringMVC 请求流程源码分析

    MyBatis

    1. MyBatis 源码分析之 SqlSessionFactory 创建
    2. MyBatis 源码分析之 SqlSession 创建
    3. MyBatis 源码分析之 Mapper 接口代理对象生成及方法执行
    4. MyBatis 源码分析之 Select 语句执行(上)
    5. MyBatis 源码分析之 Select 语句执行(下)
    6. MyBatis 源码分析一二级缓存

    ---------------------【End】--------------------

    一、SqlSession的创建过程

    SqlSession是mybatis的核心接口之一,是myabtis接口层的主要组成部分,对外提供了mybatis常用的api。myabtis提供了两个SqlSesion接口的实现,常用的实现类是DefaultSqlSession,它相当于一个数据库连接对象,在一个SqlSession中可以执行多条SQL语句。

    前面我们已经分析等到了 SqlSessionFactory ,下面我们再来看看通过它如何拿到 SqlSession 。

    // 获得会话;
    SqlSession session = sqlSessionFactory.openSession();
    
    • 1
    • 2

    org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession()

    public SqlSession openSession() {
        // 第一个参数解释:configuration.getDefaultExecutorType(),获取了默认的执行器类型 SIMPLE
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }
    
    • 1
    • 2
    • 3
    • 4

    org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource

    /**
         * ExecutorType 指定Executor的类型,分为三种:SIMPLE, REUSE, BATCH,默认使用的是SIMPLE
         * TransactionIsolationLevel 指定事务隔离级别,使用null,则表示使用数据库默认的事务隔离界别
         * autoCommit 是否自动提交,传过来的参数为false,表示不自动提交
         */
    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,即执行器
            // 它是真正用来Java和数据库交互操作的类,后面会展开说。
            final Executor executor = configuration.newExecutor(tx, execType);
    
            // 创建DefaultSqlSession对象返回,其实现了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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    上述方法主要分以下几个步骤:

    1. 首先从 configuration 获取 Environment 对象,里面主要包含了 DataSource 和 TransactionFactory 对象。
    2. 创建 TransactionFactory
    3. 创建 Transaction
    4. 从 configuration 获取 Executor
    5. 构造 DefaultSqlSession 对象

    我们先来看看常规的 environment 配置

    //配置environment环境
    <environments default="development">
        <environment id="development">
            /** 事务配置 type= JDBC、MANAGED 
            *  1.JDBC:这个配置直接简单使用了JDBC的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。
            *  2.MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。
            */
            <transactionManager type="JDBC" />
            /** 数据源类型:type = UNPOOLED、POOLED、JNDI 
            *  1.UNPOOLED:这个数据源的实现是每次被请求时简单打开和关闭连接。
            *  2.POOLED:这是JDBC连接对象的数据源连接池的实现。 
            *  3.JNDI:这个数据源的实现是为了使用如Spring或应用服务器这类的容器
            */
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/xhm" />
                <property name="username" value="root" />
                <property name="password" value="root" />
                //默认连接事务隔离级别
                <property name="defaultTransactionIsolationLevel" value=""/> 
            dataSource>
        environment>
    environments>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    environment 配置的解析在 2.1.5 节有分析。

    再 environment 配置中 transactionManager type=“JDBC” 和 dataSource type=“POOLED” ,则生成的transactionManager 为 JdbcTransactionFactory,DataSourceFactory 为 PooledDataSourceFactory 。

    回到 openSessionFromDataSource 方法,接着看看 getTransactionFactoryFromEnvironment 源码。

    org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#getTransactionFactoryFromEnvironment

    private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
        if (environment == null || environment.getTransactionFactory() == null) {
            return new ManagedTransactionFactory();
        }
        return environment.getTransactionFactory();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.1 创建Transaction

    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    
    • 1

    org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory#newTransaction(javax.sql.DataSource, org.apache.ibatis.session.TransactionIsolationLevel, boolean)

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

    直接通过工厂方法创建了一个JdbcTransaction对象,并传参DataSource ,事务隔离级别null,自动提交false三个参数,我们来看看 JdbcTransaction 这个类。

    public class JdbcTransaction implements Transaction {
    
        //数据库连接对象
        protected Connection connection;
    
        //数据库DataSource
        protected DataSource dataSource;
    
        //数据库隔离级别
        protected TransactionIsolationLevel level;
        // MEMO: We are aware of the typo. See #941
        //是否自动提交
        protected boolean autoCommmit;
    
        public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
            //设置dataSource和隔离级别,是否自动提交属性
            //这里隔离级别传过来的是null,表示使用数据库默认隔离级别,自动提交为false,表示不自动提交
            dataSource = ds;
            level = desiredLevel;
            autoCommmit = desiredAutoCommit;
        }
    
        @Override
        public Connection getConnection() throws SQLException {
            if (connection == null) {
                openConnection();
            }
            return connection;
        }
    
        //提交功能是通过Connection去完成的
        @Override
        public void commit() throws SQLException {
            if (connection != null && !connection.getAutoCommit()) {
                if (log.isDebugEnabled()) {
                    log.debug("Committing JDBC Connection [" + connection + "]");
                }
                connection.commit();
            }
        }
    
        //回滚功能是通过Connection去完成的
        @Override
        public void rollback() throws SQLException {
            if (connection != null && !connection.getAutoCommit()) {
                if (log.isDebugEnabled()) {
                    log.debug("Rolling back JDBC Connection [" + connection + "]");
                }
                connection.rollback();
            }
        }
    
        //关闭功能是通过Connection去完成的
        @Override
        public void close() throws SQLException {
            if (connection != null) {
                resetAutoCommit();
                if (log.isDebugEnabled()) {
                    log.debug("Closing JDBC Connection [" + connection + "]");
                }
                connection.close();
            }
        }
    
        //获取连接是通过dataSource来完成的
        protected void openConnection() throws SQLException {
            if (log.isDebugEnabled()) {
                log.debug("Opening JDBC Connection");
            }
            connection = dataSource.getConnection();
            if (level != null) {
                connection.setTransactionIsolation(level.getLevel());
            }
            setDesiredAutoCommit(autoCommmit);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    JdbcTransaction主要维护了一个默认autoCommit为false的Connection对象,对事物的提交,回滚,关闭等都是接见通过Connection完成的。

    1.2 创建Executor

    回到 openSessionFromDataSource 方法,看下面这行代码

    // 创建Executor,即执行器
    // 它是真正用来Java和数据库交互操作的类,后面会展开说。
    final Executor executor = configuration.newExecutor(tx, execType);
    
    • 1
    • 2
    • 3

    org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)

    //创建一个执行器,默认是SIMPLE
    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        //根据executorType来创建相应的执行器,Configuration默认是SIMPLE
        if (ExecutorType.BATCH == executorType) {
            executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
            executor = new ReuseExecutor(this, transaction);
        } else {
            //创建SimpleExecutor实例,并且包含Configuration和transaction属性
            executor = new SimpleExecutor(this, transaction);
        }
    
        //如果要求缓存,生成另一种CachingExecutor,装饰者模式,默认都是返回CachingExecutor
        /**
             * 二级缓存开关配置示例
             * 
             *   
             * 
             */
        // cacheEnabled 默认为 true
        if (cacheEnabled) {
            //CachingExecutor使用装饰器模式,将executor的功能添加上了二级缓存的功能,二级缓存会单独文章来讲
            executor = new CachingExecutor(executor);
        }
    
        //此处调用插件,通过插件可以改变Executor行为,此处我们后面有机会在分析
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    executor 包含了 Configuration 和刚刚创建的 Transaction,默认的执行器为 SimpleExecutor,如果开启了二级缓存(默认开启一级缓存),则 CachingExecutor 会包装 SimpleExecutor,然后依次调用拦截器的 plugin 方法返回一个被代理过的Executor对象。

    executor 默认情况下就是 CachingExecutor ,只不过如果二级缓存没有开启,则实际还是调用 SimpleExecutor 去执行(CachingExecutor 包装了 SimpleExecutor 类型),后面会分析该类。

    1.3 构造DefaultSqlSession对象

    // 创建DefaultSqlSession对象返回,其实现了SqlSession接口
    return new DefaultSqlSession(configuration, executor, autoCommit);
    
    • 1
    • 2

    大致看一下 DefaultSqlSession 类

    public class DefaultSqlSession implements SqlSession {
    
        /**
         * mybatis全局配置新
         */
        private final Configuration configuration;
    
        /**
         * SQL执行器
         */
        private final Executor executor;
    
        /**
         * 是否自动提交
         */
        private final boolean autoCommit;
        private boolean dirty;
        private List<Cursor<?>> cursorList;
    
        public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
            this.configuration = configuration;
            this.executor = executor;
            this.dirty = false;
            this.autoCommit = autoCommit;
        }
    
        // ...
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    SqlSession的所有查询接口最后都归结位Exector的方法调用,后面在分析。

    好了,今天的内容到这里就结束了,我是 【J3】关注我,我们下期见


    • 由于博主才疏学浅,难免会有纰漏,假如你发现了错误或偏见的地方,还望留言给我指出来,我会对其加以修正。

    • 如果你觉得文章还不错,你的转发、分享、点赞、留言就是对我最大的鼓励。

    • 感谢您的阅读,十分欢迎并感谢您的关注。

  • 相关阅读:
    3分钟了解全民秒杀电商平台营销模式,寄售秒杀模式详解
    electron-vite工具打包后通过内置配置文件动态修改接口地址实现方法
    Vue开发实战二十二:列表中表格动态合并的实现
    适用于 Mac 的 10 款最佳数据恢复工具
    QCC51XX---QACT用户指南
    基于Arduino开发板的太阳能灯光控制器
    MYSQL CONCAT函数
    C语言天花板——指针(进阶1)
    深潜Kotlin协程(十五):测试 Kotlin 协程
    centos7.6部署fabric以及链码
  • 原文地址:https://blog.csdn.net/qq_40399646/article/details/127834643