MyBatis 是 Java 生態中非常著名的一款 ORM 框架,目前在一線網際網路大廠中應用廣泛,Mybatis已經成為了一個必會框架。
如果你想要進入一線大廠,能夠熟練使用 MyBatis 開發已經是一項非常基本的技能,同時大廠也更希望自己的開發人員深入瞭解 MyBatis 框架的原理和核心實現。
從這個角度看,理解 MyBatis 原理,閱讀 MyBatis核心原始碼,這樣更有利於提高職場競爭力。
在深入瞭解Mybatis的原始碼之前,我們先了解一下Mybatis的整體架構和工作原理,這樣有助於我們在閱讀原始碼過程中瞭解思路和流程@mikechen。
我們把Mybatis的功能架構分為三層:
介面層:主要就是和資料庫互動,提供給外部使用的介面API,開發人員通過這些本地API來操縱資料庫,介面層一接收到呼叫請求就會呼叫資料處理層來完成具體的資料處理。
以使用Mapper介面為例,將組態檔中的每一個 節點抽象為一個 Mapper 介面,這個介面中宣告的方法和跟Mapper.xml中的 節點項對應。
id值對應方法名稱,parameterType 值對應方法的入參型別,而resultMap 值則對應返回值型別。
設定好後,MyBatis 會根據介面宣告的方法資訊,通過動態代理機制生成一個Mapper 範例,當呼叫介面方法時,根據這個方法的方法名和引數型別,確定Statement Id,底層還是通過 SqlSession.select/update( 「statementId」, parameter) 等來實現對資料庫的操作。
資料處理層:可以說是MyBatis 的核心,負責具體的SQL查詢、SQL解析、SQL執行和執行結果對映處理等,它主要的目的是根據呼叫的請求完成一次資料庫操作。
從大的方面上講,它要完成兩個功能:
動態語句生成能夠說是MyBatis框架很是優雅的一個設計,MyBatis 經過傳入的引數值,使用 Ognl 來動態地構造SQL語句,使得MyBatis 有很強的靈活性和擴充套件性。
引數對映指的是對於java 資料型別和jdbc資料型別之間的轉換,這裡有包括兩個過程:
查詢階段要將java型別的資料,轉換成jdbc型別的資料,經過 preparedStatement.setXXX() 來設值;
另外一個就是對resultset查詢結果集的jdbcType 資料轉換成java 資料型別。
動態SQL語句生成以後,MyBatis 將執行SQL語句,並將可能返回的結果集轉換成List<E> 列表。
MyBatis 在對結果集的處理中,支援結果集關係一對多和多對一的轉換,而且有兩種支援方式,一種為巢狀查詢語句的查詢,還有一種是巢狀結果集的查詢。
基礎支撐層是整個MyBatis框架的地基,負責最基礎的功能支撐,包括連線管理、事務管理、設定載入和快取處理,這些都是共用的東西,將他們抽取出來作為最基礎的元件,為上層的資料處理層提供最基礎的支撐。
資料庫是實踐生成中非常核心的儲存,很多業務資料都會落地到資料庫,所以資料庫效能的優劣直接影響了上層業務系統的優劣。
我們很多線上業務都是讀多寫少的場景,在資料庫遇到瓶頸時,快取是最有效、最常用的手段之一,正確使用快取可以將一部分資料庫請求攔截在快取這一層,這就能夠減少一部分資料庫的壓力,提高系統效能。
除了使用 Redis、Memcached 等外接的第三方快取以外,持久化框架一般也會自帶內建的快取,例如,MyBatis 就提供了一級快取和二級快取,具體實現位於基礎支撐層的快取模組中。
該模組對 Java 原生的反射進行了良好的封裝,提供了更加簡潔易用的 API ,方便上層使呼叫,並且對反射操作進行了一系列優化,例如快取了類的後設資料,提高了反射操作的效能。
型別轉換模組提供了兩個主要功能,一個功能是別名機制,MyBatis 為了簡化組態檔提供了別名機制。
另一個功能是實現 JDBC 型別與 Java 型別之間的轉換,該功能在為 SQL 語句繫結實參以及對映查詢結果集時都會涉及。
提供詳細的紀錄檔輸出資訊,並且能夠整合多種紀錄檔框架,其紀錄檔模組的一個主要功能就是整合第三方紀錄檔框架。
資源載入模組主要是對類載入器進行封裝,確定類載入器的使用順序,並提供了載入類檔案以及其他資原始檔的功能。
解析器模組主要提供兩個功能,一個功能是對 XPath 進行封裝,為 MyBatis 初始化時解析 mybatis-config.xml 組態檔以及對映組態檔提供支援。
另一個功能是為處理動態 SQL 語句中的預留位置提供支援。
持久層框架一般都會提供一套事務管理機制實現資料庫的事務控制,MyBatis 對資料庫中的事務進行了一層簡單的抽象,提供了簡單易用的事務介面和實現。
一般情況下,Java 專案都會整合 Spring,並由 Spring 框架管理事務。
在呼叫 SqlSession 相應方法執行資料庫操作時,需要指定對映檔案中定義的 SQL 節點,如果出現拼寫錯誤,我們只能在執行時才能發現相應的異常。為了儘早發現這種錯誤,MyBatis 通過 Binding 模組將使用者自定義的 Mapper 介面與對映組態檔關聯起來,系統可以通過呼叫自定義 Mapper 介面中的方法執行相應的SQL 語句完成資料庫操作,從而避免上述問題。
需要注意的是,開發人員無須編寫自定義 Mapper 介面的實現,MyBatis 會自動為其建立動態代理物件。
對於 ORM 框架而言,資料來源的組織是一個非常重要的一部分,這直接影響到框架的效能問題。
資料庫連線是一項有限的昂貴資源,一個資料庫連線物件均對應一個物理資料庫連線,每次操作都開啟一個物理連線,使用完都關閉連線,這樣造成系統的效能低下。
資料庫連線池的解決方案是在應用程式啟動時建立足夠的資料庫連線,並將這些連線組成一個連線池,由應用程式動態地對池中的連線進行申請、使用和釋放。
開啟Mybatis原始碼找到datasource包下就可以看到連線池的實現,如下圖所示:
mybatis的總體執行流程,總結如下:
config.xml:設定了全域性組態檔,設定了MyBatis的執行環境等資訊。
mapper,xml:sql的對映檔案,設定了運算元據庫的sql語句,此檔案需在config.xml中載入。
通過MyBatis環境等設定資訊構造SqlSessionFactory(對談工廠)。
通過對談工廠建立SqlSession(對談),對資料庫進行增刪改查操作。
MyBatis底層自定義了Exector執行器介面來具體運算元據庫,Exector介面有兩個實現,一個基本執行器(預設),一個是快取執行器,SqlSession底層是通過Exector介面運算元據庫。
MyBatis的一個底層封裝物件,它包裝了MyBatis設定資訊與sql對映資訊等。mapper.xml中的insert/select/update/delete標籤對應一個MappedStatement物件。標籤的id就是MappedStatement的id。
MappedStatement對sql執行輸入引數進行定義,包括HashMap、基本型別、pojo、Executor通過MappedStatement在執行sql前將輸入的Java物件對映至sql中,輸入引數對映就是JDBC程式設計對preparedStatement設定引數。
MappedStatement對sql執行輸出結果進行定義,包括HashMap、基本型別、pojo,Executor通過MappedStatement在執行sql後將輸出結果對映至Java物件中,輸出結果對映就是JDBC程式設計對結果的解析處理過程。
到此我就把Mybatis的架構從全域性上做了一個拆解,後續我將重點分析其核心原始碼,這樣先全域性再區域性,這樣更有利於掌握其核心原理實現,希望這個框架系列能對你有所用。
以上
陳睿|mikechen,10年+大廠架構經驗,《BAT架構技術500期》系列文章作者,分享十餘年BAT架構經驗以及面試心得!
閱讀mikechen的網際網路架構更多技術文章合集
Java並行|JVM|MySQL|Spring|Redis|分散式|高並行|架構師