<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Mybatis源碼解析之六劍客

    一顆小胡椒2022-07-21 11:11:14

    前言

    • Mybatis的專題文章寫到這里已經是第四篇了,前三篇講了Mybatis的基本使用,相信只要認真看了的朋友,在實際開發中正常使用應該不是問題。沒有看過的朋友,作者建議去看一看,三篇文章分別是Mybatis入門之基本操作Mybatis結果映射,你射準了嗎?Mybatis動態SQL,你真的會了嗎?
    • 當然,任何一個技術都不能淺藏輒止,今天作者就帶大家深入底層源碼看一看Mybatis的基礎架構。此篇文章只是源碼的入門篇,講一些Mybatis中重要的組件,作者稱之為六劍客

    環境版本

    • 本篇文章講的一切內容都是基于Mybatis3.5SpringBoot-2.3.3.RELEASE

    Myabtis的六劍客

    • 其實Mybatis的底層源碼和Spring比起來還是非常容易讀懂的,作者將其中六個重要的接口抽離出來稱之為Mybatis的六劍客,分別是SqlSessionExecutorStatementHandlerParameterHandlerResultSetHandlerTypeHandler
    • 六劍客在Mybatis中分別承擔著什么角色?下面將會逐一介紹。
    • 介紹六劍客之前,先來一張六劍客執行的流程圖,如下:

    SqlSession

    • SqlSession是Myabtis中的核心API,主要用來執行命令,獲取映射,管理事務。它包含了所有執行語句、提交或回滾事務以及獲取映射器實例的方法。

    有何方法

    • 其中定義了將近20個方法,其中涉及的到語句執行,事務提交回滾等方法。下面對于這些方法進行分類總結。

    語句執行方法

    • 這些方法被用來執行定義在 SQL 映射 XML 文件中的 SELECT、INSERT、UPDATE 和 DELETE 語句。你可以通過名字快速了解它們的作用,每一方法都接受語句的 ID 以及參數對象,參數可以是原始類型(支持自動裝箱或包裝類)、JavaBean、POJO 或 Map。
    <T> T selectOne(String statement, Object parameter)
    <E> List<E> selectList(String statement, Object parameter)
    <T> Cursor<T> selectCursor(String statement, Object parameter)
    <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
    int insert(String statement, Object parameter)
    int update(String statement, Object parameter)
    int delete(String statement, Object parameter)
    
    • 其中的最容易誤解的就是selectOneselectList,從方法名稱就很容易知道區別,一個是查詢單個,一個是查詢多個。如果你對自己的SQL無法確定返回一個還是多個結果的時候,建議使用selectList
    • insertupdatedelete方法返值是受影響的行數。
    • select還有幾個重用的方法,用于限制返回行數,在Mysql中對應的就是limit,如下:
    <E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
    <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds)
    <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
    void select (String statement, Object parameter, ResultHandler<T> handler)
    void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)
    
    • 其中的RowBounds參數中保存了限制的行數,起始行數。

    立即批量更新方法

    • 當你將 ExecutorType 設置為 ExecutorType.BATCH 時,可以使用這個方法清除(執行)緩存在 JDBC 驅動類中的批量更新語句。
    List<BatchResult> flushStatements()
    

    事務控制方法

    • 有四個方法用來控制事務作用域。當然,如果你已經設置了自動提交或你使用了外部事務管理器,這些方法就沒什么作用了。然而,如果你正在使用由 Connection 實例控制的 JDBC 事務管理器,那么這四個方法就會派上用場:
    void commit()
    void commit(boolean force)
    void rollback()
    void rollback(boolean force)
    
    • 默認情況下 MyBatis 不會自動提交事務,除非它偵測到調用了插入、更新或刪除方法改變了數據庫。如果你沒有使用這些方法提交修改,那么你可以在commitrollback 方法參數中傳入 true 值,來保證事務被正常提交(注意,在自動提交模式或者使用了外部事務管理器的情況下,設置 force 值對 session 無效)。大部分情況下你無需調用 rollback(),因為 MyBatis 會在你沒有調用 commit 時替你完成回滾操作。不過,當你要在一個可能多次提交或回滾的 session 中詳細控制事務,回滾操作就派上用場了。

    本地緩存方法

    • Mybatis 使用到了兩種緩存:本地緩存(local cache)和二級緩存(second level cache)。
    • 默認情況下,本地緩存數據的生命周期等同于整個 session 的周期。由于緩存會被用來解決循環引用問題和加快重復嵌套查詢的速度,所以無法將其完全禁用。但是你可以通過設置 localCacheScope=STATEMENT 來只在語句執行時使用緩存。
    • 可以調用以下方法清除本地緩存。
    void clearCache()
    

    獲取映射器

    • 在SqlSession中你也可以獲取自己的映射器,直接使用下面的方法,如下:
    <T> T getMapper(Class<T> type)
    
    • 比如你需要獲取一個UserMapper,如下:
    UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
    

    有何實現類

    • 在Mybatis中有三個實現類,分別是DefaultSqlSessionSqlSessionManagerSqlSessionTemplate,其中重要的就是DefaultSqlSession,這個后面講到Mybatis執行源碼的時候會一一分析。
    • 在與SpringBoot整合時,Mybatis的啟動器配置類會默認注入一個SqlSessionTemplate,源碼如下:
    @Bean
      @ConditionalOnMissingBean
      public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) 
        //根據執行器的類型創建不同的執行器,默認CachingExecutor
        ExecutorType executorType = this.properties.getExecutorType();
        if (executorType != null) {
          return new SqlSessionTemplate(sqlSessionFactory, executorType);
        } else {
          return new SqlSessionTemplate(sqlSessionFactory);
        }
      }
    

    Executor

    • Mybatis的執行器,是Mybatis的調度核心,負責SQL語句的生成和緩存的維護,SqlSession中的crud方法實際上都是調用執行器中的對應方法執行。
    • 繼承結構如下圖:

    實現類

    • 下面我們來看看都有哪些實現類,分別有什么作用。

    BaseExecutor

    • 這是一個抽象類,采用模板方法的模式,有意思的是這個老弟模仿Spring的方式,真正的執行的方法都是doxxx()
    • 其中有一個方法值得注意,查詢的時候走的一級緩存,因此這里注意下,既然這是個模板類,那么Mybatis執行select的時候默認都會走一級緩存。代碼如下:
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        //此處的localCache即是一級緩存,是一個Map的結構
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
          //執行真正的查詢
          list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
          localCache.removeObject(key);
        }
        localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
          localOutputParameterCache.putObject(key, parameter);
        }
        return list;
      }
    

    CachingExecutor

    • 這個比較有名了,二級緩存的維護類,與SpringBoot整合默認創建的就是這個家伙。下面來看一下如何走的二級緩存,源碼如下:
    @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
          throws SQLException {
        //查看當前Sql是否使用了二級緩存
        Cache cache = ms.getCache();
        //使用緩存了,直接從緩存中取
        if (cache != null) {
          flushCacheIfRequired(ms);
          if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, boundSql);
            @SuppressWarnings("unchecked")
            //從緩存中取數據
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) {
              //沒取到數據,則執行SQL從數據庫查詢
              list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
              //查到了,放入緩存中
              tcm.putObject(cache, key, list); // issue #578 and #116
            }
            //直接返回
            return list;
          }
        }
        //沒使用二級緩存,直接執行SQL從數據庫查詢
        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    
    • 這玩意就是走個二級緩存,其他沒什么。

    SimpleExecutor

    • 這個類像個直男,最簡單的一個執行器,就是根據對應的SQL執行,不會做一些額外的操作。

    BatchExecutor

    • 通過批量操作來優化性能。通常需要注意的是批量更新操作,由于內部有緩存的實現,使用完成后記得調用flushStatements來清除緩存。

    ReuseExecutor

    • 可重用的執行器,重用的對象是Statement,也就是說該執行器會緩存同一個sql的Statement,省去Statement的重新創建,優化性能。
    • 內部的實現是通過一個HashMap來維護Statement對象的。由于當前Map只在該session中有效,所以使用完成后記得調用flushStatements來清除Map。

    SpringBoot中如何創建

    • 在SpringBoot到底創建的是哪個執行器呢?其實只要閱讀一下源碼可以很清楚的知道,答案就在org.apache.ibatis.session.Configuration類中,其中創建執行器的源碼如下:
    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        //沒有指定執行器的類型,創建默認的,即是SimpleExecutor
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        //類型是BATCH,創建BatchExecutor
        if (ExecutorType.BATCH == executorType) {
          executor = new BatchExecutor(this, transaction);
          //類型為REUSE,創建ReuseExecutor
        } else if (ExecutorType.REUSE == executorType) {
          executor = new ReuseExecutor(this, transaction);
        } else {
          //除了上面兩種,創建的都是SimpleExecutor
          executor = new SimpleExecutor(this, transaction);
        }
        //如果全局配置了二級緩存,則創建CachingExecutor,SpringBoot中這個參數默認是true,可以自己設置為false
        if (cacheEnabled) {
        //創建CachingExecutor
          executor = new CachingExecutor(executor);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
    
    • 顯而易見,SpringBoot中默認創建的是CachingExecutor,因為默認的cacheEnabled的值為true

    StatementHandler

    • 熟悉JDBC的朋友應該都能猜到這個接口是干嘛的,很顯然,這個是對SQL語句進行處理和參數賦值的。

    實現類

    • 該接口也是有很多的實現類,如下圖:

    SimpleStatementHandler

    • 這個很簡單了,就是對應我們JDBC中常用的Statement接口,用于簡單SQL的處理

    PreparedStatementHandler

    • 這個對應JDBC中的PreparedStatement,預編譯SQL的接口。

    CallableStatementHandler

    • 這個對應JDBC中CallableStatement,用于執行存儲過程相關的接口。

    RoutingStatementHandler

    • 這個接口是以上三個接口的路由,沒有實際操作,只是負責上面三個StatementHandler的創建及調用。

    ParameterHandler

    • ParameterHandler在Mybatis中負責將sql中的占位符替換為真正的參數,它是一個接口,有且只有一個實現類DefaultParameterHandler
    • setParameters是處理參數最核心的方法。這里不再詳細的講,后面會講到。

    TypeHandler

    • 這位大神應該都聽說過,也都自定義過吧,簡單的說就是在預編譯設置參數和取出結果的時候將Java類型和JDBC的類型進行相應的轉換。當然,Mybatis內置了很多默認的類型處理器,基本夠用,除非有特殊的定制,我們才會去自定義,比如需要將Java對象以JSON字符串的形式存入數據庫,此時就可以自定義一個類型處理器。
    • 很簡單的東西,此處就不再詳細的講了,后面會單獨出一篇如何自定義類型處理器的文章。

    ResultSetHandler

    • 結果處理器,負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合或者Cursor
    • 具體實現類就是DefaultResultSetHandler,其實現的步驟就是將Statement執行后的結果集,按照Mapper文件中配置的ResultType或ResultMap來封裝成對應的對象,最后將封裝的對象返回。
    • 源碼及其復雜,尤其是其中對嵌套查詢的解析,這里只做個了解,后續會專門寫一篇文章介紹。

    總結

    • 至此,Mybatis源碼第一篇就已經講完了,本篇文章對Mybatis中的重要組件做了初步的了解,為后面更深入的源碼閱讀做了鋪墊,如果覺得作者寫的不錯,在看分享一波,謝謝支持。
    源碼mybatis
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    前言Mybatis的專題文章寫到這里已經是第四篇了,前三篇講了Mybatis的基本使用,相信只要認真看了的朋友,在實際開發中正常使用應該不是問題。
    前言作為一個資深后端碼農天天都要和數據庫打交道,最早使用的是 Hiberate,一個封裝性極強的持久性框架。自從接觸到 Mybatis 就被它的靈活性所折服了,可以自己寫 SQL,雖然輕量級,但是麻雀雖小,五臟俱全。這篇文章就來講講什么是 Mybatis,如何簡單的使用 Mybatis
    目前,多數項目會有多數據源的要求,或者是主從部署的要求,所以我們還需要引入mybatis-plus關于多數據源的依賴:。#設置默認的數據源或者數據源組,默認值即為master. true未匹配到指定數據源時拋異常,false使用默認數據源。表名注解,用于標識實體類對應的表。其說明如下,關于這些書寫,常規情況基本很少用到,不做多余解釋了:@Documented
    文章末尾會附上文章的所有代碼、腳本和測試用例。本文環境: SpringBoot 2.5.7 + MySQL 8.0 X + MybatisPlus + Swagger2.9.2模擬工具: Jmeter模擬場景: 減庫存->創建訂單->模擬支付2.商品秒殺-超賣在開發中,對于下面的代碼,可能很熟悉:在Service里面加上@Transactional事務注解和Lock鎖控制層:Controller@ApiOperation. "哎呦喂,人也太多了,請稍后!
    拋開“穩定”,個人感覺還是先入為主占了主要因素。至此,ubuntu已經陪伴了我10年之久。大部分的國人,依賴的都還是百度搜索,然后導流到csdn/博客園等平臺。因為群體的聚集性,被推薦使用CentOS的確是順理成章的事。而在“外文圈”中,debian/ubuntu的使用量和推薦度絕對是高居榜首的。CentOS用戶的聚集地,主要分布在中日韓三國。嫌Ubuntu穩定性不高的,我以自己舉例,沒遇上過自己跑著跑著跑掛的情況,除了斷電掛和自己折騰掛。
    我們可以通過線程名,分析當前是哪個線程執行的,在多線程環境下對代碼運行分析起到輔助作用。
    fastjson.jar是阿里開發的一款專門用于Java開發的包,可以方便的實現json對象與JavaBean對象的轉換,實現JavaBean對象與json字符串的轉換,實現json對象與json字符串的轉換。除了這個fastjson以外,還有Google開發的Gson包,其他形式的如net.sf.json包,都可以實現json的轉換。方法名稱不同而已,最后的實現結果都是一樣的。
    但是,這樣一來,就必須在每一個Controller類都定義一套這樣的異常處理方法,因為異常可以是各種各樣。所以注解@ControllerAdvice出現了,簡單的說,該注解可以把異常處理器應用到所有控制器,而不是單個控制器。這就是統一異常處理的原理。統一異常處理實戰在定義統一異常處理類之前,先來介紹一下如何優雅的判定異常情況并拋異常。
    項目安裝迷你天貓商城是一個基于Spring Boot的綜合性B2C電商平臺,需求設計主要參考天貓商城的購物流程:用戶從注冊開始,到完成登錄,瀏覽商品,加入購物車,進行下單,確認收貨,評價等一系列操作。作為迷你天貓商城的核心組成部分之一,天貓數據管理后臺包含商品管理,訂單管理,類別管理,用戶管理和交易額統計等模塊,實現了對整個商城的一站式管理和維護。
    本系列將以官網資料為基礎主要通過動態跟蹤來解析DynamoRIO的源代碼。因為如果不結合實例只是將各函數的作用寫出來,實在無法很好的說明問題,我們將以代碼覆蓋工具drcov為例,分析DynamoRIO的執行流程。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类