前言:工作中雖然用到了 MyBatis,可完全不知道為什么,再不學習就晚了,這里將記錄我的學習筆記,整個 MyBatis 的體系。
一、簡介
1、傳統的JDBC
JDBC 是一種典型的橋接模式。
使用傳統的 JDBC 方式來訪問數據庫,有一些弊端,工作量相對較大,對處理異常、事務並正確關閉資源十分復雜。
JDBC 大致有這幾步:
連接數據庫(注冊驅動和數據庫信息);
操作 Connection,打開Statement對象;
通過 Statement 執行 SQL,返回結果到ResultSet對象;
使用 ResultSet 對象讀取數據,然后將這些數據轉為 POJO 實體類;
最后需要關閉所有的數據庫相關資源。
2、ORM 模型是什么
對象-關系映射(OBJECT/RELATIONALMAPPING,簡稱ORM),是隨着面向對象的軟件開發方法發展而產生的。用來把對象模型表示的對象映射到基於S Q L 的關系模型數據庫結構中去。這樣,我們在具體的操作實體對象的時候,就不需要再去和復雜的 SQ L 語句打交道,只需簡單的操作實體對象的屬性和方法 。O R M 技術是在對象和關系之間提供了一條橋梁,前台的對象型數據和數據庫中的關系型的數據通過這個橋梁來相互轉化 。
簡單地說,ORM 模型就是數據庫的表和簡單的Java對象(POJO)的映射關系模型。主要解決數據庫數據和 POJO 對象的相互映射。
有了 ORM 模型,在大部分情況下,程序員只需要了解 java 應用,而無需對數據庫相關知識深入了解,就可以寫出通俗易懂的程序了。
ORM 框架的主要功能就是根據映射配置文件,完成數據在對象模型與關系模型之間的映射,同時也屏蔽了一些重復的代碼,只暴露簡單的 API 供開發人員使用 。
3、MyBatis
MyBatis是一個實現了 JPA 規范的用來連接數據庫並對其進行增刪改查操作的開源框架(就和傳統的 JDBC 一樣,就是個連接數據庫的東西),其實,它底層就是一個 JDBC 封裝的組件
我沒有用過 Hibernate,但是了解了一下 Hibernate 的缺點,而 MyBatis 就是為了解決 Hibernate 的不足。
MyBatis 的前身是 Apache 的一個開源項目 iBatis,2010 年這個項目有 apache software foundation 遷移到了 google code,並且改名為 MyBatis。2013 年 11 月遷移到 Github,所以目前 MyBatis 是由 Github 維護的。
4、MyBatis 和 Hibernate 的區別,如何選擇
JDBC:目前極少用到,因為需要提供太多的代碼,操作太多的對象,不僅十分麻煩,還極易出錯,所以不推薦在項目中直接使用 JDBC。
Hibernate:是一個標准的ORM框架。入門門檻較高,不需要寫sql,sql語句自動生成,對sql語句進行優化、修改比較困難。編程建議,需要我們提供映射的規則,完全可以通過 IDE 生成,同時無需編寫 SQL ,所以開發效率確實優於 MyBatis。而且,它也提供了緩存、日志、級聯等強大的功能,但是 Hibernate 的缺點也十分明顯,多表關聯復雜 SQL,數據系統權限控制,根據條件變化的 sql,存儲過程等場景使用它十分不方便,而且性能難以通過 sql 優化。所以注定 Hibernate 只適用於不復雜,不太要求性能的項目。應用場景:適用需求變化不多的中小型項目,比如:后台管理系統,erp,orm,oa等。
MyBatis:專注sql本身,需要程序員自己編寫sql語句,sql修改、優化比較方便。mybatis是一個不完全的ORM框架,雖然程序員自己寫sql,mybatis也可以實現映射(輸入映射,輸出映射)。它幾乎可以代替 JDBC,支持動態列、動態表名,存儲過程,同時提供了簡易的緩存、日志、級聯。Mybatis 具有高靈活度、可優化、易維護等特點。但是它的缺點是需要提供映射規則和 SQL,所以開發工作量要比 Hibernate 大。應用場景:適用需求變化較多的項目,比如:互聯網項目。
在項目中,需要根據實際情況去選擇使用哪一個框架。
5、MyBatis 的整體架構分為三層 , 分別是基礎支持層 、 核心處理層和接口層
二、基礎
1、MyBaits 的核心組件
- SqlSessionFactoryBuilder (構造器):它會根據配置信息或者代碼來生成 SqlSessionFactory (工廠接口)
- SqlSessionFactory:依靠工廠來生成 SqlSession (會話)
- SqlSession:是一個既可以發送 SQL 去執行並返回接口,也可以獲取 Mapper 的接口。
- SQL Mapper:它是 MyBatis 新設計的組件,它是有一個 Java 接口和 XML 文件(或注解)構成的,需要給出對應的 SQL 和映射規則。它負責發送 SQL 去執行,並返回結果。
2、Configuration
這里我們的 Configuration 的類全限定名為 org.apache.ibatis.session.Configuration,它在 MyBatis 中將以一個 Configuration 類對象的形式存在,而這個對象將存在於整個 MyBatis 應用的生命周期中,以便重復讀取和運用。在內存中的數據是計算機系統中讀取速度最快的,我們可以解析一次配置的 XML 文件保存到 Configuration 對象中,方便我們從這個對象中讀取配置信息,性能高。單例占用空間小,基本不占用存儲空間,而且可以反復使用。Configuration 類對象保存着我們配置在 MyBatis 的信息。在 MyBatis 中提供了兩個 SqlSessionFactory 的實現類, DefualtSqlSessionFactory 和 SqlSessionManager。不過 SqlSessionManager 目前還沒有使用,MyBatis 中目前使用的是 DefualtSqlSessionFactory。
3、映射器
映射器是由 Java 接口和 XML 文件(或注解)共同組成的,它的作用如下:定義參數類型、描述緩存、描述 SQL 語句、定義查詢結果和 POJO 的映射關系。
一個映射器的實現方式有兩種:一種是通過 XML 的方式實現,一種是通過代碼+注解實現。第二種方式實現復雜、代碼可讀性差等,所以建議使用第一種方式。第一種方式其實就是由一個 Java 接口和一個 XML 文件構成(即 MyBatis 代碼生成器生成的一樣)。
4、映射器,一個沒有實現類的接口怎么能運行呢?
這里需要用到 Java 的動態代理:我們會在 MyBatis 上下文中描述這個接口,而 MyBatis 會為這個接口生成代理類對象,代理對象會根據“接口全路徑+方法名”去匹配,找到對應的 XML 文件(或注解)去完成它所需要的任務,返回我們需要的結果。
5、生命周期
想要保證多線程中 MyBatis 的正確性、高性能、沒有並發問題,就必須掌握 MyBatis 中 SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper 的生命周期。
SqlSessionFactoryBuilder:利用 XML 或者 Java 編碼獲得資源來構建 SqlSessionFactory 的,通過它可以構建多個 SessionFactory。它的作用就是一個構建起,一旦我們構建了 SqlSessionFactory,它的作用就已經完結了,失去了存在的意義,這是我們就應該毫不猶豫的廢棄它,將它回收。所以它的生命周期只存在於方法的局部,它的作用就是生成 SqlSessionFactory 對象。
SqlSessionFactory:作用是創建 SqlSession,而 SqlSession 就是一個會話,相當於 JDBC 中的 Connection 對象。每次應用程序需要訪問數據庫,我們就要通過 SqlSessionFactory 創建 SqlSession,所以 SqlSessionFactory 應該在 MyBatis 應用的整個生命周期中。而如果我們多次創建同一個數據庫的 SqlSessionFactory,則每次創建 SqlSessionFactory 會打開更多的數據庫連接(Connection)資源,那么連接資源就很快會被耗盡。因此 SqlSessionFactory 的責任是唯一的,它的責任就是創建 SqlSession,所以我們果斷采用單例模式。如果我們采用多例,那么它對數據庫連接的消耗是很大的,不利於我們統一的管理,這樣便嗅到了代碼的壞味道。
SqlSession:是一個會話,相當於 JDBC 的一個 Connection 對象,它的生命周期應該是在請求數據處理事務的過程中。它是一個線程不安全的對象,在涉及多線程的時候,我們需要特別的當心,操作數據庫需要注意其隔離級別,數據庫鎖等高級特性。此外,每次創建的 SqlSession 都必須及時關閉它,它長期存在就會使數據庫連接池的活動資源減少,對系統性能的影響很大。
Mapper:是一個接口,而沒有任何實現類,它的作用是發送 SQL,然后返回我們需要的結果,或者執行 SQL 從而修改數據庫的數據,因此它應該在一個 SqlSession 事務方法之內,是一個方法級別的東西。它就如果 JDBC 中的一個 SQL 語句的執行,它最大的范圍和 SqlSession 是相同的。
三、配置
1、全部 MyBatis 的配置元素
MyBatis 配置 XML 文件的層次結構。這些層次是不能夠顛倒順序的,如果顛倒順序,MyBatis 在解析 XML 文件的時候就會出現異常了。
<?xml version="1.0" encoding="UTF-8"?> <configuration><!--配置--> <properties/><!--屬性--> <settions/><!--設置--> <typeAliases/><!--類型命名,不區分大小寫--> <typeHandlers/><!--類型處理器--> <objectFactory/><!--對象工廠--> <plugins/><!--插件--> <environments><!--配置環境--> <environment> <transactionManager/><!--事務管理器--> <dataSource/><!--數據源--> </environment> </environments> <databaseIdProvider/><!--數據庫廠商標識--> <mappers/><!--映射器--> </configuration>
四、映射器
MyBatis 是針對映射器構造的 SQL 構建的輕量級框架。
1、select 元素
有3中傳遞多個參數的方式:map;@Param;實體。
這三種方式對比:
- 使用 Map 傳遞參數。因為 Map 導致代碼的可讀性下降,會導致后面的擴展和維護比較困難,所以建議少使用這種方式。
- 使用 @Param 注解方式。這種方式可讀性高,但是當參數個數很多的時候,同樣會導致可讀性和維護性下降,所以,當參數個數小於等於 5 時,建議采取這種方式。
- 使用實體 JavaBean 的方式。當參數個數大於 5 時,建議采取這種方式。
2、insert 元素(寫demo)
實現主鍵回填,即執行完新增后返回主鍵 id。
3、級聯(寫demo)
級聯的延遲加載實現原理是通過動態代理來實現的。有一個動態代理對象,里面保存着相關的 SQL 和參數,一旦我們使用這個代理對象的方法,它會進入到動態代理對象的代理方法里,方法里面會通過發送 SQL 和參數,就可以把對應的結果從數據庫里查回來,這便是其實現原理。
4、緩存(寫demo)
目前流行的緩存服務器有 MongoDB、Redis、Ehcache 等。無需從磁盤上讀入,具有快速讀取和使用的特點。如果緩存的命中率高,那么可以極大的提高系統的性能,所以使用緩存的關鍵在於存儲內容訪問的命中率。
五、動態 SQL
六、MyBatis 的解析和運行原理
MyBatis 的運行分為兩大部分,第一部分為讀取配置文件到 Configuration 對象,用以創建 SqlSessionFactory;第二部分是 SqlSession 的執行過程。
問題一、Mapper 僅僅是一個接口,而不是一個包含邏輯的實現類,所以 Dao 層是怎么執行的呢?
答:動態代理。
未完待續。。。
參考書籍《深入淺出MyBatis技術原理與實戰》
鏈接:https://pan.baidu.com/s/1uPTZZVANU5przcOzT6v_gA 密碼:qatn