MyBatis 四大組件之 Executor 執(zhí)行器

數(shù)據(jù)庫驅(qū)動連接管理和SQL解析的管理組件之 Executor 執(zhí)行器

MyBatis 四大組件之 Executor 執(zhí)行器

? ? 每一個 SqlSession 都會擁有一個 Executor 對象,這個對象負(fù)責(zé)增刪改查的具體操作,我們可以簡單的將它理解為 JDBC中Statement 的封裝版。

Executor的繼承結(jié)構(gòu)

如圖所示,位于繼承體系最頂層的是 Executor 執(zhí)行器,它有兩個實現(xiàn)類,分別是BaseExecutorCachingExecutor。

BaseExecutor是一個抽象類,這種通過抽象的實現(xiàn)接口的方式是適配器設(shè)計模式之接口適配的體現(xiàn),是 Executor 的默認(rèn)實現(xiàn),實現(xiàn)了大部分 Executor 接口定義的功能,降低了接口實現(xiàn)的難度。

BaseExecutor 的子類有三個,分別是SimpleExecutor、ReuseExecutorBatchExecutor

SimpleExecutor:簡單執(zhí)行器。是MyBatis的默認(rèn)執(zhí)行器,每執(zhí)行一次update或select,就開啟一個Statement對象,用完就直接關(guān)閉Statement對象(可以是Statement或者PreparedStatment 對象)

ReuseExecutor:可重用執(zhí)行器,這里的重用是指重復(fù)使用Statement。它會在內(nèi)部使用一個Map把創(chuàng)建的Statement都緩存起來,每次執(zhí)行SQL命令時候都會去判斷是否存在基于該SQL的Statement對象,如果存在Statement對象并且對應(yīng)的Connection還沒有關(guān)閉情況下就繼續(xù)使用之前的Statement對象,并將其緩存起來。因為每次Sqlsession都有一個新的Executor對象,所以緩存在ReuseExecutor的Statement對象作用域是同一個Sqlsession.

BatchExecutor: 批處理執(zhí)行器,用于將多個SQL一次性輸出到數(shù)據(jù)庫

CachingExecutor: 緩存執(zhí)行器,先從緩存中查詢結(jié)果,如果存在,就返回;如果不存在,再委托給 Executor delegate 去數(shù)據(jù)庫中取,delegate 可以是上面任何一個執(zhí)行器。

Executor的設(shè)計用現(xiàn)實做例子

Executor執(zhí)行器是MyBatis的重要組件。

Executor相當(dāng)于外包的Boss,它定義了甲方(SQL)需要干的活(Executor的主要方法),boss接到開發(fā)任務(wù)時候會先找到項目經(jīng)理(CachingExecutor),項目經(jīng)理幾乎不懂技術(shù),它主要和技術(shù)leader(BaseExecutor)打交道,技術(shù)leader主要負(fù)責(zé)框架搭建,具體工作都會交給下面的程序員來做。但程序員也有優(yōu)劣,高級程序員(BatchExecutor)、中級程序員(ReuseExecutor)、初級程序員(SimpleExecutor),

它們干的活也不一樣。一般有新的項目需求傳達(dá)到項目經(jīng)理這里,項目經(jīng)理先判斷自己手里有沒有現(xiàn)成的類庫或者項目直接套用(Cache),有的話就直接讓技術(shù)leader拿來直接套用就好,沒有的話就需要搭建框架,再把框架存入本地類庫中,再進(jìn)行解析。




Executor創(chuàng)建過程以及源碼分析

直接使用MyBatis方式來分析。

創(chuàng)建完 SqlSessionFactory 之后,調(diào)用其openSession方法:

SqlSession sqlSession=factory.openSession();

SqlSessionFactory 的默認(rèn)實現(xiàn)是 DefaultSqlSessionFactory,所以我們需要關(guān)心的就是 DefaultSqlSessionFactory 中的 openSession() 方法

openSession 調(diào)用的是openSessionFromDataSource方法,傳遞執(zhí)行器的類型,方法傳播級別,是否自動提交,然后在 openSessionFromDataSource 方法中會創(chuàng)建一個執(zhí)行器


調(diào)用 newExecutor 方法,根據(jù)傳入的 ExecutorType 類型來判斷是哪種執(zhí)行器,然后執(zhí)行相應(yīng)的邏輯


ExecutorType 的選擇:

ExecutorType 來決定 Configuration 對象創(chuàng)建何種類型的執(zhí)行器,它的賦值可以通過兩個地方進(jìn)行賦值:

可以通過 settings 標(biāo)簽來設(shè)置當(dāng)前工程中所有的 SqlSession 對象使用默認(rèn)的 Executor

<settings><!--取值范圍 SIMPLE,REUSE,BATCH--><setting name="defaultExecutorType"value="SIMPLE"/></settings>

另外一種直接通過Java對方法賦值的方式

session=factory.openSession(ExecutorType.BATCH);

ExecutorType 是一個枚舉,它只有三個值 SIMPLE, REUSE, BATCH

創(chuàng)建完成 Executor 之后,會把 Executor 執(zhí)行器放入一個 DefaultSqlSession 對象中來對四個屬性進(jìn)行賦值,他們分別是configuration、executor、dirty、autoCommit

Executor接口的主要方法

增、更、刪 :

int update(MappedStatement var1, Object var2)throws SQLException;

查詢,帶分頁,帶緩存,BoundSql :

<E>? List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6)throws SQLException;

??查詢,帶分頁:

<E>?List?<E>?query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4)throws SQLException;

通過游標(biāo)查詢(數(shù)據(jù)量大時候用):

<E>?Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3)throws SQLException;

刷新批處理語句:

List<BatchResult> flushStatements()throws SQLException;

提交:

void commit(boolean var1)throws SQLException;

回滾:

void rollback(boolean var1)throws SQLException;

創(chuàng)建緩存KEY:

CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);

判斷是否緩存了:

boolean isCached(MappedStatement var1, CacheKey var2);

清除session緩存:

void clearLocalCache();

延遲加載:

void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class var5);

Transaction getTransaction();

void close(boolean var1);

boolean isClosed();

void setExecutorWrapper(Executor var1);


Executor 接口的方法還是比較多的,這里我們就幾個主要的方法和調(diào)用流程來做一個簡單的描述


緩存機制角度:



從查詢query方法角度解剖:

query 方法有兩種形式,一種是直接查詢;一種是從緩存中查詢,下面來看一下源碼以及時序圖:

當(dāng)有一個查詢請求訪問的時候,首先會經(jīng)過 Executor 的實現(xiàn)類 CachingExecutor,先從緩存中查詢 SQL 是否是第一次執(zhí)行,如果是第一次執(zhí)行的話,那么就直接執(zhí)行 SQL 語句,并創(chuàng)建緩存,如果第二次訪問相同的 SQL 語句的話,那么就會直接從緩存中提取

CachingExecutor:

// 第一次查詢,并創(chuàng)建緩存


MapperStatement維護了一條 select、update、delete、insert 節(jié)點的封裝,包括資源(resource),配置(configuration),SqlSource(sql源文件)等。使用Configuration 的 getMappedStatement 方法來獲取 MappedStatement 對象。

BoundSql這個類包括 SQL 的基本信息,基本的 SQL 語句,參數(shù)映射,參數(shù)類型等

調(diào)用到 CachingExecutor 類中的 query 查詢緩存的方法


Executor 執(zhí)行器所起的作用相當(dāng)于是管理 StatementHandler 的整個生命周期的工作,包括創(chuàng)建、初始化、解析、關(guān)閉。

ReuseExecutor 完成的 doQuery 工作:幾乎和 SimpleExecutor 完成的工作一樣,其內(nèi)部不過是使用一個 Map 來存儲每次執(zhí)行的查詢語句,為后面的 SQL 重用作準(zhǔn)備。

BatchExecutor 完成的 doQuery 工作和 SimpleExecutor 完成的工作一樣。

Executor的執(zhí)行查詢的大致流程時序圖:


update() 方法(update 鏈?zhǔn)莍nsert、update、delete 在語義上其實都是更新的意思)

ReuseExecutor 完成的 doUpdate 工作:幾乎和 SimpleExecutor完成的工作一樣,其內(nèi)部不過是使用一個Map來存儲每次執(zhí)行的更新語句,為后面的SQL重用作準(zhǔn)備。

BatchExecutor 完成的 doUpdate 工作:和 SimpleExecutor 完成的工作相似,只是其內(nèi)部有一個 List 列表來一次行的存儲多個 Statement,用于將多個 sql 語句一次性輸送到數(shù)據(jù)庫執(zhí)行。

update時序圖:

queryCursor()方法

我們查閱其源碼的時候,在執(zhí)行器的執(zhí)行過程中并沒有發(fā)現(xiàn)其與 query 方法有任何不同之處,但是在doQueryCursor方法中我們可以看到它返回了一個 cursor 對象,網(wǎng)上搜索cursor 的相關(guān)資料并查閱其基本結(jié)構(gòu),得出來的結(jié)論是:用于逐條讀取 SQL 語句,應(yīng)對數(shù)據(jù)量

// 查詢可以返回Cursor<T>類型的數(shù)據(jù),類似于JDBC里的ResultSet類,

// 當(dāng)查詢百萬級的數(shù)據(jù)的時候,使用游標(biāo)可以節(jié)省內(nèi)存的消耗,不需要一次性取出所有數(shù)據(jù),可以進(jìn)行逐條處理或逐條取出部分批量處理。

public? interfaceCursor<T>extendsCloseable,Iterable<T>? ?{

booleanisOpen();

booleanisConsumed();

intgetCurrentIndex();

}

flushStatements() 方法

主要用來釋放 statement,或者用于 ReuseExecutor 和 BatchExecutor 來刷新緩存。

createCacheKey() 方法

createCacheKey() 方法主要由 BaseExecutor 來執(zhí)行并創(chuàng)建緩存,MyBatis 中的緩存分為一級緩存和二級緩存

Executor 中的其他方法

Executor 中還有其他方法,提交 commit,回滾 rollback,判斷是否時候緩存 isCached,關(guān)閉 close,獲取事務(wù) getTransaction 立即清除本地緩存 clearLocalCache 等

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容