1. 程式人生 > >MyBatis架構設計及原始碼分析系列(一):MyBatis架構

MyBatis架構設計及原始碼分析系列(一):MyBatis架構

如果不太熟悉MyBatis使用的請先參見MyBatis官方文件,這對理解其架構設計和原始碼分析有很大好處。

一、概述

MyBatis並不是一個完整的ORM框架,其官方首頁是這麼介紹自己

The MyBatis data mapper framework makes it easier to use a relational database with object-oriented applications. MyBatis couples objects with stored procedures or SQL statements using a XML descriptor or annotations. Simplicity
is the biggest advantage of the MyBatis data mapper over object relational mapping tools.

而在其官方文件中介紹“What is MyBaits”中說到

MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results.
MyBatis can use simple XML or Annotations for configuration and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records.
ORM是Object和Relation之間的對映,包括Object->Relation和Relation->Object兩方面。Hibernate是個完整的ORM框架,而MyBatis完成的是Relation->Object,也就是其所說的data mapper framework。關於ORM的一些設計思路和細節可以參見Martin Flow《企業應用架構模式》一書中的ORM章節,MyBatis並不刻意於完成ORM(物件對映)的完整概念,而是旨在更簡單、更方便地完成資料庫操作功能,減輕開發人員的工作量,我想這對於應用系統來說也是最實用的,相信用Hibernate的都受過它的痛苦,而用過MyBatis的都會感覺它很簡捷輕鬆。

二、整體架構

下面是從功能流程層次描述MyBatis的整體架構圖

技術分享

而下面是MyBatis原始碼包對應的架構圖

技術分享

下面以“功能流程角度的架構圖”來簡要地分析下各層的架構,在後面系列文章中將有專題來深入解析MyBatis重要的功能點。

1、介面層

我們知道,在不考慮與Spring整合的情況下,使用MyBatis執行資料庫操作的程式碼如下
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
  Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
  session.close();
}

SqlSessionFactory、SqlSession這是MyBatis介面層的核心類,尤其是SqlSession,是實現所有資料庫操作的API,這幾個類都是org.apache.ibatis.session包下的,這個包的主體類結構圖如下

技術分享

Configuration是MyBatis中相當重要的一個類,可以這麼說,如果理解了其中的所有引數的意義,不僅清楚地知道MyBatis提供的所有配置項,還理解了MyBatis的內部核心執行原理,當然要真正理解這些引數的意義及實現,還需要閱讀完完整的MyBatis框架之後才能做到。

由上圖可以看到,Configuration物件與DefaultSqlSessionFactory是1:1的關聯關係,這也就意味著在一個DefaultSqlSessionFactory衍生出來的所有SqlSession作用域裡,Configuration物件是全域性唯一的。同時SqlSessionFactory提供了getConfiguration()介面來公開Configuration物件,因此開發者除了配置檔案之外,還可以在程式裡動態更改Configuration的屬性項以達到動態調整的目的,但此時不僅要考慮到執行完reset,同時還要考慮在修改過程中會可能影響到其他SqlSession的執行。

2、核心層

2.1 配置解析

在應用啟動的時候,MyBatis解析兩種配置檔案

  • SqlMapConfig.xml
  • SqlMap.xml

SqlMapConfig.xml是在XMLConfigBuilder類中完成解析的,其類圖關係大致如下

技術分享

我們知道XML有兩種解析方式:一是DOM,另一個是SAX,MyBatis使用的是org.wrc.dom——JDK提供的文件物件模型(DOM)介面(SqlMapConfig.xml並不大,所以DOM方式並沒有什麼效率損耗,JDK也提供了SAX模型介面org.xml.sax,這兩個都是JAXP的元件API),以及JDK官方提供的javax.xml.xpath.XPath來作為XML路徑尋找元件。

SqlMap.xml是在XMLMapperBuilder中解析完成的,其中把對Statement的解析(即SqlMap.xml中SELECT|INSERT|UPDATE|DELETE定義部分)委託給XMLStatementBuilder來完成。SqlMap.xml的解析比較複雜的,涉及到PreparedMapping、ResultMapping、LanguageDriver、Discriminator、快取、自動對映等一系列物件的構造,這裡暫時略過,後面專題分析。

2.2 SQL執行

MyBatis中Executor是的核心,圍繞著它完成了資料庫操作的完整過程。下面是Executor的類圖

技術分享

在上圖中我列出了Executor中方法的引數,而在其子類中就沒有明確寫出。從上圖中可以看到,Executor主要提供了

  • QUERY|UPDATE(INSERT和DELETE也是使用UPDATE),從方法定義中可看到,它需要MappedStatement、parameter、resultHandler這幾個例項物件,這幾個也是SQL執行的主要部分,詳細實現在後面專題中再介紹。
  • 事務提交/回滾,這委託給Transaction物件來完成。
  • 快取,createCacheKey()/isCached()。
  • 延遲載入,deferload()。
  • 關閉,close(),主要是事務回滾/關閉。

BaseExecutor的屬性表明:它內部維護了localCache來localOutputParameterCache來處理快取,至於這快取儲存的是什麼,這後面專題再說。以及執行緒安全的延遲載入列表deferredLoads、事務物件Transaction。

BatchExecutor的屬性已經表明:它內部維護了StatementList批量提交併通過batchResultList儲存執行結果。

ResueExecutor的屬性及方法表明:它內部維護了java.sql.Statement物件快取,以重用Statement物件(對於支援預編譯的資料庫而言,在建立PreparedStatement時需要傳送一次資料庫請求預編譯,而重用Statement物件主要是減少了這次預編譯的網路開銷)。

下面以SqlSession.selectList為例,畫出SQL執行的時序圖(點選下方的圖片檢視大圖,部分分支有所簡化)

技術分享

3、基礎層

3.1、logging:

MyBatis使用了自己定義的一套logging介面,根據開發者常使用的日誌框架——Log4j、Log4j2、Apache Commons Log、java.util.logging、slf4j、stdout(控制檯)——分別提供了介面卡。由於各日誌框架的Log級別分類法有所不同(比如java.util.logging.Level提供的是All、FINEST、FINER、FINE、CONFIG、INFO、WARNING、SEVERE、OFF這九個級別,與通常的日誌框架分類法不太一樣),MyBatis統一提供trace、debug、warn、error四個級別,這基本與主流框架分類法是一致的(相比而言缺少Info,也許MyBatis認為自己的日誌要麼是debug需要的,要麼就至少是warn,沒有Info的必要)。

在org.apache.ibatis.logging裡還有個比較特殊的包jdbc,這不是按字面意義理解把日誌通過jdbc記錄到資料庫裡,而是將jdbc操作以開發者配置的日誌框架打印出來,這也就是我們在開發階段常用的跟蹤SQL語句、傳入引數、影響行數這些重要的除錯資訊。

3.2、IO

MyBatis裡的IO主要是包含兩大功能:提供讀取資原始檔的API、封裝MyBatis自身所需要的ClassLoader和載入順序。

3.3、reflection

在MyBatis如引數處理、結果對映這些大量地使用了反射,需要頻繁地讀取Class元資料、反射呼叫get/set,因此MyBatis提供了org.apache.ibatis.reflection對常見的反射操作進一步封裝,以提供更簡潔方便的API。比如我們reflect時總是要處理異常(IllegalAccessException、NoSuchMethodException),MyBatis統一處理為自定義的RuntimeException,減少程式碼量。

3.4、exceptions

在以Spring為代表的開源框架中,對於應用程式中無法進一步處理的異常大都轉成RuntimeException來方便呼叫者操作,另外如頻繁遇到的SQLException,JDK約定其是個Exception,從JDK的角度考慮,強制要求開發者捕獲SQLException是為了能在catch/finally中關閉資料庫連線,而Spring之類的框架為開發者做了資源管理的事情,自然就不需要開發者再煩心SQLException,因此封裝轉換成RuntimeException。MyBatis的異常體系不復雜,org.apache.ibatis.exceptions下就幾個類,主要被使用的是PersistenceException。

3.5、快取

快取是MyBatis裡比較重要的部分,有兩種快取:

  • SESSION或STATEMENT作用域級別的快取,預設是SESSION,BaseExecutor中根據MappedStatement的Id、SQL、引數值以及rowBound(邊界)來構造CacheKey,並使用BaseExccutor中的localCache來維護此快取。
  • 全域性的二級快取,通過CacheExecutor來實現,其委託TransactionalCacheManager來儲存/獲取快取,這個全域性二級快取比較複雜,後面還需要專題分析,至於其快取的效率以及應用場景也留到那時候再分析。

3.6、資料來源/連線池

MyBatis自身提供了一個簡易的資料來源/連線池,在org.apache.ibatis.datasource下,後面專題分析。主要實現類是PooledDataSource,包含了最大活動連線數、最大空閒連線數、最長取出時間(避免某個執行緒過度佔用)、連線不夠時的等待時間,雖然簡單,卻也體現了連線池的一般原理。阿里有個“druid”專案,據他們說比proxool、c3p0的效率還要高,可以學習一下。

3.7 事務

MyBatis對事務的處理相對簡單,TransactionIsolationLevel中定義了幾種隔離級別,並不支援內嵌事務這樣較複雜的場景,同時由於其是持久層的緣故,所以真正在應用開發中會委託Spring來處理事務實現真正的與開發者隔離。分析事務的實現是個入口,藉此可以瞭解不掃JDBC規範方面的事情。

後續將對MyBatis各個部分做詳細的設計及原始碼分析,由於讀取和解析SqlMapConfig.xml和SqlMap.xml的邏輯與各個模組的相關性較強,因此將把這部分內容與在各模組組合在一起分析。

相關推薦

MyBatis架構設計原始碼分析系列():MyBatis架構

如果不太熟悉MyBatis使用的請先參見MyBatis官方文件,這對理解其架構設計和原始碼分析有很大好處。 一、概述 MyBatis並不是一個完整的ORM框架,其官方首頁是這麼介紹自己 The MyBatis data mapper framework makes i

MyBatis架構設計原始碼分析系列 :MyBatis架構

ORM是Object和Relation之間的對映,包括Object->Relation和Relation->Object兩方面。Hibernate是個完整的ORM框架,而MyBatis完成的是Relation->Object,也就是其所說的data mapper framework。關於O

Dubbo 原始碼分析系列之三 —— 架構原理

1 核心功能 首先要了解Dubbo提供的三大核心功能: Remoting:遠端通訊 提供對多種NIO框架抽象封裝,包括“同步轉非同步”和“請求-響應”模式的資訊交換方式。 Cluster: 服務框架 提供基於介面方法的透明遠端過程呼叫,包括多協議支援,以及

Hibernate使用原始碼分析

Hibernate使用及原始碼分析(一) 本篇文章主要通過hibernate初級使用分析一下原始碼,只是給初學者一點小小的建議,不喜勿噴,謝謝! hibernate環境搭建 簡單使用 原始碼走讀 一 hibernate環境搭建 這裡直接

MyBatis全版教程+原始碼分析

一.什麼是 MyBatis MyBatis 是一款優秀的持久層框架,它支援定製化 SQL、儲存過程以及高階對映。MyBatis 避免了幾乎所有的 JDBC 程式碼和手動設定引數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和對映原生資訊,將介面和 J

Vue專案原始碼分析系列

Vue相關的基礎知識我們已經介紹了不少,官網也有很詳細的使用說明,那麼從本篇文章開始,我會陸續為大家介紹一款開源的Vue專案。首先感謝唐崗在github上分享的“基於vue.js重寫Cnodejs.org社群的webapp”,原始碼地址如下:https://github.com/shinygang/Vue

SparkStreaming的WordCount示例原始碼分析

一.程式碼示例 object WordCount { def main(args: Array[String]) { val conf = new SparkConf().setAppName("WordCount").setMaster("loc

WPF原始碼分析系列:剖析WPF模板機制的內部實現(

眾所周知,在WPF框架中,Visual類是可以提供渲染(render)支援的最頂層的類,所有視覺化元素(包括UIElement、FrameworkElment、Control等)都直接或間接繼承自Visual類。一個WPF應用的使用者介面上的所有視覺化元素一起組成了一個視覺化樹(visual tree),任何

WPF原始碼分析系列:剖析WPF模板機制的內部實現(五)

(注:本文是《剖析WPF模板機制的內部實現》系列文章的最後一篇文章,檢視上一篇文章請點這裡) 上一篇文章我們討論了DataTemplate型別的兩個重要變數,ContentControl.ContentTemplate和ContentPresenter.ContentTemplate,這一篇將討論這個型別的另

Spring5原始碼分析系列(十)SpringAOP設計原理應用場景

本章開始講解SpringAOP設計原理及應用場景,文章參考自Tom老師視訊~~ SpringAOP應用示例 AOP是OOP的延續,是AspectOrientedProgramming的縮寫,意思是面向切面程式設計。可以通過預編譯方式和執行期動態代理實現在不修改原始碼的情況

mybatis原理,配置介紹原始碼分析

前言 mybatis核心元件有哪些?它是工作原理是什麼? mybatis配置檔案各個引數是什麼含義? mybatis只添加了介面類,沒有實現類,為什麼可以直接查詢呢? mybatis的mapper對映檔案各個引數又是什麼含義? mybatis-spring提供哪些機制簡化了原生mybatis? m

[Mybatis原始碼分析系列]]03 TypeAliasRegistry

TypeAliasRegistry 負責註冊,儲存,獲取MyBatis別名的類 typeAliases(別名介紹) 類型別名是為 Java 型別設定一個短的名字。它只和 XML 配置有關,存在的意義僅在於用來減少類完全限定名的冗餘。例如: <typeAliases>

[Mybatis原始碼分析系列] 01 解析mybatis-config.xml配製檔案並返回SqlSessionFactory的類SqlSessionFactoryBuilder

前言 公司一直在使用Jpa + Hibernate那一套東西,但是這套技術封裝的太過後重。不利於開發人員掌握,而在優化sql方面也是蛋疼的很。所以在後臺不是特別重要的專案中引入了MyBatis。當然光會使用,是滿足不了本吊絲的胃口,所以走上了分析MyBatis原始碼的道路,並有了這一系列

[MyBatis原始碼分析系列] ResolverUtil

ResolverUtil ResolverUtil用於查詢在類路徑可用並滿足任意條件的類。最常見的兩種情況是一個類繼承或實現了另一個類,或者此類被指定的註解標記了。然而,通過使用Test類,可以滿足任意條件的搜尋。 類載入器用於定位類路徑下指定包下面的必要類,然後載入並檢驗他們。預設

jQuery原始碼分析系列 : 整體架構

query這麼多年了分析都寫爛了,老早以前就拜讀過, 不過這幾年都是做移動端,一直御用zepto, 最近抽出點時間把jquery又給掃一遍 我也不會照本宣科的翻譯原始碼,結合自己的實際經驗一起拜讀吧! github上最新是jquery-master,加入了AMD規範了,我就以官方最新2.0.3為準 整體架構

[MyBatis原始碼分析系列] SqlSession, DefaultSqlSession

SqlSession 簡介 使用MyBatis工作時,主要的Java介面。 通過此介面你可以執行命令,獲取對映及管理事務。 原始碼 public interface SqlSession extends Closeable { <T> T selectOne(S

[MyBatis原始碼分析系列] Discriminator

Discriminator 簡介 鑑別器 <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator

[MyBatis原始碼分析系列] DatabaseIdProvider

DatabaseIdProvider 簡介 應該返回一個可以辨別資料庫型別的id。 此id可以用於稍後對於不資料庫型別構建不同的查詢。 此機制可以支援多個vendors或版本。 程式碼 public interface DatabaseIdProvider { void

[MyBatis原始碼分析系列] BoundSql

BoundSql 簡介 從SqlSource得到的處理了動態內容的真正的SQL。這個SQL可以有?佔位符和一系列引數。 也可以是由動態語言(如loops,bind)建立的額外的引數。 原始碼 public class BoundSql { private final Str

[MyBatis原始碼分析系列] Environment

Environment 簡介 MyBatis 可以配置成適應多種環境,這種機制有助於將 SQL 對映應用於多種資料庫之中, 現實情況下有多種理由需要這麼做。例如,開發、測試和生產環境需要有不同的配置;或者共享相同 Schema 的多個生產資料庫, 想使用相同的 SQL 對映。許多類似