終于談到了Mybatis最核心的東西了,最核心的就是通過配置XML文件或注解中的SQL,直接調用接口就能執行配置好的SQL語句并封裝成對應的返回類型的數據。
先看一下Mybatis使用示例:
//創建Builder對象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//讀取配置文件IO流
InputStream is = Resources.getResourceAsStream("mybatis/mybatis-cfg.xml");
//解析IO流到內存Configuration對象中
SqlSessionFactory build = builder.build(is);
//獲取一個持久化會話對象
SqlSession openSession = build.openSession();
//通過SqlSession調用
List list = openSession.selectOne("com.test.mybatis.MybatisTest.official.dao.IUserDao.getUserInfo");
System.out.println("============"+list);
//通過接口對象調用
IUserDao dao = openSession.getMapper(IUserDao.class);
System.out.println(dao.getUserLimit());//關閉SqlSession
openSession.close();
雖然看起來使用非常簡單,但是內部構建這么一個ORM框架還是很艱難的,所以本章節會特別長
設計模式
照舊先來看一看設計模式
建造者模式(Builder Pattern)
使用多個簡單的對象一步一步構建成一個復雜的對象。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。
一個 Builder 類會一步一步構造最終的對象。該 Builder 類是獨立于其他對象的。
UML:
Builder:給出一個抽象接口,以規范產品對 象的各個組成成分的建造。這個接口規定要實 現復雜對象的哪些部分的創建,并不涉及具體 的對象部件的創建;
ConcreteBuilder:實現Builder接口,針對 不同的商業邏輯,具體化復雜對象的各部分的 創建。 在建造過程完成后,提供產品的實例;
Director:調用具體建造者來創建復雜對象的 各個部分,在指導者中不涉及具體產品的信息, 只負責保證對象各部分完整創建或按某種順序 創建;
Product:要創建的復雜對象。
建造者模式對齊工廠模式創建出來的對象要更加的復雜,適用于實例化對象頻繁改變的場景,適合流式編程。
但是現在流式編程是趨勢,所以使用建造者模式創建對象的框架會越來越多,下面會講到流式編程。
策略模式(Strategy Pattern)
一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬于行為型模式。
在策略模式中,我們創建表示各種策略的對象和一個行為隨著策略對象改變而改變的 context 對象。策略對象改變 context 對象的執行算法。
應用最多的就是if else代碼當中
UML:
?
Context:算法調用者,使用setStrategy方法靈活的選擇策略(strategy);
Strategy:算法的統一接口;
ConcreteStrategy:算法的具體實現;
源碼分析
構建并解析XML文件
在使用的過程中都了解到了,首先就是將Mybatis的配置XML文件進行解析,解析后的數據都是存于Configuration對象當中,
先看一下Configuration這個類,單例,生命周期是應用級別,屬性太多這里就不全截圖出來了,大家可以自己下載源碼去查看,截圖如下:
但是在這個類中有幾個對象需先講一下:
MapperRegistry
mapper接口動態代理工廠類的注冊中心。在MyBatis中,通過mapperProxy實現 InvocationHandler接口,MapperProxyFactory用于生成動態代理的實例對象;
TypeAliasRegistry
所以別名存儲的地方,但是大家可以去看一下Configuration的構造方法,各類的默認參數都已經設置進去,不僅僅是Configuration的構造,在它自己的構造中將jdk自帶的一些類也加進去了,這就是為什么有些別名默認就有的原因:
這個類里面也是有個Map<String,Class<?>>的這樣的map用來儲存這些數據
mappedStatements
這個是所有SQL信息儲存的地方,使用的事繼承自HashMap的自定義類StrictMap,這個map的key值為mapper的XML的namespace加上select|update|delete|insert標簽的ID。具體的下面會談到。
SqlSource
mapper.xml文件中的sql語句會被解析成SqlSource對象,經過解析SqlSource包含的語 句最終僅僅包含?占位符,可以直接提交給數據庫執行;
TypeHandlerRegistry
是將數據庫中的類型轉換成JDK中的類型,進行對應,這個也有默認的集合,大家也可以去源碼里面看一下。
整體介紹
整體解析建造者Builder類關系圖:
使用的建造者圖解:
我們就跟著解析流程講解一下(在創建XMLConfigBuiler對象的時候會把Configuration對象創建出來):
進XMLConfigBuilder對象的parse方法看,執行了一個parseConfiguration(parser.evalNode("/configuration"));解析對象之前封裝的XNode節點數據;
有些并非很重要的方法就沒有標明,所以感興趣的可以去看一下。
解析properties標簽
解析完后會將所有的數據設置在configuration對象當中。
解析typeAliases標簽
解析plugin標簽
插件的使用這好像好沒有提到,等后面在說吧。
解析mappers標簽
這個算是解析文件里的一個比較中的東西
這時候開始使用XMLMapperBuilder建造者開始建造數據了:
我們看一下configurationElement方法:
這里的最后一個方法就開始了XMLStatementBuilder建造者開始建造了,看一些比較關鍵的地方
在創建MappedStatement對象時,使用的是流式編程
將數據全部加載完后,就是綁定工作空間了
在綁定方法中,如果namespace不是指定的接口全類名,那么就不會被添加到?mapperRegistry對象當中,這個對象就是用來跟接口做綁定的,進入到mapperRegistry的addMapper方法當中,
基本就是這么將動態代理跟namespace綁定的,構造XML的過程基本完結了,使用Mybatis的初始化已經完成,接下來就是使用持久化會話SqlSession這一塊了
創建SqlSession
SqlSession是Mybatis對外提供的一個重要接口,可以通過這個跟數據庫進行交互,或者創建接口代理對象,
UML:
創建一個默認的SqlSession工廠,這里用到了抽象工廠設計模式,之前為博客中提到了抽象工廠,那么這里就不細講了,
我們調用時創建出了一個默認的DefaultSqlSession對象,在之前的示例當中,我們可以直接調用selectList方法也能調用getMapper方法,其實直接使用SelectList方法是ibatis編程模型,當加入了getMapper方法后就是采用接口形式開發,這種開始模式比之前的ibatis開發模式要更為簡潔,并清楚業務。
我們先看一下原始的ibatis編程,即調用namespace+id方法
我們在看一下getMapper編程模式:
創建除了動態代理類之后,我們看一下代理類的實現:
這里的代理有一定的版本問題,由于JDK8支持在接口中寫方法主體,所以需做一層判斷才能執行正確的方法。
又是在invoke中執行的是invoke方法,所以在PlainMethodInvoker這個對象,執行的是mapperMethod.execute,
所以由此看出,getMapper也是基于ibatis編程模式開發。
SqlSession基本講完了,接下來就是Executor執行器了。
Executor執行器
這個用到是模板模式,我在AQS中談到的設計模式也是模板設計模式,模板模式-設計模式
BaseExecutor:抽象類,實現了executor接口的大部 分方法,主要提供了緩存管理和事務管理的能力,其他 子類需要實現的抽象方法為:doUpdate,doQuery等方法;
這個有四個對應的實現類:
SimpleExecutor:默認配置,使用PrepareStatement對象訪問數據庫,每次訪問都要創建新的 PrepareStatement對象;
ReuseExecutor:使用預編譯PrepareStatement對象訪問數據庫,訪問時,會重用緩存中的statement對象;
BatchExecutor:實現批量執行多條SQL語句的能力;
ClosedExecutor:異常執行器。
默認的方法為SimpleExecutor(在Configuration參數中有),之前看到是執行了Executor中的query方法,這里講一下:
進入到queryFormDatabase方法中:
在doQuery方法中,發現多了Executor也并非執行,讓Handler去執行:
通過對SimpleExecutor doQuery()方法的解讀發現,Executor是個指揮官,它在調度三個Handler工作:
StatementHandler:它的作用是使用數據庫的Statement或PrepareStatement執行操作,啟承上啟下作用;
ParameterHandler:對預編譯的SQL語句進行參數設置,SQL語句中的的占位符“?”都對應 BoundSql.parameterMappings集合中的一個元素,在該對象中記錄了對應的參數名稱以及該參數的相關屬性
ResultSetHandler:對數據庫返回的結果集(ResultSet)進行封裝,返回用戶指定的實體類型;
StatementHadnler
BaseStatementHandler:所有子類的抽象 父類,定義了初始化statement的操作順序, 由子類實現具體的實例化不同的statement (模板模式);
RoutingStatementHandler:Excutor組件 真正實例化的子類,使用靜態代理模式, 根據上下文決定創建哪個具體實體類;
SimpleStatmentHandler :使用statement 對象訪問數據庫,無須參數化;
PreparedStatmentHandler :使用預編譯 PrepareStatement對象訪問數據庫;
CallableStatmentHandler :調用存儲過程;
在獲取到對應的Statement對象之后就是執行Statement并封裝結果集
隨著Executor執行器執行完畢,數據也就這樣執行出來了。
Mybatis大部分的核心知識基本都在這里,但是難免還是會有一些遺漏;后面遇到了就會做一些補充。
很多插件以及可擴展的方法接口等等,Mybatis發展這么多年,不可能在一段時間內知曉,還需潛心學習。