一口吃個胖子,吃透Mybatis

什么是Mybatis

CRUD框架,面向數(shù)據(jù)庫開發(fā)的腳手架。它提供了支持CRUD操作,還具有以下特性。

Mybatis這套框架的核心在于遵循到位了開閉原則,里氏替換原。組件思想,抓核心思想等。

Mybatis分為三大組件:SQL組裝層,數(shù)據(jù)處理轉(zhuǎn)換層,插件管理層。

Mybatis的核心對象有:BoundSQL,Configuration,SqlSession,MapperProxy,StatementHandler.
一張圖描述下查詢List的幾個步驟


list.jpg

查詢在mybatis大概執(zhí)行過程,"↓"表示下一個執(zhí)行片段。

SqlSessionFactory

↓ 創(chuàng)建SqlSesssion,SqlSession相當于Mybatis的執(zhí)行引擎 預裝載datasource,進入configuration,

注意Datasource如果是在依賴注入容器里,也是Singletone模式的

Configuration

根據(jù)ID在configuration里面找到相應(yīng)的命名空間維度的配置項,Mybatis設(shè)計上基本都是一次初始永久使用的,交給mapperProxy代理類去處理,MapperProxy處理類由它的工廠類MapperProxyFactory創(chuàng)建

MapperProxy 執(zhí)行invoke,這里面有個cache機制

找到MapperMethod觸發(fā)execute,里面有根據(jù)SQLCommandType去路由到不同的處理方式

SqlSession insert() or update()

Plugin 是否有Plugin機制

RoutingStatementHandler 路由處理

ParameterHandler 執(zhí)行參數(shù)處理,

ResultHandler 結(jié)果集處理,游標處理。映射成返回對象

DebuggingPrintLog 有開啟Debug日志才會代理Stmt 或者Psmt或者 Result代理

TransactionHandler 關(guān)閉事物,如果有Spring食物切面就會托管給它去做這個事情

Spring事物代理切面處理器

*可拓展

*調(diào)用鏈短

*支持內(nèi)存分頁

*手動SQL,滿足對SQL性能有要求場景

SQL支持面向語言用注解方式進行組裝,或者用ONGL的方式將SQL邏輯表現(xiàn)在XML語言文檔中

*Mybatis不像Hibernate是一個真正的持久化框架,HBT更像是弱化了SQL這一基礎(chǔ)技能屬性。MB的SQL始終是具備完整性的。

*該框架提供了別名機制,別名可以外部配置然后在運行階段一次性加載到全局的configuration中

Mybatis代碼非常精簡,上手非常容易。源碼理解程度除了ONGL這塊比較抽象,其他部分都很容易理解,本文也是化煩為簡地方式來講解,適用于初學者和老手。Mybatis是Web開發(fā)里面最簡單的框架,很容易了解到原理。

如何快速理解一套框架,我的建議是把它當做一個工具集,既然是工具,先理解它的構(gòu)造和設(shè)計原理,先通過官網(wǎng)描述了解它實現(xiàn)了什么功能,這些功能分別是什么,然后可以以架構(gòu)師的角度去了解它的作用場景。

當了解了以上這些后,再去庖丁解牛的方式去解開它的內(nèi)部構(gòu)造,一般高級語言實現(xiàn)的一個框架在代碼的結(jié)構(gòu)上都是分包的,接著去了解它每個包的作用域,然后再去抽象出它所用到的設(shè)計模式??紤]一下為什么要用到這個設(shè)計模式,當了解了這些之后,就可以上升到如何用好和拓展這個框架了。

*設(shè)計模式:

包裝器(Wrapper關(guān)鍵字的類群體),JDK動態(tài)代理模式,Cglib代理模式。(MapperInterface類),靜態(tài)工廠模式(SqlSessionFactory,
LoggingFactory,
MapperProxyFactory),建造者模式(包含關(guān)鍵字builder的類),策略模式(隱藏式設(shè)計模式,緩存策略模式,日志輸出策略模式,StatementHandler模式,使用者不需要關(guān)注處理細節(jié),只需要告訴Mybatis你要做什么,只需要體現(xiàn)使用者的行為即可),裝飾器模式,ONGL中用到了組合模式(<if>,<else><choosen>等組合tag),Plugin衍生的使用攔截器場景使用的責任鏈設(shè)計模式等(攔截器就是All-in-List then execute-stepbystep-in-chainGroup)。

模擬通訊服務(wù)上的路由器的網(wǎng)絡(luò)分發(fā)功能實現(xiàn)的策略模式:

通過RoutingStatementHandler類就是一個路由器,然后通過StatementType類型來決定跳轉(zhuǎn)到不同的handler。

*包結(jié)構(gòu)

排名不分先后順序

annonations:注解SQL的,作用于SqlBuilder使用場景

binding: 實現(xiàn)Mapper的綁定,里面有個MapperRegister

builder:構(gòu)造Mapper,解析Configuration的基礎(chǔ)配置等

cache:緩存包,裝飾器模式。比如與依賴注入控制反轉(zhuǎn)框架Spring使用時要以外部注入裝載的方式引入redis等緩存中間件。

cursor:游標,獲取Jdbc游標的迭代位置

datasource:數(shù)據(jù)源[jndi數(shù)據(jù)源,普通數(shù)據(jù)源(BasicDatasource)],基于數(shù)據(jù)庫驅(qū)動包的條件下,才能ping上對應(yīng)的數(shù)據(jù)庫,同時,需要設(shè)置鏈接,鏈接的必要屬性一般都是要用戶名和密碼認證通過才可以。

下面是一個jdbc接口和數(shù)據(jù)庫交互的大致過程~
加載jdbc驅(qū)動包->創(chuàng)建鏈接->開啟會話->開啟事物->創(chuàng)建statement->讀場景游標方式迭代結(jié)果集->關(guān)閉statement->寫場景關(guān)閉事物->_遇到異?;貪L事物

exceptions:異常包,這部分使用到了是factory model。

executor:支持JDBC操作的入口包

plugin:插件包,通?;诓寮C制可以實現(xiàn)分頁,SQL執(zhí)行效率上的性能監(jiān)控。

IO:流處理

session: JDBCSession等

logging:日志組件,開啟debug模式時,針對Statement和result進行了打印日志的代理攔截,不影響原來的流程,該怎么往下執(zhí)行還得繼續(xù)。

transaction:事務(wù)包,mybatis對事物這塊簡單地加工了一下,可以支持強制關(guān)閉操作等行為

parsing:sql語句轉(zhuǎn)換包

scripting:腳本包

type: JDBC type handler

mapping: 映射包。鑒別器,boundSQL,resultMap,這個包里面涵蓋了

sql屬性維度,columnType維度,resultMap等維度與ONGL之間的映射模型。這里面幾乎都是PlainObject,幾乎沒有行為透露和傳播。

reflect:反射機制包,針對數(shù)據(jù)庫字段提取出值,通過反射機制映射到property字段里。

獲取反射類內(nèi)部用了取出一次就緩存的動作的。


核心類

Confiuration:Mybatis的核心配置類,頂層類,xml配置項,由XMLConfigurationBuilder提供解析裝載。它提供你全局獲取ResultMap,獲取MapMethed.

MapperRegister:注冊Mapper類。

SqlSessionFactory:通過build創(chuàng)建sqlSession,需要自定義一個datasouce,

比如集成到Spring框架中時會先定義一個DataSource。

BaseExecutor:執(zhí)行 insert,update,select

,delete操作。Mybatis嚴格按照jdbc的CRUD操作返回的類型進行一一對應(yīng)設(shè)計。

MapperProxy:代理類,用jdk動態(tài)代理機制,創(chuàng)建字節(jié)碼方式。XML可以不需要定義接口,但是定義接口就必須要它所包含的package全路徑需要是正確的路徑。

ResultTypeHandler:Jdbc中的ResultType映射成返回ResultType使用的Java類型。Mybatis定義了自己的JdbcType,為什么它不fixed對應(yīng)的Java類型,這是基于開閉原則,因為用戶拿到的type尤其是char這樣的類型豐富多變。它有個TypeRegister機制,用戶可以定義自己的type作用于rst.

SqlSesession:就是用來處理connection,connection的處理流程是:開啟事物,創(chuàng)建stm或者pstm或者callstm,獲取結(jié)果集返回的游標,關(guān)閉事物(遇到異常拋出來)

CacheProvider:緩存提供者,MB提供了namespace作用域緩存和sqlsession兩種緩存作用域。用裝飾者設(shè)計模式設(shè)計出來了多種不同方式的緩存機制。緩存是個多余的設(shè)計,不滿足業(yè)務(wù)場景,一般業(yè)務(wù)層應(yīng)用開發(fā)都單獨使用緩存中間件。比如Spring+redis實現(xiàn)。如果業(yè)務(wù)上的數(shù)據(jù)只需要隔離數(shù)據(jù)庫,可以實現(xiàn)它的cache接口,用redisCache,用靜態(tài)方法獲取靜態(tài)注入的RedisSessionFacotory來使用。

,,,

doPut ();
clear();
update();

,,,

image

ResultMap:核心類,它的上層是namspace,namespace實際上是存在于xml中,ResultMap是結(jié)果集映射,它的是結(jié)構(gòu)化的,語義上的表現(xiàn)結(jié)構(gòu)是由property與associate,collection組成。這些ONGL語言標簽由mybatis解析。RSTM它對應(yīng)的是一個map容器存在。從另一個角度來說,該框架一部分是語義型,一部分是將語義結(jié)構(gòu)化的部分用組件的方式一一抽離出來的。

它的Id是由上往下一直到parameter組裝的字符串形式。它是放在一個Map容器的。當SqlSessionFactory完成build以后,mybatis需要的config->作用的對象,cache生成,日志形式等就隨著build動作結(jié)束以后完成所有的初始化了。

StatementHandler:處理statement,jdbc基礎(chǔ)知識,可以預處理,存儲過程或函數(shù)處理。

PooledConnection:簡單地實現(xiàn)了連接池,池化的關(guān)鍵是基于線程池創(chuàng)建管理數(shù)據(jù)庫鏈接池,一方面減少線程上下文切換奢侈開支的同時又,另一方面又復用了數(shù)據(jù)庫連接池。它里面會檢測壞的連接池,壞鏈接會專門管理,復活會重新使用。對于它里面的設(shè)計只需要改進連接池隊列和利用aqs來更高的處理就可以演化成一個好用的連接池。

*特性

1.事務(wù)性

2.SQL日志
SQL日志可以用攔截器的方式美化query和update的語句。

3.Plugin特性(指定簽名方法進行攔截,可以實現(xiàn)SQL性能監(jiān)控,分頁,租戶等機制),mybatis支持多個plugin同時存在,沒有嚴格意義上的先后執(zhí)行順序,由定義順序來決定的,業(yè)務(wù)上有需要的場景有:分頁功能,數(shù)據(jù)加密,防止SQL注入,schema鑒定(比如mybatis拿到用戶域以后可以指定用戶的schema,SQL性能監(jiān)控,慢查詢直接中斷查詢,但是就算關(guān)閉鏈接,數(shù)據(jù)庫的任務(wù)也不會終止)

4.緩存機制(namespace層,SqlSession粒度的緩存,一個是作用域在命名空間,一個作用域在session粒度).
關(guān)于緩存,Mybatis里面提供了LRU緩存機制,如果感興趣的朋友可以自行實現(xiàn)。因為緩存不是Mybatis的重要屬性

下面如何添加一個FILO緩存代碼示例

importjava.util.Deque;

importjava.util.LinkedList;

importorg.apache.ibatis.cache.Cache;

/**

* FILO (first in, last out) cache decorator.

*

* @author mybatis

*/

publicclassFifoCacheimplementsCache{

privatefinalCachedelegate;

privatefinalDeque<object style="box-sizing: border-box;">keyList;</object>

privateintsize;

publicFifoCache(Cachedelegate) {

this.delegate=delegate;

this.keyList=newLinkedList<>();

this.size=1024;

  }

@Override

publicStringgetId() {

returndelegate.getId();

  }

@Override

publicintgetSize() {

returndelegate.getSize();

  }

publicvoidsetSize(intsize) {

this.size=size;

  }

@Override

publicvoidputObject(Objectkey,Objectvalue) {

cycleKeyList(key);

delegate.putObject(key,value);

  }

@Override

publicObjectgetObject(Objectkey) {

returndelegate.getObject(key);

  }

@Override

publicObjectremoveObject(Objectkey) {

returndelegate.removeObject(key);

  }

@Override

publicvoidclear() {

delegate.clear();

keyList.clear();

  }

privatevoidcycleKeyList(Objectkey) {

keyList.addLast(key);

if(keyList.size() >size) {

ObjectoldestKey=keyList.removeLast();

delegate.removeObject(oldestKey);

    }

  }

}

5.支持ONGL的SQL寫法,支持SqlBuilder以注解形式組裝 寫SQL,然后表現(xiàn)形式 以顯示在方法上

6.支持拓展,比如國產(chǎn)化的Mybatis-Plugin框架,這個框架就是改變MapperBuilder這個內(nèi)部機制的基礎(chǔ)上將單個表的增刪改查方法嵌入在SqlMethod里面了。這個框架還實現(xiàn)了低代碼思想,并支持Stream流式寫法。

7.寫了一個不支持高并發(fā)的鏈接池,低吞吐量用戶量少時可用

一句話結(jié)束

Mybatis是JAVA-Web開發(fā)中國內(nèi)最流行的一套面向數(shù)據(jù)庫開發(fā)的框架,它是一款面向?qū)ο笤O(shè)計思想的框架。簡單易上手,深受廣大各個級別的程序員喜愛。

問:How to designe a new framework that is better than mybatis.

問:What is the lack functions of current mybatis-plugin

問:How to avoid restart the web application when some resources ref mybatis has changed by user

答:定義FileChangeListern事件,監(jiān)聽文件。先找到修改文件對應(yīng)的MapperInterface,指定它重新注入mapper,用MapperRegister重新注冊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容