2014年一码中特
首頁 > 其他 > 詳細

MyBatis架構設計及源代碼分析系列(一):MyBatis架構

時間:2014-12-28 19:27:39      閱讀:40567      評論:0      收藏:3      [點我收藏+]

標簽:des   事務   class   style   代碼   src   java   使用   com   

如果不太熟悉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架構

標簽:des   事務   class   style   代碼   src   java   使用   com   

原文:http://www.cnblogs.com/mengheng/p/3739610.html

(5)
(42)
   
舉報
評論 一句話評論(0
登錄后才能評論!
? 2014 bubuko.com 版權所有 魯ICP備09046678號-4
打開技術之扣,分享程序人生!
             

魯公網安備 37021202000002號

2014年一码中特