日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

mybatis源码阅读(五) ---执行器Executor

發布時間:2023/12/3 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mybatis源码阅读(五) ---执行器Executor 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自??mybatis源碼閱讀(五) ---執行器Executor

1.?Executor接口設計與類結構圖

public interface Executor {ResultHandler NO_RESULT_HANDLER = null;// 執行update,delete,insert三種類型的sql語句int update(MappedStatement ms, Object parameter) throws SQLException;// 執行select類型的SQL語句,返回值分為結果對象列表和游標對象<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;// 批量執行SQL語句List<BatchResult> flushStatements() throws SQLException;// 提交事務void commit(boolean required) throws SQLException;// 事務回滾void rollback(boolean required) throws SQLException;// 創建緩存中用到的CacheKey對象CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);boolean isCached(MappedStatement ms, CacheKey key);// 清空一級緩存void clearLocalCache();// 延遲加載一級緩存中的數據void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);// 獲取事務對象Transaction getTransaction();// 關閉Executor對象void close(boolean forceRollback);// 檢測Executor對象是否關閉boolean isClosed();void setExecutorWrapper(Executor executor);}

簡單執行器SimpleExecutor:每執行一次update或select,就開啟一個Statement對象,用完立刻關閉Statement對象。(可以是Statement或PrepareStatement對象)

重用執行器ReuseExecutor:執行update或select,以sql作為key查找Statement對象,存在就使用,不存在就創建,用完后,不關閉Statement對象,而是放置于Map<String, Statement>內,供下一次使用。(可以是Statement或PrepareStatement對象)

批量執行器BatchExecutor:執行update(沒有select,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch()),等待統一執行(executeBatch()),它緩存了多個Statement對象,每個Statement對象都是addBatch()完畢后,等待逐一執行executeBatch()批處理的;BatchExecutor相當于維護了多個桶,每個桶里都裝了很多屬于自己的SQL,就像蘋果藍里裝了很多蘋果,番茄藍里裝了很多番茄,最后,再統一倒進倉庫。(可以是Statement或PrepareStatement對象)

緩存執行器CachingExecutor:裝飾設計模式典范,先從緩存中獲取查詢結果,存在就返回,不存在,再委托給Executor delegate去數據庫取,delegate可以是上面任一的SimpleExecutor、ReuseExecutor、BatchExecutor。

無用執行器ClosedExecutor:毫無用處,讀者可自行查看其源碼,僅作為一種標識,和Serializable標記接口作用相當。

作用范圍:以上這五個執行器的作用范圍,都嚴格限制在SqlSession生命周期范圍內。

2. 基類BaseExecutor源碼解析

它是一個實現了Executor接口的抽象類,實現了接口中的大部分方法,其中就是使用了模板模式,它主要提供了緩存和事物管理的基本功能,不同的實現類,只要實現4個基本方法來完成數據庫的相關操作,這4個抽象方法:doUpdate()、doQuery()、doFlushStatement()、doQueryCursor。

源碼片段

protected Transaction transaction;// 實現事務的回滾和提交,關閉操作 protected Executor wrapper; // 其中封裝的Executor對象// 延遲加載隊列 protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads; // 下面兩個屬性是一級緩存用到的對象 protected PerpetualCache localCache; protected PerpetualCache localOutputParameterCache;protected Configuration configuration;// 嵌套查詢層級 protected int queryStack; private boolean closed; 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;

2.1 SimpleExecutor

@Override public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);stmt = prepareStatement(handler, ms.getStatementLog());return handler.update(stmt);} finally {closeStatement(stmt);} }@Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);//關閉statement對象} }

源碼很簡單,從configuration對象中去材料,交給handler去處理,處理完后,statement對象馬上關閉。

2.2 ReuseExecutor

執行器提供了Statement的重用功能,代碼片段如下:

// 緩存使用過的Statement對象,key是SQL語句,value是SQL對應的Statement對象 private final Map<String, Statement> statementMap = new HashMap<String, Statement>(); /*** 準備獲取Statement對象* @param handler* @param statementLog* @return* @throws SQLException*/ private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;BoundSql boundSql = handler.getBoundSql();String sql = boundSql.getSql();if (hasStatementFor(sql)) {// 檢測是否緩存了相同模式的SQL語句所對應的Statement對象stmt = getStatement(sql);// 從緩存中獲取statement對象applyTransactionTimeout(stmt);// 修改超時時間} else {Connection connection = getConnection(statementLog);stmt = handler.prepare(connection, transaction.getTimeout());putStatement(sql, stmt);}handler.parameterize(stmt);return stmt; }

那statement對象是什么時候關閉的呢?當事物提交回滾或者關閉時都需要關閉這些緩存的Statement對象,在BaseExecutor.commit(),rollback(),close()方法中都會掉用doFlushStatement()方法,所以在改方法中實現關閉Statement對象是非常合適。具體如下:

@Override public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {// 遍歷map集合并關閉其中的Statement對象for (Statement stmt : statementMap.values()) {closeStatement(stmt);}// 清空緩存statementMap.clear();// 返回空集合return Collections.emptyList(); }

2.3 BatchExecutor

BatchExecutor實現了批處理多條SQL語句的功能,需要注意的是在批處理執行SQL語句時,每次向數據庫發送的SQL語句條數是有上限,超過上限會拋出異常,所以批量發送SQL語句的時機是很重要的。

其中的核心字段含義如下:

//緩存多個Statement對象,每個Statement對象中都緩存了多條SQL語句private final List<Statement> statementList = new ArrayList<Statement>();//記錄批處理的結果 BatchResult中通過updateCounts字段(int[])記錄每個Statement執行批處理的結果private final List<BatchResult> batchResultList = new ArrayList<BatchResult>();// 記錄當前執行的SQL語句private String currentSql;// 記錄當前的MappedStatement對象private MappedStatement currentStatement;

方法實現如下:

@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();// 本次執行的sqlfinal String sql = boundSql.getSql();final Statement stmt;// 如果當前執行的SQL與上一次執行的SQL相同且對應的MappedStatement對象相同if (sql.equals(currentSql) && ms.equals(currentStatement)) {int last = statementList.size() - 1;// 已經存在Statement,取出最后一個Statement,有序stmt = statementList.get(last);applyTransactionTimeout(stmt);handler.parameterize(stmt);//fix Issues 322BatchResult batchResult = batchResultList.get(last);batchResult.addParameterObject(parameterObject);} else {// 尚不存在,新建StatementConnection connection = getConnection(ms.getStatementLog());stmt = handler.prepare(connection, transaction.getTimeout());handler.parameterize(stmt); //fix Issues 322currentSql = sql;currentStatement = ms;// 放到statementList緩存statementList.add(stmt);batchResultList.add(new BatchResult(ms, sql, parameterObject));} // handler.parameterize(stmt);// 將sql以addBatch()的方式,添加到Statement中(該步驟由StatementHandler內部完成)handler.batch(stmt);return BATCH_UPDATE_RETURN_VALUE; }

需要注意的是sql.equals(currentSql)和statementList.get(last),充分說明了其有序邏輯:AABB,將生成2個Statement對象;AABBAA,將生成3個Statement對象,而不是2個。因為,只要sql有變化,將導致生成新的Statement對象。

緩存了這么多Statement批處理對象,何時執行它們?在doFlushStatements()方法中完成執行stmt.executeBatch(),隨即關閉這些Statement對象。

2.4 CachingExecutor

CachingExecutor是一個Executor接口的裝飾器,它為Executor對象增加了二級緩存的相關功能。

//委托的執行器對象,可以是SimpleExecutor、ReuseExecutor、BatchExecutor任一一個 private final Executor delegate; //管理使用的二級緩存對像 private final TransactionalCacheManager tcm = new TransactionalCacheManager();

query方法執行的查詢操作步驟:

(1)獲取BoundSql對象,創建查詢語句對應的CacheKey對象,

(2)檢測是否開啟了二級緩存,如果沒有,則指教調用delegate對象的query()方法查詢,如果開啟了,則繼續后面的步驟

(3)檢測查詢是否包含輸出類型的參數,如果是,則報錯

(4)調用TransactionalCacheManager.getObject()方法查詢二級緩存,如果二級緩存中查找到相應的結果,則直接返回結果。

(5)如果二級緩存沒有相應的結果對象,在調用delegate對象的query()方法查詢。最后將得到的結果放入

TransactionalCache.entriesToAddOnCommit集合中保存。

@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// (1)BoundSql boundSql = ms.getBoundSql(parameterObject);CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }@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) {// (2)flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {// (3)ensureNoOutParams(ms, boundSql);// (4)@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {// (5)list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}// 沒有啟動二級緩存,只調用底層Executor查詢return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }

?

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的mybatis源码阅读(五) ---执行器Executor的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。