MyBatis 是一款優秀的持久層框架,它支持自定義 SQL、存儲過程以及高級映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數據庫中的記錄。
平時用MyBatis框架開發時,配置好config.xml和mapper.xml映射文件和定義好java接口,就可以操作數據庫了,
當然也可以像spring一樣基於注解配置,看個人情況。
mybatis配置文件
mybatis mapper映射文件(由於模塊眾多,mapper映射文件很多,只列舉一個)
然后只需定義好java接口,就能操作數據庫了
測試代碼:
執行結果:
實在簡單,我就簡單介紹下mybatis怎么一步一步運行起來的,下面才是重點
1. xml文件解析階段,怎么把xml文件解析成Configuration對象(很重要的一個全局性對象)
1.1 MyBatis Config xml配置文件
它是一種符合DTD約束的一種配置文件,可以詳細看它如何規范的http://mybatis.org/dtd/mybatis-3-config.dtd,
具體表現可在config.xml配置文件中查看其組成部分 https://mybatis.org/mybatis-3/zh/configuration.html,現在流行注解,不知道能不能很好的表現這種結構,dom樹結構
Mybatis要把該xml文件解析成一個全局性的Configurationl類型的java對象,它的類定義諸多屬性,該文件在org.apache.ibatis.session包下
下面開始對配置文件進行處理,希望你有java和xml數據綁定的開發經歷,可以參考Java and XML Data Binding.pdf https://github.com/dongguangming/java/blob/master/O'Reilly%20-%20Java%20and%20XML%20Data%20Binding.pdf
首先,我們使用 MyBatis 自帶的工具類 Resources 讀取加載配置文件,得到一個輸入流。
然后再通過 SqlSessionFactoryBuilder 對象的build
方法構建 SqlSessionFactory 對象。
從圖片上看到MyBatis 配置文件是通過XMLConfigBuilder
進行parse()解析的,繼續一層一層往下看
接着調用parseConfiguration(),剛好11個直接子節點,一個個分別解析處理
這樣,一個 MyBatis 的解析過程就出來了,每個配置的解析邏輯都封裝在了相應的方法中。由於解析模塊眾多,我就選幾個了,要不然寫不完
1.1.1 解析 properties 配置
mysql.properties屬性文件,內容如下
解析properties
節點是由propertiesElement
這個方法完成的,在上面的配置中, properties 節點配置了一個 resource 屬性。下面我們參照上面的配置,來分析一下 propertiesElement 的邏輯。
properties 節點解析的主要過程主要包含三個步驟,一是解析 properties 節點的子節點,並將解析結果設置到 Properties 對象中。二是從文件系統或通過網絡讀取屬性配置,這取決於 properties 節點的 resource 和 url 是否為空。最后一步則是將解析出的屬性對象設置到 XPathParser 和 Configuration 對象中。
需要注意的是,propertiesElement 方法是先解析 properties 節點的子節點內容,后再從文件系統或者網絡讀取屬性配置,並將所有的屬性及屬性值都放入到 defaults 屬性對象中。這就會存在同名屬性覆蓋的問題,也就是從文件系統,或者網絡上讀取到的屬性及屬性值會覆蓋掉 properties 子節點中同名的屬性和及值。
1.1.2 解析 settings 配置
1.1.2.1 settings 節點的解析過程
settings 配置是 MyBatis 中非常重要的配置,這些配置用於調整 MyBatis 運行時的行為。settings 配置繁多,在對這些配置不熟悉的情況下,保持默認配置即可。關於 settings 相關配置,MyBatis 官網上進行了比較詳細的描述,https://mybatis.org/mybatis-3/zh/configuration.html#settings,我就以我的配置舉例
接下來,對照上面的配置,來分析源碼。如下:
注意由於節點多,導致xml dom解析邏輯判斷也多,就不一一舉例細節了,但邏輯不是技術,只需要知道會生成一個Configurationl類型的java對象即可。
2. SQL語句的執行流程
再次貼下代碼
MybatisDao ud= sqlSession.getMapper(MybatisDao.class);會返回一個代理對象,繼續追蹤sqlSession.getMapper(MybatisDao.class)是如何實現的,
而knowMappers實際上存放的是
MapperProxyFactory定義如下
MapperProxy定義如下,實現了InvocationHandler接口
然后通過調用mapperProxyFactory.newInstance(sqlSession)返回代理對象
這下明白很多文章說為啥說mybatis只定義接口就能調用方法了。
此時控制台輸出
緊接着就是調用接口方法(注意是代理對象調用方法):User user = ud.selectUserById(1);
怎么拿方法不一一細看,又是一大堆邏輯,這里只看最后一句執行命令:mapperMethod.execute(sqlSession, args);
具體代碼如下
此例子中其實會執行select
session是DefaultSqlSession類型的,因為sqlSessionFactory默認生成的SqlSession是DefaultSqlSession類型。selectOne()會調用selectList()。
如圖
在DefaultSqlSession.selectList中的各種CURD操作都是通多Executor進行的,這里executor的類型是CachingExecutor,接着跳轉到其中的query方法中。
getBoundSql為了獲取綁定的sql命令,在創建完cacheKey之后,就進入到CachingExecutor 類中的另一個query方法中。
這里真正執行query操作的是SimplyExecutor代理來完成的,接着就進入到了SimplyExecutor的父類BaseExecutor的query方法中。
此時可以斷定是第一次SQL查詢操作,
所以會調用queryFromDatabase方法來執行查詢。
從數據庫中查詢數據,調用doQuery方法,進入到SimplyExecutor中進行操作。
特別注意,在prepareStatement方法中會進行SQL查詢參數的設置,也就是咱們最開始傳遞進來的參數,其值為1。handler.<E>query(stmt)方法中會進行實際的SQL查詢操作和結果集的封裝(封裝成Java對象)。
prepareStatement方法階段(即設置SQL查詢參數):
通過getConnection方法來獲取一個Connection,調用prepare方法來獲取一個Statement(這里的handler類型是RoutingStatementHandler,RoutingStatementHandler的prepare方法調用的是PrepareStatementHandler的prepare方法,因為PrepareStatementHandler並沒有覆蓋其父類的prepare方法,其實最后調用的是BaseStatementHandler中的prepare方法。是)。調用parameterize方法來設置SQL的參數值(這里最后調用的是PrepareStatementHandler中的parameterize方法,而PrepareStatementHandler.parameterize方法調用的是DefaultParameterHandler中的setParameters方法)。
此時已經給Statement設置了最初傳遞進去的參數。
那么接着分析流程2:
handler.<E>query(stmt)方法階段(SQL查詢及結果集的設置):
ResultSetWrapper是ResultSet的包裝類,調用getFirstResultSet方法獲取第一個ResultSet,同時獲取數據庫的MetaData數據,包括數據表列名、列的類型、類序號等,這些信息都存儲在ResultSetWrapper類中了。然后調用handleResultSet方法來來進行結果集的封裝。
這里調用handleRowValues方法來進行值的設置:
mapping.typeHandler.getResult會獲取查詢結果值的實際類型,比如我們user表中id字段為int類型,那么它就對應Java中的Integer類型,然后通過調用statement.getInt("id")來獲取其int值,其類型為Integer。metaObject.setValue方法會把獲取到的Integer值設置到Java類中的對應字段。
metaValue.setValue方法最后會調用到Java類中對應數據域的set方法,這樣也就完成了SQL查詢結果集的Java類封裝過程。
至此,分析完畢。和spring有點類似,只是方向不一樣,都是大量的dom解析(現在是java注解比較多了)成全局java文件,然后結合邏輯編碼實現相應的功能。
但記住:邏輯往往不是技術,如何構思、組裝才是重點!!!
注意: 你如果只是想使用mybatis(寫下配置文件和mapper文件)就不需要看此文了,也沒什么必要!
課題:留給你們一個分頁插件,構思設想和編碼如何實現???
后記:
請你們務必靈活運用這些器:分發器,過濾器,攔截器,監聽器,反應堆器,特別注意這跟語言、框架無關。
當然還有操作系統相關知識,希望其他人早意識到cpu、內存分配、io模型、進程/線程等是很重要的(會讓你更好的理解一些庫比如libevent和中間件的實現原理),也跟語言、庫、框架無關,而不管你用java、scala還是golang實現。
因為很少有網課關於操作系統和網絡的系統化培訓,那還是讓我董廣明告訴要學什么,如下
可能市面上做crud的開發者居多,那數據庫要留意下。
已把mybatis電子書上傳,很簡單 https://github.com/dongguangming/java/blob/master/MyBatis/Java%20Persistence%20with%20MyBatis%203(%E4%B8%AD%E6%96%87%E7%89%88).pdf
參考:
0. MyBatis事務 https://blog.csdn.net/dong19891210/article/details/105672535
-
SpringBoot : Working with MyBatis https://www.sivalabs.in/2016/03/springboot-working-with-mybatis/
-
mybatis配置 https://mybatis.org/mybatis-3/zh/configuration.html
-
Mybatis source code analysis https://developpaper.com/mybatis-source-code-analysis/
-
Mybatis Source Code Analysis https://programming.vip/docs/mybatis-source-code-analysis.html
-
MyBatis source code analysis https://programmer.group/mybatis-source-code-analysis.html