[Mybatis] TypeHandler的簡單應(yīng)用及源碼分析

TypeHandlers

無論是 MyBatis 在預(yù)處理語句(PreparedStatement)中設(shè)置一個(gè)參數(shù)時(shí)姻蚓,還是從結(jié)果集中取出一個(gè)值時(shí), 都會(huì)用類型處理器將獲取的值以合適的方式轉(zhuǎn)換成 Java 類型抖拴。

下面是常見的一些對(duì)應(yīng)類型:


對(duì)應(yīng)類型示例
對(duì)應(yīng)類型示例

以BigDecimalTypeHandler看一下佣谐,它主要完成了哪些工作规伐。


BigDecimalTypeHandler
BigDecimalTypeHandler

這個(gè)類的第一個(gè)方法是對(duì)預(yù)處理語句(PreparedStatement)設(shè)置參數(shù)做盅,之后的三個(gè)函數(shù)都是從ResultSet或者用于執(zhí)行存儲(chǔ)過程的CallableStatement語句中獲取BigDecimal類型的數(shù)值,用于向BigDecimal類型的Java字段賦值分扎。
BigDecimalTypeHandler繼承的BaseTypeHandler是個(gè)泛型類澄成,其他的TypeHandler也是通過繼承這個(gè)抽象類,實(shí)現(xiàn)其中的抽象方法,實(shí)現(xiàn)類型轉(zhuǎn)換的工作墨状。


輸入圖片說明
輸入圖片說明

這個(gè)抽象類實(shí)現(xiàn)了TypeHandler接口卫漫,這個(gè)接口主要定義了類型轉(zhuǎn)換的幾種操作。


輸入圖片說明
輸入圖片說明

至于這個(gè)抽象類繼承的TypeReference<T>肾砂,主要是提供了獲取這個(gè)T具體是哪個(gè)類型列赎。在判斷使用使用哪個(gè)TypeHandler時(shí)有用,后文會(huì)看到镐确。


輸入圖片說明
輸入圖片說明

如何使用

大致介紹了TypeHandler的作用包吝,及其相關(guān)類,我們來看看如何使用它源葫。
今天遇到的主要是從SqlServer中取數(shù)據(jù)诗越,遇到很多列都是Numeric(10,2)類型,指的是字段是數(shù)字型,長度為10,小數(shù)為兩位息堂。Mybatis默認(rèn)的BigDecimalTypeHandler取到后嚷狞,都默認(rèn)變成4位小數(shù),不夠的補(bǔ)了0荣堰。而上層的要求是床未,拿到的和數(shù)字相關(guān)的數(shù)據(jù)都要2位小數(shù)。

有兩種做法振坚,一種是在所有給上層賦值的時(shí)候薇搁,都人工對(duì)BigDeciam的數(shù)據(jù)做如下操作。

setScale(2, BigDecimal.ROUND_HALF_UP)

因?yàn)檫@是一個(gè)全局性的要求渡八,所有相關(guān)的地方只酥,都需要有這個(gè)代碼,雖然可以寫一個(gè)工具類呀狼,各個(gè)地方調(diào)用,但就對(duì)原本間接的代碼造成了侵入损离。既然這樣哥艇,為什么不試試TypeHandler。

我的做法是繼承BigDecimalTypeHandler僻澎,覆蓋原來的取值方法貌踏,對(duì)取到的數(shù)值做范圍限定。


加上@MappedJdbcTypes注解是為了表明這個(gè)類是用于映射JdbcType的NUMERIC類型窟勃,這會(huì)覆蓋默認(rèn)的用于轉(zhuǎn)換Java BegDecimal和Jdbc NUMERIC的BigDecimal祖乳,在后面源碼中可略窺一二。

開發(fā)完這個(gè)轉(zhuǎn)換類后秉氧,你需要在Mybatis的配置文件中聲明這個(gè)TypeHandler眷昆,這樣Mybatis才知道你自己聲明了一個(gè)TypeHandler。

<typeHandlers>
    <typeHandler handler="com.codelab.learn.SubBigDecimalTypeHandler"/>
</typeHandlers>

這樣TypeHandler就起作用了。下面是前后效果亚斋。


輸入圖片說明
輸入圖片說明

輸入圖片說明
輸入圖片說明

源碼層面

首先Mybatis有一個(gè)默認(rèn)的TypeHandler實(shí)現(xiàn)作媚,這些TypeHandler是如何被Mybatis識(shí)別的呢。
答案是TypeHandlerRegistry帅刊。在Mybatis初始化配置的時(shí)候纸泡,TypeHandlerRegistry會(huì)把JdbcType和Java類型對(duì)應(yīng)的映射關(guān)系注冊(cè)進(jìn)該類內(nèi)部的Map中。


輸入圖片說明
輸入圖片說明

輸入圖片說明
輸入圖片說明

JDBC_TYPE_HANDLER_MAP中記錄的是JdbcType和TypeHandler對(duì)應(yīng)的關(guān)系赖瞒。


12
12

TYPE_HANDLER_MAP中記錄的是Java類型和對(duì)應(yīng)的所有JdbcType以及其對(duì)應(yīng)TypeHandler的映射關(guān)系關(guān)系女揭。
13
13

UNKNOWN_TYPE_HANDLER是在執(zhí)行BaseTypeHandler的抽象方法時(shí),去先解析出來該用什么TypeHandler栏饮,目前還沒用到吧兔,先不研究。
ALL_TYPE_HANDLERS_MAP中記錄的是所有TypeHandler的Class和其實(shí)例之間的映射關(guān)系抡爹。

我們以系統(tǒng)默認(rèn)注冊(cè)的三個(gè)作為例子掩驱,看看整個(gè)執(zhí)行的流程

1 register(String.class, new StringTypeHandler());
2 register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
3 register(JdbcType.NCHAR, new NStringTypeHandler());

第一個(gè)是告訴 String類型的轉(zhuǎn)換,要用StringTypeHandler冬竟。
直接進(jìn)的這個(gè)函數(shù)欧穴,因?yàn)槲覀兊腡ypeHandler上并沒有打注解,因此直接進(jìn)入箭頭標(biāo)記的邏輯泵殴。


輸入圖片說明
輸入圖片說明

然后繼續(xù)注冊(cè)涮帘,只不過jdbcType是null。


輸入圖片說明
輸入圖片說明

后續(xù)的代碼比較簡單笑诅,會(huì)先從Type_HANLER_MAP中看是否有已經(jīng)存在的 Map<JdbcType, TypeHandler<?>> map调缨,沒有的話,新建吆你,并且放入TYPE_HANDLER_MAP中弦叶,在Map<JdbcType, TypeHandler<?>> map放入此次的jdbcType和它對(duì)應(yīng)的Handler。最后在ALL_TYPE_HANDLERS_MAP放入handler的類和實(shí)例妇多。

第二個(gè)伤哺,傳入了jdbcType是NCHAR,和第一個(gè)類似者祖,但直接就進(jìn)入了最后一步的注冊(cè)環(huán)節(jié)立莉,沒有去判斷傳入什么樣的jdbcType類型,因?yàn)橐呀?jīng)指定了七问。


輸入圖片說明
輸入圖片說明

第三個(gè)是綁定了 jdbcType和Handler之間的對(duì)應(yīng)關(guān)系蜓耻。


輸入圖片說明
輸入圖片說明

OK,前面是系統(tǒng)默認(rèn)注冊(cè)進(jìn)去的械巡,那我們看一下我們?cè)谌绾问褂谜鹿?jié)中添加進(jìn)去的SubBigDecimalTypeHandler是如何被注冊(cè)進(jìn)去的呢刹淌。

Mybatis在應(yīng)用中啟動(dòng)時(shí)饶氏,會(huì)根據(jù)XML文件初始化配置,負(fù)責(zé)解析XML生成配置類的就是XMLConfigBuilder芦鳍,通過調(diào)用其中的parseConfiguration方法填充配置類嚷往。


輸入圖片說明
輸入圖片說明

箭頭表示處,就是解析typehandlers節(jié)點(diǎn)柠衅,我們看看他具體做了些什么皮仁。


輸入圖片說明
輸入圖片說明

因?yàn)槲覀儾皇菍?duì)整個(gè)package進(jìn)行注冊(cè),所以進(jìn)入else分支菲宴,因?yàn)橹槐砻髁艘粋€(gè)最簡單的Handler贷祈,所以要獲取的字段都為null,由此我們也可以看出喝峦,在編寫XML時(shí)势誊,我們也是可以直接指定映射關(guān)系的,因?yàn)楂@取不到j(luò)avaType和jdbcType谣蠢,后面應(yīng)該是會(huì)根據(jù)這個(gè)類再解析一波粟耻。跟注冊(cè)相關(guān)的又回到了TypeHandlerRegistry這個(gè)類里面,職責(zé)還是很清晰的眉踱。

輸入圖片說明
輸入圖片說明

在這個(gè)方法里面挤忙,首先會(huì)獲取有沒有打MappedType這個(gè)注解,這個(gè)注解是表明這個(gè)類對(duì)應(yīng)處理的JavaType是啥谈喳。我們這邊沒有找到册烈,因此繼續(xù)往下走。


輸入圖片說明
輸入圖片說明

從Mybatis3.1.0開始婿禽,會(huì)自動(dòng)解析這個(gè)類對(duì)應(yīng)的Java類型赏僧,還記得之前我們繼承的BigDecimalTypeHandler中我們的基類BaseTypeHandler繼承了TypeReference么?


輸入圖片說明
輸入圖片說明

這個(gè)類的構(gòu)造函數(shù)會(huì)獲取泛型中具體的類型是什么,細(xì)節(jié)代碼可以私下看一下扭倾。
獲取到了具體的Java類型淀零,我們就繼續(xù)往下傳。

輸入圖片說明
輸入圖片說明

因?yàn)槲覀兊膕ubBigDecimalTypeHandler是打了MappedJdbcType注解的膛壹,因此之后的步驟和register(String.class, JdbcType.NCHAR, new NStringTypeHandler())是一致的窑滞,可以回看上文。

到這里恢筝,TypeHandler的注冊(cè)部分已經(jīng)完成了。


在之前的關(guān)于映射的文章中巨坊,我們提過撬槽,Mybatis完成映射后,會(huì)選擇合適的TypeHandler處理器趾撵,完成對(duì)Java業(yè)務(wù)對(duì)象的賦值侄柔,我們首先找到入口在哪里共啃。

輸入圖片說明
輸入圖片說明

完成賦值的就是在1,2處暂题,我們這邊用的是自動(dòng)映射移剪,因此進(jìn)1看看,具體關(guān)于TypeHandler的處理薪者,不會(huì)有太大的差異纵苛。
在之前的createAutomaticMappings,找到列名后言津,會(huì)找出對(duì)應(yīng)的字段攻人,首先會(huì)判斷是否有對(duì)應(yīng)的TypeHandler。

輸入圖片說明
輸入圖片說明

因?yàn)槟阒懒薐DBC的類型悬槽,也通過反射知道了Java的類型怀吻。

輸入圖片說明
輸入圖片說明

這邊就首先去TYPE_HANDLER_MAP中找已經(jīng)存在的JDBC-TypeHandler的映射,如果有的話直接取初婆,沒有的話蓬坡,就默認(rèn)取null所對(duì)應(yīng)的那個(gè)類型。
因?yàn)槲覀冎纉dbc的類型是NUMERIC磅叛,而且之前注冊(cè)的SubBigDecimalTypeHandler對(duì)應(yīng)的JDBC類型是NUMERIC屑咳。

輸入圖片說明
輸入圖片說明

因此就取了更匹配的SubBigDecimalTypeHandler。
之后就是調(diào)用getResult方法宪躯,完成值的獲取即可乔宿。


總結(jié)

本文主要介紹了

  1. 什么是TypeHandler。
  2. 如何使用TypeHandler访雪。
  3. 從系統(tǒng)默認(rèn)的以及自定義的TypeHandler的注冊(cè)和獲取的角度详瑞,從源碼層面分析了整個(gè)過程。

希望得到您的點(diǎn)贊臣缀,打賞支持坝橡,謝謝。

輸入圖片說明
輸入圖片說明
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末精置,一起剝皮案震驚了整個(gè)濱河市计寇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脂倦,老刑警劉巖番宁,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赖阻,居然都是意外死亡蝶押,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門火欧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棋电,“玉大人茎截,你說我怎么就攤上這事蜕提“秆担” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵专筷,是天一觀的道長于未。 經(jīng)常有香客問我撕攒,道長,這世上最難降的妖魔是什么沉眶? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任打却,我火速辦了婚禮,結(jié)果婚禮上谎倔,老公的妹妹穿的比我還像新娘柳击。我一直安慰自己,他們只是感情好片习,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布捌肴。 她就那樣靜靜地躺著,像睡著了一般藕咏。 火紅的嫁衣襯著肌膚如雪状知。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天孽查,我揣著相機(jī)與錄音饥悴,去河邊找鬼。 笑死盲再,一個(gè)胖子當(dāng)著我的面吹牛西设,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播答朋,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼贷揽,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了梦碗?” 一聲冷哼從身側(cè)響起禽绪,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洪规,沒想到半個(gè)月后印屁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡斩例,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年库车,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片樱拴。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柠衍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晶乔,到底是詐尸還是另有隱情珍坊,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布正罢,位于F島的核電站阵漏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏翻具。R本人自食惡果不足惜履怯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望裆泳。 院中可真熱鬧叹洲,春花似錦、人聲如沸工禾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闻葵。三九已至民泵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間槽畔,已是汗流浹背栈妆。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留厢钧,地道東北人鳞尔。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像坏快,于是被迫代替她去往敵國和親铅檩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容