Mybatis源碼閱讀(四):核心接口4.2——Executor(下)

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************
SimpleExecutor

接上一章博客繼續。

SImpleExecutor繼承了BaseExecutor類,是最簡單的Executor實現。Executor使用了模板方法模式,所以SimpleExecutor不必在關心一級緩存等操作,只需要實現基本的4個方法。

首先看doQuery

/*** 執行查詢操作* @param ms* @param parameter* @param rowBounds* @param resultHandler* @param boundSql* @param <E>* @return* @throws SQLException*/
@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();// 創建RoutingStatementHandler,根據MappedStatement.statementType決定選擇具體的StatementHandlerStatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 完成Statement的初始化。先創建對應的StatementHandler,再調用parameterize方法處理占位符stmt = prepareStatement(handler, ms.getStatementLog());// 調用StatementHandler.query方法執行SQLreturn handler.query(stmt, resultHandler);} finally {closeStatement(stmt);}
}/*** 創建Statement并處理占位符* @param handler* @param statementLog* @return* @throws SQLException*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);// 創建Statementstmt = handler.prepare(connection, transaction.getTimeout());// 處理占位符handler.parameterize(stmt);return stmt;
}

代碼很簡單,先獲取配置對象,再創建RoutingStatementHandler對象,對Statement進行初始化,最后調用query方法完成查詢操作。doQueryCursor、update方法與之類似,不進行介紹。SimpleExecutor不提供批量處理SQL的功能所以doFlushStatement方法直接返回空集合。
ReuseExecutor

傳統的JDBC編程中,Statement對象重用是最常見的一種優化手段,這樣可以減少SQL的預編譯以及創建、銷毀Statement對象的開銷,從而提高性能。

ReuseExecutor提供了Statement重用的功能,通過statementMap字段緩存使用過的Statement對象,key是sql,value是statement。

該類中的doQuery、doQueryCursor、doUpdate與SimpleExecutor中的實現一樣, 不同的是PrepareStatement方法。SimpleExecutor每次都會通過JDBC的Connection創建新的Statement,而ReuseExecutor則先嘗試從statementMap中查找緩存的對象。

/*** 默認會從緩存中查找Statement* @param handler* @param statementLog* @return* @throws SQLException*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;BoundSql boundSql = handler.getBoundSql();//  獲取SQLString sql = boundSql.getSql();if (hasStatementFor(sql)) {stmt = getStatement(sql);// 修改事務超時時間applyTransactionTimeout(stmt);} else {// 獲取數據庫連接Connection connection = getConnection(statementLog);// 創建新的Statement放到statementMapstmt = handler.prepare(connection, transaction.getTimeout());putStatement(sql, stmt);}handler.parameterize(stmt);return stmt;
}

當事務提交、回滾、連接關閉時,需要銷毀這些緩存的Statement對象。在BaseExecutor中commit、rollback、close方法中,都會調用doFlushStatement方法,所以在doFlushStatement方法中關閉Statement比較合適。方法實現如下。

@Override
public List<BatchResult> doFlushStatements(boolean isRollback) {for (Statement stmt : statementMap.values()) {closeStatement(stmt);}statementMap.clear();return Collections.emptyList();
}

BatchExecutor

系統在執行一條sql語句的時候,會將SQQL語句以及相關參數通過網絡發送到數據庫。對于頻繁操作數據庫的系統,如果執行一條SQl就向數據庫發送一次請求,在網絡通信上會有很多性能折損。因此使用批量處理的方式進行優化,緩存多條SQL語句,在合適的時機將多條SQL打包發送給數據庫執行,從而減少網絡方面的開銷,提高性能。

需要注意,批量執行SQL的時候,每次向數據庫發送的SQL語句條數是有上限的,如果超出了這個上限,數據庫會拒絕執行這些SQL并拋出異常。

BatchExecutor實現了批量處理SQL的功能,核心字段如下。

/*** 緩存多個Statement,每個Statement都緩存了多條SQL*/
private final List<Statement> statementList = new ArrayList<>();
/*** 記錄批處理的結果*/
private final List<BatchResult> batchResultList = new ArrayList<>();
/*** 記錄當前執行的SQL*/
private String currentSql;
/*** 記錄當前執行的MappedStatement*/
private MappedStatement currentStatement;

JDBC只支持insert、update、delete的批處理,select不存在批處理一說,因此這里主要分析doUpdate方法。

doUpdate方法在添加一條SQL的時候,會先將currentSql字段記錄的SQl以及currentStatement記錄的MappedStatement對象與當前添加的SQL對比,如果相同則添加到同一個Statement對象中等待執行,不同則創建新的Statement對象并緩存到statementList集合中等待執行,代碼如下。

@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;// 如果sql和當前sql相同,這里的sql還有問號占位符,并且MappedStatement和當前Statement相同,就添加到同一個Statement對象中等待執行if (sql.equals(currentSql) && ms.equals(currentStatement)) {// 獲取最后一個Statement對象int last = statementList.size() - 1;stmt = statementList.get(last);applyTransactionTimeout(stmt);// 綁定實參,處理?占位符handler.parameterize(stmt);//fix Issues 322// 查找對應的BatchResult,記錄用戶傳入的實參BatchResult batchResult = batchResultList.get(last);batchResult.addParameterObject(parameterObject);} else {// 否則創建新的Statement緩存到statementList中等待執行Connection connection = getConnection(ms.getStatementLog());// 創建Statementstmt = handler.prepare(connection, transaction.getTimeout());handler.parameterize(stmt);    //fix Issues 322currentSql = sql;currentStatement = ms;// 添加到statementListstatementList.add(stmt);// 添加新的BatchResultbatchResultList.add(new BatchResult(ms, sql, parameterObject));}handler.batch(stmt);return BATCH_UPDATE_RETURN_VALUE;
}

JDBC的Statement可以添加不同模式的SQL,每添加一個新模式的SQl就會觸發一次編譯操作。而PrepareStatement中只能添加統一模式的SQL語句,只觸發一次編譯操作,但是可以通過綁定多組不同的參數實現批處理。而BatchExecutor做的就是這件事,將連續添加的、相同模式的SQL語句添加到同一個Statement對象中,從而有效地減少編譯次數。

在添加完待執行的SQL之后,doFlushStatement方法會處理這些SQL語句。

@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {try {// 記錄批處理的結果List<BatchResult> results = new ArrayList<>();// 如果明確指定了要回滾事務,則直接返回空集合,忽略statementList中記錄的sqlif (isRollback) {return Collections.emptyList();}// 遍歷StatementListfor (int i = 0, n = statementList.size(); i < n; i++) {Statement stmt = statementList.get(i);applyTransactionTimeout(stmt);BatchResult batchResult = batchResultList.get(i);try {// 調用批量執行方法,返回int數組,每個元素都表示每條sql影響的記錄條數batchResult.setUpdateCounts(stmt.executeBatch());MappedStatement ms = batchResult.getMappedStatement();List<Object> parameterObjects = batchResult.getParameterObjects();// 獲取配置的KeyGeneratorKeyGenerator keyGenerator = ms.getKeyGenerator();if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;// 獲取數據庫生成的主鍵,并配置到parameterObjectsjdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);} else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141// 其他類型的KeyGenerator,調用其processAfterfor (Object parameter : parameterObjects) {keyGenerator.processAfter(this, ms, stmt, parameter);}}// Close statement to close cursor #1109closeStatement(stmt);} catch (BatchUpdateException e) {StringBuilder message = new StringBuilder();message.append(batchResult.getMappedStatement().getId()).append(" (batch index #").append(i + 1).append(")").append(" failed.");if (i > 0) {message.append(" ").append(i).append(" prior sub executor(s) completed successfully, but will be rolled back.");}throw new BatchExecutorException(message.toString(), e, results, batchResult);}// 將BatchResult添加到resultsresults.add(batchResult);}return results;} finally {for (Statement stmt : statementList) {closeStatement(stmt);}currentSql = null;statementList.clear();batchResultList.clear();}
}

結語

Executor接口的內容有點多,因此就分成了兩篇博客進行介紹,而最后的CachingExecutor是為Mybatis實現二級緩存功能,其中使用了裝飾器模式。Mybatis的二級緩存功能在實際開發中很少會使用,因此這里就不進行介紹,感興趣的朋友可以自己摸索。
*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/536960.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536960.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536960.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python解橢圓方程的例題_橢圓標準方程典型例題及練習題

橢圓標準方程典型例題例1已知P 點在以坐標軸為對稱軸的橢圓上&#xff0c;點P 到兩焦點的距離分別為354和352&#xff0c;過P 點作焦點所在軸的垂線&#xff0c;它恰好過橢圓的一個焦點&#xff0c;求橢圓方程&#xff0e; 解&#xff1a;設兩焦點為1F 、2F &#xff0c;且3541…

leetcode393. UTF-8 Validation

題目要求 A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules:For 1-byte character, the first bit is a 0, followed by its unicode code. For n-bytes character, the first n-bits are all ones, the n1 bit is 0, followed by n-1 by…

Mybatis源碼閱讀(五 ):接口層——SqlSession

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

插入公式_一個小工具,徹底幫你搞定在Markdown中插入公式的問題

在編輯Markdown文檔時&#xff0c;插入公式是一個挺麻煩的活兒。需要掌握LaTex語法。我自己看完語法后&#xff0c;直接放棄&#xff0c;這絕對是反人類的語法。&#xff08;好吧&#xff0c;是我不會用...&#xff09;但是&#xff0c;我相信你看了這篇文章后&#xff0c;絕對…

JavaScript數據結構與算法——字典

1.字典數據結構 在字典中&#xff0c;存儲的是【鍵&#xff0c;值】對&#xff0c;其中鍵名是用來查詢特定元素的。字典和集合很相似&#xff0c;集合以【值&#xff0c;值】的形式存儲&#xff0c;字典則是用【鍵&#xff0c;值】對的形式存儲。字典也稱作映射。 2.創建字典 f…

Mybatis源碼閱讀(一):Mybatis初始化1.2 —— 解析別名、插件、對象工廠、反射工具箱、環境

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

中西方對時間的差異_中西方時間觀念差異 英文

The concept of time(時間觀念)①Inchina&#xff0c;words and phrases about time are very general. Forexample&#xff0c;ifyoudatewithsomeone,mostofChineseusedtoanswer: in the afternoon /at night/after a while and so on.Butinwestern,peoplehaveaverystrongconc…

Google 修改 Chrome API,防止隱身模式檢測

開發四年只會寫業務代碼&#xff0c;分布式高并發都不會還做程序員&#xff1f; 在使用 Chrome 瀏覽網頁時&#xff0c;某些網站會使用某種方法來確定訪問者是否處于隱身模式&#xff0c;這是一種隱私泄漏行為。Google 目前正在考慮修改 Chrome 的相關 API&#xff0c;來杜絕…

Mybatis源碼閱讀(一):Mybatis初始化1.1 解析properties、settings

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

亞馬遜推薦python_使用python查找amazon類別

我想得到amazon的類別&#xff0c;我計劃廢棄不用API。我已經取消了http://www.amazon.com&#xff0c;我已經在Shop By Department下拉列表中抓取了所有的類別和子類別&#xff0c;我創建了一個web服務來完成這項工作&#xff0c;代碼就在這里route(/hello)def hello():textli…

JavaScript異步基礎

唯一比不知道代碼為什么崩潰更可怕的事情是&#xff0c;不知道為什么一開始它是工作的&#xff01;在 ECMA 規范的最近幾次版本里不斷有新成員加入&#xff0c;尤其在處理異步的問題上&#xff0c;更是不斷推陳出新。然而&#xff0c;我們在享受便利的同時&#xff0c;也應該了…

Flutter、ReactNative、uniapp對比

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

JavaScript數組方法

一、基本類型和引用類型 數值、字符串、布爾值、undefined、null可以直接寫出來&#xff0c;比較簡單的數據稱為基本類型&#xff0c;在比較的時候&#xff0c;是直接按值比較。對象、函數、數組復雜的數據是引用類型&#xff0c;在比較的時候&#xff0c;是按照地址比較。cons…

nodejs mysql模塊_NodeJs使用Mysql模塊實現事務處理

依賴模塊&#xff1a;1. mysql&#xff1a;https://github.com/felixge/node-mysqlnpm install mysql --save2. async&#xff1a;https://github.com/caolan/asyncnpm install async --save(ps: async模塊可換成其它Promise模塊如bluebird、q等)因為Node.js的mysql模塊本身對于…

計數排序vs基數排序vs桶排序

從計數排序說起 計數排序是一種非基于元素比較的排序算法&#xff0c;而是將待排序數組元素轉化為計數數組的索引值&#xff0c;從而間接使待排序數組具有順序性。 計數排序的實現一般有兩種形式&#xff1a;基于輔助數組和基于桶排序。 基于輔助數組 整個過程包含三個數組&…

多線程中ThreadLocal的使用

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

mysql 查看所有表的引擎_MySQL查看數據庫、表的占用空間大小以及某個庫中所有表的引擎類型...

本文章來給大家介紹一些常用的MySQL查看數據庫、表的占用空間大小sql命令吧&#xff0c;希望此教程 對各位同學會有所幫助。查看各庫的大小代碼如下復制代碼SELECT SUM(DATA_LENGTH)SUM(INDEX_LENGTH) FROM information_schema.tables WHERE TABLE_SCHEMAdatabase_name;結果是以…

Fusion組件庫是如何支持多語言能力的

隨著國際化發展&#xff0c;多語言的需求越來越常見&#xff0c;單一的語言已經遠不能滿足需求了。作為一個組件庫&#xff0c;支持多語言也是基本能力。 多語言功能的本質其實是文本的替換&#xff0c;一個詞匯“OK”&#xff0c;在英文語境下是“OK”&#xff0c;日語語境下是…

mysql 存儲過程 replace_mysql replace存儲過程

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云數據庫專家保駕護航&#xff0c;為用戶…

注解版poi操作工具

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…