MyBatis学习笔记(二) Executor
一、概述
当我们打开一个sqlsession的时候,我们就完成了操作数据库的第一步,那mybatis是如何执行sql的呢?其实mybatis的增删改查都是通过executor执行的,executor和sqlsession绑定在一起,由configuration类的newexecutor方法创建。
二、executor类图
首先,顶层接口是executor,有两个实现类,分别是baseexecutor和cachingexecutor,cachingexecutor用于二级缓存,而baseexecutor则用于一级缓存及基础的操作,baseexecutor是一个抽象类,又有三个实现,分别是simpleexecutor,batchexecutor,reuseexecutor,而具体使用哪一个executor则是可以在mybatis-config.xml中进行配置的,配置方式如下:
<settings> <!--simple、reuse、batch--> <setting name="defaultexecutortype" value="reuse"/> </settings>
如果没有配置executor,默认情况下是simpleexecutor。
三、各个executor介绍
1.baseexecutor
相当于一个基础,实现了executor的方法,但是只是做一些准备工作,比如查询的cachekey定义等,以及公共方法的定义,比如close、commit、rollback方法等,而具体的执行则是定义了抽象方法doupdate、doquery,这些将由baseexecutor的子类实现,如下
baseexecutor这里采用了模板方法模式
protected abstract int doupdate(mappedstatement ms, object parameter) throws sqlexception; protected abstract list<batchresult> doflushstatements(boolean isrollback) throws sqlexception; protected abstract <e> list<e> doquery(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, boundsql boundsql) throws sqlexception; protected abstract <e> cursor<e> doquerycursor(mappedstatement ms, object parameter, rowbounds rowbounds, boundsql boundsql) throws sqlexception;
1.1 simpleexecutor
最简单的执行器,根据对应的sql直接执行,每执行一次update或select,就开启一个statement对象,用完立刻关闭statement对象。(可以是statement或preparestatement对象)
1.2 batchexecutor
执行update(没有select,jdbc批处理不支持select),将所有sql都添加到批处理中(addbatch()),等待统一执行(executebatch()),它缓存了多个statement对象,每个statement对象都是addbatch()完毕后,等待逐一执行executebatch()批处理的;batchexecutor相当于维护了多个桶,每个桶里都装了很多属于自己的sql,就像苹果蓝里装了很多苹果,番茄蓝里装了很多番茄,最后,再统一倒进仓库。(可以是statement或preparestatement对象)
通常需要注意的是批量更新操作,由于内部有缓存的实现,使用完成后记得调用flushstatements
来清除缓存。
@override public int doupdate(mappedstatement ms, object parameterobject) throws sqlexception { final configuration configuration = ms.getconfiguration(); final statementhandler handler = configuration.newstatementhandler(this, ms, parameterobject, rowbounds.default, null, null); final boundsql boundsql = handler.getboundsql(); final string sql = boundsql.getsql(); final statement stmt; if (sql.equals(currentsql) && ms.equals(currentstatement)) { int last = statementlist.size() - 1; stmt = statementlist.get(last); applytransactiontimeout(stmt); handler.parameterize(stmt);//fix issues 322 batchresult batchresult = batchresultlist.get(last); batchresult.addparameterobject(parameterobject); } else { connection connection = getconnection(ms.getstatementlog()); stmt = handler.prepare(connection, transaction.gettimeout()); handler.parameterize(stmt); //fix issues 322 currentsql = sql; currentstatement = ms; statementlist.add(stmt); batchresultlist.add(new batchresult(ms, sql, parameterobject)); } // handler.parameterize(stmt); handler.batch(stmt); return batch_update_return_value; }
1.3 reuseexecutor
flushstatements
来清除map。private final map<string, statement> statementmap = new hashmap<string, statement>();
2.cachingexecutor
先从缓存中获取查询结果,存在就返回,不存在,再委托给executor delegate去数据库取,delegate可以是上面任一的simpleexecutor、reuseexecutor、batchexecutor。
这几个executor的生命周期都是局限于sqlsession范围内。
public cachingexecutor(executor delegate) { this.delegate = delegate; delegate.setexecutorwrapper(this); } ...... @override public int update(mappedstatement ms, object parameterobject) throws sqlexception { flushcacheifrequired(ms); return delegate.update(ms, parameterobject); } @override public <e> list<e> query(mappedstatement ms, object parameterobject, rowbounds rowbounds, resulthandler resulthandler) throws sqlexception { boundsql boundsql = ms.getboundsql(parameterobject); cachekey key = createcachekey(ms, parameterobject, rowbounds, boundsql); return query(ms, parameterobject, rowbounds, resulthandler, key, boundsql); } @override public <e> cursor<e> querycursor(mappedstatement ms, object parameter, rowbounds rowbounds) throws sqlexception { flushcacheifrequired(ms); return delegate.querycursor(ms, parameter, rowbounds); } @override public <e> list<e> query(mappedstatement ms, object parameterobject, rowbounds rowbounds, resulthandler resulthandler, cachekey key, boundsql boundsql) throws sqlexception { cache cache = ms.getcache(); if (cache != null) { flushcacheifrequired(ms); if (ms.isusecache() && resulthandler == null) { ensurenooutparams(ms, parameterobject, boundsql); @suppresswarnings("unchecked") list<e> list = (list<e>) tcm.getobject(cache, key); if (list == null) { list = delegate.<e> query(ms, parameterobject, rowbounds, resulthandler, key, boundsql); tcm.putobject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<e> query(ms, parameterobject, rowbounds, resulthandler, key, boundsql); } ......
四、executor初始化
1.先调用startmanagedsession
public void startmanagedsession() { this.localsqlsession.set(opensession()); }
2.调用startmanagedsession
方法来使sqlsessionmanager内部维护的localsqlsession
变量生效,这一步操作中会涉及到对executor的获取,代码如下:
@override public sqlsession opensession() { return sqlsessionfactory.opensession(); } @override public sqlsession opensession() { return opensessionfromdatasource(configuration.getdefaultexecutortype(), null, 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); //这里根据不同的executortype获取不同的executor final executor executor = configuration.newexecutor(tx, exectype); 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(); } }
3.最后就是文章开头所说的newexecutor方法创建返回不同的executor
public executor newexecutor(transaction transaction, executortype executortype) { executortype = executortype == null ? defaultexecutortype : executortype; executortype = executortype == null ? executortype.simple : executortype; executor executor; 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; }
参考文章:
https://www.jianshu.com/p/53cc886067b1
https://blog.csdn.net/cleargreen/article/details/80614362
上一篇: 游戏合成装备算法面试题
下一篇: IO流简要总结