Mybaits 源码解析 (四)----- SqlSession的创建过程(看懂框架源码再也不用死记硬背面试题)
sqlsession是mybatis的核心接口之一,是myabtis接口层的主要组成部分,对外提供了mybatis常用的api。myabtis提供了两个sqlsesion接口的实现,常用的实现类是defaultsqlsession。它相当于一个数据库连接对象,在一个sqlsession中可以执行多条sql语句。
创建sqlsession
前面的两篇文章我们已经得到了sqlsessionfactory
,那么sqlsession
将由sqlsessionfactory
进行创建。
sqlsession sqlsession=sqlsessionfactory.opensession();
我们就来看看这个sqlsessionfactory
的 opensession
方法是如何创建sqlsession对象的。根据上面的分析,这里的sqlsessionfactory
类型对象其实是一个defaultsqlsessionfactory
对象,因此,需要到defaultsqlsessionfactory
类中去看opensession
方法。
@override public sqlsession opensession() { return opensessionfromdatasource(configuration.getdefaultexecutortype(), null, false); }
调用了opensessionfromdatasource方法,并且第一个参数获取了默认的执行器类型,第二个参数为null,第三个参数为false,看看这个默认的执行器类型是啥
默认的执行器类型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(); } }
主要包含以下几个步骤:
- 首先从configuration获取environment对象,里面主要包含了datasource和transactionfactory对象
- 创建transactionfactory
- 创建transaction
- 从configuration获取executor
- 构造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的方法调用。后面文章我们来分析其调用流程