參考:
MyBatis結(jié)構(gòu)和組件 http://www.itdecent.cn/p/75a6a2297c69
MyBatis運行流程、緩存和插件 http://www.itdecent.cn/p/4fe280c44294
MyBatis緩存
http://www.itdecent.cn/p/7a98dda8cd75
http://www.itdecent.cn/p/8801d1aa20a0
MyBatis常見面試題 https://blog.csdn.net/a745233700/article/details/80977133
一.MyBatis概述
1.MyBatis
- 支持普通SQL查詢、存儲過程和高級映射的持久層框架
- 消除了幾乎所有的JDBC代碼和參數(shù)的手工設(shè)置以及對結(jié)果集的封裝
- MyBatis可使用XML或注解進(jìn)行配置,將接口和Java的POJO映射成數(shù)據(jù)庫記錄
- 專注于Sql本身,適用于對性能要求很高或需求變化較多的項目
2.MyBatis特點
- 優(yōu)點
1)簡單易學(xué)。MyBatis本身小而簡單
2)靈活。MyBatis允許自由編寫Sql語句
3)Sql和程序代碼解耦,且提供xml標(biāo)簽支持編寫動態(tài)sql
4)提供映射標(biāo)簽,支持對象與數(shù)據(jù)庫的ORM字段關(guān)系映射
5)與JDBC相比,減少了50%以上的代碼量 - 缺點
1)自由編寫Sql語句意味較大的工作量
2)Sql語句依賴于數(shù)據(jù)庫,導(dǎo)致數(shù)據(jù)庫無法隨意更換,移植性差
二.MyBatis原理
1.MyBatis功能結(jié)構(gòu)
- API接口層
開發(fā)人員通過接口API操縱數(shù)據(jù)庫,接口層接收請求后會調(diào)用數(shù)據(jù)處理層完成具體的數(shù)據(jù)處理 - 數(shù)據(jù)處理層
根據(jù)API接口層調(diào)用的請求完成一次數(shù)據(jù)庫操作,負(fù)責(zé)具體的Sql查找、Sql解析和執(zhí)行結(jié)果映射處理等 - 基礎(chǔ)支撐層
實現(xiàn)連接管理、事務(wù)管理和執(zhí)行結(jié)果映射處理等基礎(chǔ)功能,為數(shù)據(jù)處理層提供基礎(chǔ)支持

MyBatis功能結(jié)構(gòu)
2.MyBatis運行流程
- 加載配置
從配置文件或注解獲取配置信息并加載成MappedStatement對象,此對象=傳入?yún)?shù)的映射配置+要執(zhí)行的Sql語句+結(jié)果的映射配置 - Sql解析
API接口層接收調(diào)用請求時,根據(jù)傳入的Sql的Id找到對應(yīng)的MappedStatement,再根據(jù)傳入的參數(shù)對象解析MappedStatement,獲得最終要執(zhí)行的Sql語句和參數(shù) - Sql執(zhí)行
獲取數(shù)據(jù)庫連接,執(zhí)行Sql解析獲得的Sql語句和參數(shù),并返回結(jié)果 - 結(jié)果映射
將數(shù)據(jù)庫返回結(jié)果按映射配置轉(zhuǎn)換成需要的數(shù)據(jù)類型并返回

MyBatis運行流程
3.MyBatis主要構(gòu)件
-
SqlSession
MyBatis主要頂層API,負(fù)責(zé)和數(shù)據(jù)庫交互的會話,完成增刪查改操作 -
Executor
MyBatis執(zhí)行器,是MyBatis調(diào)度和核心,負(fù)責(zé)Sql語句生成和查詢緩存的維護(hù) -
StatementHandler
封裝了JDBC Statement,負(fù)責(zé)JDBC中對Statement的操作,如設(shè)置參數(shù)、將Statement結(jié)果集轉(zhuǎn)換為List集合 -
ParameterHandler
將用戶傳遞的參數(shù)轉(zhuǎn)換為JDBC Statement所需的參數(shù) -
ResultSetHandler
將JDBC返回的ResultSet結(jié)果集轉(zhuǎn)換為List集合 -
TypeHandler
負(fù)責(zé)Java數(shù)據(jù)類型和JDBC數(shù)據(jù)類型間的映射和轉(zhuǎn)換 -
MappedStatement
維護(hù)<select/update/delete/insert>節(jié)點的封裝 -
SqlSource
根據(jù)用戶傳遞的parameterObject,動態(tài)生成Sql語句,將信息封裝到BoundSql對象并返回 -
BoundSql
表示動態(tài)生成的Sql語句及相應(yīng)的參數(shù)信息 -
Configuration
維持MyBatis所有的配置信息

MyBatis主要組件及其相互關(guān)系
三.MyBatis緩存
1.緩存總覽

MyBatis流程圖
- 緩存在流程的Executor使用,Executor執(zhí)行查詢時先判斷緩存,若緩存命中,直接返回緩存中的結(jié)果,否則繼續(xù)之后的步驟查詢數(shù)據(jù)庫
- MyBatis使用底層為HashMap的Cache對象,通過各種裝飾類對其層層裝飾,以實現(xiàn)各種功能和隔離不同功能邏輯
- MyBatis緩存分為一級緩存和二級緩存,默認(rèn)開啟一級緩存
- 數(shù)據(jù)查詢優(yōu)先級:二級緩存---一級緩存---數(shù)據(jù)庫
2.一級緩存
- 緩存默認(rèn)Session級別(可設(shè)為Statement),同一會話的重復(fù)Sql語句可使用緩存
- 不同Session間緩存相互獨立,分布式查詢可能讀到臟數(shù)據(jù)
- Executor收到Sql語句后,先查詢自己的localCache(HashMap),緩存命中則直接返回結(jié)果,緩存命中失敗則繼續(xù)查詢數(shù)據(jù)庫后將結(jié)果寫入localCache
3.二級緩存
- 為解決跨SqlSession的緩存問題,MyBatis提供二級緩存功能
- 二級緩存使用CachingExecutor包裝Executor,CachingExecutor負(fù)責(zé)與全局二級緩存交互

二級緩存流程圖
- CachingExecutor優(yōu)先查詢二級緩存,如沒有匹配則委托給它包裝的Executor匹配的一級緩存

Executor
- 二級緩存全局有效,劃分到mapper級別(namespace定義),而非整個application使用同一緩存
- 由于不同namespace的二級緩存相互獨立,多表查詢時容易出現(xiàn)臟數(shù)據(jù)。如果將多個表的操作放在相同namespace會導(dǎo)致緩存顆粒度過大,頻繁刷新緩存
- MyBatis Cache是基于本地的,分布式環(huán)境下必然會讀取臟數(shù)據(jù),用戶自定義實現(xiàn)MyBatis的Cache接口開發(fā)成本較高,直接使用Redis/Memcached等分布式緩存成本更低,安全性更高
四.MyBatis插件
1.插件概述
- MyBatis插件又稱攔截器,采用責(zé)任鏈模式,通過動態(tài)代理組織多個插件(攔截器),改變MyBatis默認(rèn)行為(如Sql重寫)
- 插件通過動態(tài)代理方式在已映射語句執(zhí)行過程中的某一點進(jìn)行攔截調(diào)用
- MyBatis插件需要實現(xiàn)Interceptor接口,該接口主要方法有
1)setProperties:配置MyBatis插件的自定義屬性
2)plugin:插件用來封裝目標(biāo)對象的方法,通過該方法可返回目標(biāo)對象本身或一個它的代理
3)interceptor:攔截時執(zhí)行的方法
4)plugin接口
五.MyBatis與其他框架
1.MyBatis與iBatis
- iBatis是Apache的一個開源項目,該項目在2010年從Apache Software Foundation遷移到Google Code,并改名MyBatis
- MyBatis是IBatis的升級版,MyBatis在以下方面改善了IBatis功能
1)MyBatis實現(xiàn)接口綁定,使用更方便
2)MyBatis改進(jìn)了對象關(guān)系映射(association和collection)
3)MyBatis在動態(tài)Sql語句中采用OGNL表達(dá)式取代節(jié)點配置
2.MyBatis與Hibernate
- MyBatis
1)Mybatis真正實現(xiàn)了Java代碼和Sql分離
2)Mybatis自行編寫sql,靈活度高,便于Sql優(yōu)化 - Hibernate
1)Hibernate數(shù)據(jù)庫無關(guān)性好,適用于需求固定的定制化軟件,但學(xué)習(xí)成本高
2)Hibernate大大降低了對象和數(shù)據(jù)庫的耦合度,其數(shù)據(jù)移植性遠(yuǎn)大于MyBatis
3)Hibernate在多表關(guān)聯(lián)查詢時表現(xiàn)較差
4)Hibernate效率較低,Sql自動生成,不便于人工優(yōu)化,調(diào)優(yōu)需要相當(dāng)經(jīng)驗積累 - 場景選擇
1)數(shù)據(jù)量大(超過千萬級別的表/單次業(yè)務(wù)大批量數(shù)據(jù)(百萬條)提交或讀取)適用MyBatis
2)表關(guān)聯(lián)度(主要業(yè)務(wù)表的關(guān)聯(lián)表>20)復(fù)雜適用MyBatis
六.常見面試題
1.SqlSession創(chuàng)建過程和相關(guān)對象生命周期?
SqlSessionFactoryBuilder---SqlSessionFactory---SqlSession---SqlMapper-
SqlSessionFactoryBuilder在構(gòu)建SqlSessionFactory后即可銷毀 -
SqlSessionFactory存在于整個MyBatis應(yīng)用中,類似數(shù)據(jù)庫連接池,不斷創(chuàng)建SqlSession -
SqlSession類似于數(shù)據(jù)庫連接,在完成一次業(yè)務(wù)請求后回收 -
SqlMapper由SqlSession創(chuàng)建,生命周期SqlMapper<=SqlSession

2.#{}和${}的區(qū)別?
- 前者’#{}‘是預(yù)編譯處理,MyBatis預(yù)編譯時會將#{}替換為?號,調(diào)用PreparedStatement的set方法賦值
- 后者是xml文件的字符串替換,MyBatis會把${}替換成變量的值
- 使用#{}可防止Sql注入,提高安全性
3.實體類的屬性名和表中字段名不一樣該如何映射?
- 在Sql語句中定義與實體類屬性名一樣的別名
- 通過<resultMap>映射字段名和實體類屬性名
<select id="getOrder" parameterType="int" resultMap="orderresultmap">
select * from orders where order_id=#{id}
</select>
<resultMap type=”me.gacl.domain.order” id=”orderresultmap”>
<!–用id屬性來映射主鍵字段–>
<id property=”id” column=”order_id”>
<!–用result屬性來映射非主鍵字段,property為實體類屬性名,column為數(shù)據(jù)表中的屬性–>
<result property = “orderno” column =”order_no”/>
<result property=”price” column=”order_price” />
</reslutMap>
4.每個xml映射文件都有一個Dao接口與之對應(yīng),Dao接口的工作原理是什么?Dao接口里的方法在參數(shù)不同時是否可以重載?
- Dao接口即Mapper接口:映射的xml文件的namespace是接口的全限定名;映射文件中MappedStatement的id是接口的方法名;接口方法的參數(shù)就是傳遞給sql的參數(shù)
- 調(diào)用接口方法時,通過接口全限定名+方法名定位唯一的MappedStatement
- 由于僅使用接口全限定名和方法名定位MappedStatement,故不同參數(shù)的接口方法不能重載
- 此外,不同的xml配置文件,如果設(shè)置不同的namespace,則id可重復(fù),否則不可重復(fù)
5.MyBatis是如何進(jìn)行分頁的?分頁插件的原理是什么?
- MyBatis使用RowBounds對象針對ResultSet結(jié)果集執(zhí)行內(nèi)存分頁,也可以在Sql內(nèi)直接書寫帶有物理分頁的參數(shù)實現(xiàn)物理分頁
- 分頁插件實現(xiàn)MyBatis提供的插件接口,在插件的攔截方法里攔截并重寫待執(zhí)行的sql
6.MyBatis動態(tài)Sql是做什么的?其執(zhí)行原理是什么?
- MyBatis允許在xml映射文件中以標(biāo)簽形式編寫動態(tài)Sql,完成邏輯判斷和動態(tài)拼接Sql的功能
- MyBatis提供 trim | where | set | foreach | if | choose | when | otherwise | bid 9種動態(tài)sql標(biāo)簽
- 執(zhí)行原理為
1)使用OGNL從sql參數(shù)對象中計算表達(dá)式的值
2)根據(jù)表達(dá)式值動態(tài)拼接Sql
7.MyBatis是否支持延遲加載?其實現(xiàn)原理是什么?
- 延遲加載又稱懶加載,用于處理N+1性能問題
- N+1問題指映射集合(resultMap)級聯(lián)時,我們需要的數(shù)據(jù)少于數(shù)據(jù)庫查出的數(shù)據(jù),浪費性能且加重數(shù)據(jù)庫負(fù)擔(dān)
- MyBatis僅支持association關(guān)聯(lián)對象(1對1)和collection關(guān)聯(lián)集合(1對多)的延遲加載,配置文件中參數(shù)LazyLoadingEnabled=true|false設(shè)置是否開啟延遲加載
- MyBatis通過動態(tài)代理實現(xiàn)延遲加載
參考:https://my.oschina.net/wenjinglian/blog/1857581?from=singlemessage
8.使用MyBatis的mapper接口調(diào)用時有哪些要求?
- Mapper接口方法名和mapper.xml定義的sql的id相同
- Mapper接口方法的輸入?yún)?shù)類型和mapper.xml定義的sql的parameterType類型相同
- Mapper接口方法的輸出參數(shù)類型和mapper.xml定義的sql的resultType類型相同
- Mapper.xml的namespace即是mapper接口的類路徑
9.mapper有哪幾種編寫方式?
- 接口實現(xiàn)類繼承SqlSessionDaoSupport類
- 使用org.mybatis.spring.mapper.MapperFactoryBean
- 使用mapper掃描器
10.mapper如何傳遞多個參數(shù)?
- xml使用#{0}接收dao層第一個參數(shù),#{1}接收第二個參數(shù),以此類推
- 使用@param注解