欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Mybaits 源码解析 (四)----- SqlSession的创建过程(看懂框架源码再也不用死记硬背面试题)

程序员文章站 2022-06-24 12:24:52
SqlSession是mybatis的核心接口之一,是myabtis接口层的主要组成部分,对外提供了mybatis常用的api。myabtis提供了两个SqlSesion接口的实现,常用的实现类是DefaultSqlSession。它相当于一个数据库连接对象,在一个SqlSession中可以执行多条 ......

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

创建sqlsession

前面的两篇文章我们已经得到了sqlsessionfactory,那么sqlsession将由sqlsessionfactory进行创建。

sqlsession sqlsession=sqlsessionfactory.opensession();

我们就来看看这个sqlsessionfactoryopensession方法是如何创建sqlsession对象的。根据上面的分析,这里的sqlsessionfactory类型对象其实是一个defaultsqlsessionfactory对象,因此,需要到defaultsqlsessionfactory类中去看opensession方法。

  @override
  public sqlsession opensession() {
    return opensessionfromdatasource(configuration.getdefaultexecutortype(), null, false);
  }

调用了opensessionfromdatasource方法,并且第一个参数获取了默认的执行器类型,第二个参数为null,第三个参数为false,看看这个默认的执行器类型是啥

Mybaits 源码解析 (四)----- SqlSession的创建过程(看懂框架源码再也不用死记硬背面试题)  Mybaits 源码解析 (四)----- SqlSession的创建过程(看懂框架源码再也不用死记硬背面试题)

默认的执行器类型simple,我们跟进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);
        throw exceptionfactory.wrapexception("error opening session.  cause: " + e, e);
    } finally {
        errorcontext.instance().reset();
    }
}

主要包含以下几个步骤:

  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>

还记得前面文章是怎么解析environments的吗,mybaits 源码解析 (二)----- 根据配置文件创建sqlsessionfactory(configuration的创建过程),我们简单的回顾一下

private void environmentselement(xnode context) throws exception {
    if (context != null) {
        if (environment == null) {
            // 获取 default 属性
            environment = context.getstringattribute("default");
        }
        for (xnode child : context.getchildren()) {
            // 获取 id 属性
            string id = child.getstringattribute("id");
            /*
             * 检测当前 environment 节点的 id 与其父节点 environments 的属性 default 
             * 内容是否一致,一致则返回 true,否则返回 false
             * 将其default属性值与子元素environment的id属性值相等的子元素设置为当前使用的environment对象
             */
            if (isspecifiedenvironment(id)) {
                // 将environment中的transactionmanager标签转换为transactionfactory对象
                transactionfactory txfactory = transactionmanagerelement(child.evalnode("transactionmanager"));
                // 将environment中的datasource标签转换为datasourcefactory对象
                datasourcefactory dsfactory = datasourceelement(child.evalnode("datasource"));
                // 创建 datasource 对象
                datasource datasource = dsfactory.getdatasource();
                environment.builder environmentbuilder = new environment.builder(id)
                    .transactionfactory(txfactory)
                    .datasource(datasource);
                // 构建 environment 对象,并设置到 configuration 中
                configuration.setenvironment(environmentbuilder.build());
            }
        }
    }
}

private transactionfactory transactionmanagerelement(xnode context) throws exception {
    if (context != null) {
        string type = context.getstringattribute("type");
        properties props = context.getchildrenasproperties();
        //通过别名获取class,并实例化
        transactionfactory factory = (transactionfactory)this.resolveclass(type).newinstance();
        factory.setproperties(props);
        return factory;
    } else {
        throw new builderexception("environment declaration requires a transactionfactory.");
    }
}

private datasourcefactory datasourceelement(xnode context) throws exception {
    if (context != null) {
        string type = context.getstringattribute("type");
        //通过别名获取class,并实例化
        properties props = context.getchildrenasproperties();
        datasourcefactory factory = (datasourcefactory)this.resolveclass(type).newinstance();
        factory.setproperties(props);
        return factory;
    } else {
        throw new builderexception("environment declaration requires a datasourcefactory.");
    }
}

获取transactionfactory

我们的environment配置中transactionmanager type="jdbc"和datasource type="pooled",则生成的transactionmanager为jdbctransactionfactory,datasourcefactory为pooleddatasourcefactory

我们回到opensessionfromdatasource,接着看看gettransactionfactoryfromenvironment方法

    private transactionfactory gettransactionfactoryfromenvironment(environment environment) {
        return (transactionfactory)(environment != null && environment.gettransactionfactory() != null ? environment.gettransactionfactory() : new managedtransactionfactory());
    }

创建transaction

很明显 environment.gettransactionfactory() 就是jdbctransactionfactory,看看这个工厂是如何创建transaction的

public transaction newtransaction(datasource ds, transactionisolationlevel level, boolean autocommit) {
    return new jdbctransaction(ds, level, autocommit);
}

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

public class jdbctransaction implements transaction {
    //数据库连接对象
    protected connection connection;
    //数据库datasource
    protected datasource datasource;
    //数据库隔离级别
    protected transactionisolationlevel level;
    //是否自动提交
    protected boolean autocommmit;

    public jdbctransaction(datasource ds, transactionisolationlevel desiredlevel, boolean desiredautocommit) {
        //设置datasource和隔离级别,是否自动提交属性
        //这里隔离级别传过来的是null,表示使用数据库默认隔离级别,自动提交为false,表示不自动提交
        this.datasource = ds;
        this.level = desiredlevel;
        this.autocommmit = desiredautocommit;
    }

     public connection getconnection() throws sqlexception {
        if (this.connection == null) {
            this.openconnection();
        }

        return this.connection;
    }

    //提交功能是通过connection去完成的
    public void commit() throws sqlexception {
        if (this.connection != null && !this.connection.getautocommit()) {
            if (log.isdebugenabled()) {
                log.debug("committing jdbc connection [" + this.connection + "]");
            }

            this.connection.commit();
        }

    }

    //回滚功能是通过connection去完成的
    public void rollback() throws sqlexception {
        if (this.connection != null && !this.connection.getautocommit()) {
            if (log.isdebugenabled()) {
                log.debug("rolling back jdbc connection [" + this.connection + "]");
            }

            this.connection.rollback();
        }

    }

    //关闭功能是通过connection去完成的
    public void close() throws sqlexception {
        if (this.connection != null) {
            this.resetautocommit();
            if (log.isdebugenabled()) {
                log.debug("closing jdbc connection [" + this.connection + "]");
            }

            this.connection.close();
        }

    }
    
    //获取连接是通过datasource来完成的
    protected void openconnection() throws sqlexception {
        if (log.isdebugenabled()) {
            log.debug("opening jdbc connection");
        }

        this.connection = this.datasource.getconnection();
        if (this.level != null) {
            this.connection.settransactionisolation(this.level.getlevel());
        }

        this.setdesiredautocommit(this.autocommmit);
    }
}

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

创建executor

//创建一个执行器,默认是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
    /**
     * 二级缓存开关配置示例
     * <settings>
     *   <setting name="cacheenabled" value="true"/>
     * </settings>
     */
    if (cacheenabled) {
      //cachingexecutor使用装饰器模式,将executor的功能添加上了二级缓存的功能,二级缓存会单独文章来讲
      executor = new cachingexecutor(executor);
    }
    //此处调用插件,通过插件可以改变executor行为,此处我们后面单独文章讲
    executor = (executor) interceptorchain.pluginall(executor);
    return executor;
}

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

cachingexecutor 对象里面包含了刚创建的simpleexecutor,后面文章我们会及具体讲这个类

public class cachingexecutor implements executor {
    private executor delegate;
    private transactionalcachemanager tcm = new transactionalcachemanager();

    public cachingexecutor(executor delegate) {
        this.delegate = delegate;
        delegate.setexecutorwrapper(this);
    }
    //略
}

构造defaultsqlsession对象

new defaultsqlsession(this.configuration, executor, autocommit);

传参configuration和刚生成的executor,我们来简单看看

public class defaultsqlsession implements sqlsession {

  /**
   * mybatis全局配置新
   */
  private final configuration configuration;
  /**
   * sql执行器
   */
  private final executor executor;

  /**
   * 是否自动提交
   */
  private final boolean autocommit;

  private list<cursor<?>> cursorlist;
  
  public defaultsqlsession(configuration configuration, executor executor, boolean autocommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.dirty = false;
        this.autocommit = autocommit;
  }
  
  @override
  public <t> t selectone(string statement) {
    return this.<t>selectone(statement, null);
  }

  @override
  public <t> t selectone(string statement, object parameter) {
    // popular vote was to return null on 0 results and throw exception on too many.
    list<t> list = this.<t>selectlist(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new toomanyresultsexception("expected one result (or null) to be returned by selectone(), but found: " + list.size());
    } else {
      return null;
    }
  }
  @override
  public <e> list<e> selectlist(string statement) {
    return this.selectlist(statement, null);
  }

  @override
  public <e> list<e> selectlist(string statement, object parameter) {
    return this.selectlist(statement, parameter, rowbounds.default);
  }

  @override
  public <e> list<e> selectlist(string statement, object parameter, rowbounds rowbounds) {
    try {
      mappedstatement ms = configuration.getmappedstatement(statement);
      return executor.query(ms, wrapcollection(parameter), rowbounds, executor.no_result_handler);
    } catch (exception e) {
      throw exceptionfactory.wrapexception("error querying database.  cause: " + e, e);
    } finally {
      errorcontext.instance().reset();
    }
  }
  
  //略....update等方法
}

sqlsession的所有查询接口最后都归结位exector的方法调用。后面文章我们来分析其调用流程