擴(kuò)展mybatis generator(已同時(shí)發(fā)布到我的CSDN博客上)

前段時(shí)間在優(yōu)化部門的codegen項(xiàng)目的時(shí)候沉帮,要將jdbc全部替換成mybatis去執(zhí)行,有一些個(gè)性化的需求單純的mybatis generator不能滿足贫堰,于是特意研究了下mybatis穆壕,解決在不改造源碼的情況下去另類的”擴(kuò)展“mybatis generator,由于擴(kuò)展實(shí)際上是根據(jù)mybatis的套路去進(jìn)行擴(kuò)展其屏,所以這里先在第一段介紹一下mybatis-spring的執(zhí)行原理喇勋,第二段會(huì)放出例子表明如何進(jìn)行擴(kuò)展

一.mybatis-spring執(zhí)行原理

(1)掃描basePackage,用于將mapper接口掃描成MapperFactoryBean注冊(cè)到spring

mybatis-spring里面偎行,我們通過(guò)MapperScannerConfigurer設(shè)置basePackage路徑涂籽,確定要掃描的Mapper接口,實(shí)際上當(dāng)我們配置了這個(gè)basePackage之后显押,mybatis會(huì)掃描這個(gè)路徑下的所有Mapper接口袜漩,并為每個(gè)Mapper接口初始化成一個(gè)MapperFactoryBean對(duì)象,在執(zhí)行的時(shí)候,會(huì)通過(guò)這個(gè)MapperFactoryBean對(duì)象的getObject()方法為每個(gè)Mapper接口生成一個(gè)proxy對(duì)象,通過(guò)jdk的反射完成,下面來(lái)探究一下源碼荚守。


從第一張圖片可以看到,MapperScannerConfigurer實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口练般,在正常的bean注冊(cè)完之后矗漾,可以進(jìn)一步做一些自定義的bean操作,我們可以看到第二張圖哪里踢俄,MapperScannerConfigurer會(huì)執(zhí)行一個(gè)scanner.scan方法去讀取basePackage下面的mapper接口缩功,scanner里面會(huì)調(diào)用doScan方法去掃描basePackage下面的Mapper接口成MapperFactoryBean并注冊(cè)到spring容器里面。


在doScan方法里面都办,首先會(huì)調(diào)用findCandidateComponents()方法去將basePackage下面的Mapper類掃面成一個(gè)BeanDefinition的集合嫡锌,然后對(duì)這些beanDefinition進(jìn)行解析成BeanDefinitionHolder,最后通過(guò)注冊(cè)到spring容器里面琳钉。獲取到這些BeanDefinition之后势木,再去調(diào)用processBeanDefinitions方法將beanDefinition設(shè)置beanClass為MapperFactoryBean,在第一次需要使用這個(gè)bean的時(shí)候spring就會(huì)根據(jù)beanClass通過(guò)反射將對(duì)應(yīng)的bean對(duì)象生成出來(lái)保存在map里面歌懒。

Paste_Image.png
Paste_Image.png
(2)掃描mapperLocation啦桌,掃描xml將xml里面節(jié)點(diǎn)跟namespace對(duì)應(yīng)的mapper結(jié)合起來(lái)

SqlSessionFactoryBean會(huì)在spring初始化的時(shí)候調(diào)用它的afterPropertiesSet方法,然后再里面調(diào)用buildSqlSessionFactory方法及皂,掃描mapperLocation路徑下面的xml甫男,根據(jù)namespace去找到對(duì)應(yīng)的mapper接口,并調(diào)用bindMapperForNamespace()方法將xml和對(duì)應(yīng)的mapper綁定

綁定實(shí)際上是通過(guò)反射的Class.forName方法根據(jù)namespace找到對(duì)應(yīng)的class對(duì)象验烧,根據(jù)這個(gè)class對(duì)象創(chuàng)建成MapperProxyFactory對(duì)象保存在knownMappers這個(gè)map里面板驳。

值得注意的是,mybatis在parsePendingStatements這個(gè)方法里面會(huì)將每個(gè)xml里面的節(jié)點(diǎn)(select,delete,update,insert)封裝成一個(gè)MappedStatement對(duì)象碍拆,最后保存在一個(gè)mappedStatements的map里面(保存的key是獲取到xml的namespace+節(jié)點(diǎn)id)在執(zhí)行的時(shí)候,mybatis會(huì)根據(jù)namesapce+id的方式去這個(gè)map里面找對(duì)應(yīng)的MappedStatement對(duì)象若治,然后再去執(zhí)行。

在parseStatementNode方法里面感混,會(huì)解析每個(gè)xml節(jié)點(diǎn)端幼,最后調(diào)用一個(gè)builderAssitant.addMappedStatement方法去生成一個(gè)MappedStatement對(duì)象

builderAssitant.addMappedStatement方法會(huì)調(diào)用configuration.addMappedStatement方法將創(chuàng)建好的MappedStatement對(duì)象put進(jìn)去mappedStatements的map里面


在put進(jìn)這個(gè)map的時(shí)候,mybatis是根據(jù)namespace+id(selectByPrimaryKey)作為key來(lái)put進(jìn)去mapper弧满,這樣在以后代理類執(zhí)行的時(shí)候就是根據(jù)mapper的全路徑+方法名就可以找到對(duì)應(yīng)的mappedStatement對(duì)象

Paste_Image.png
(3)mybatis執(zhí)行

mybatis在執(zhí)行的時(shí)候婆跑,首先通過(guò)MapperFactory.getObject()方法去調(diào)用getMapper方法,getMapper會(huì)根據(jù)type(即根據(jù)namespace反射生成的class對(duì)象)去knowMapper里面找到對(duì)應(yīng)的MapperProxyFactory對(duì)象庭呜,然后通過(guò)mapperProxyFactory.newInstance(sqlSession)方法為每個(gè)mapper生成一個(gè)MapperProxy代理類(通過(guò)java的jdk代理)滑进,然后再通過(guò)這個(gè)代理類去執(zhí)行mybatis




MapperProxy類執(zhí)行的時(shí)候摹迷,會(huì)首先調(diào)用invoke方法,除了是Object類的方法之外郊供,其他的都會(huì)調(diào)用cachedMapperMethod這個(gè)方法去獲取緩存在methodCache里面的MapperMethod,當(dāng)在map里面獲取不到這個(gè)對(duì)象時(shí)近哟,通過(guò)new MapperMethod方法去重新put進(jìn)去這個(gè)map驮审。在創(chuàng)建MapperMethod對(duì)象的時(shí)候,會(huì)調(diào)用一個(gè)new SqlCommand()方法吉执,我們可以看到經(jīng)常遇見到的"invalid bound statement"錯(cuò)誤也是在這里拋出疯淫,statementName實(shí)際上就是nameSpace+方法名字(也就是xml里面節(jié)點(diǎn)的id),通過(guò)這個(gè)在mappedStatement的map里面去獲取到對(duì)應(yīng)的MappedStatement對(duì)象戳玫,然后再根據(jù)這個(gè)MappedStatement對(duì)象去執(zhí)行熙掺。





二.擴(kuò)展mybatis generator

既然我們已經(jīng)知道m(xù)ybatis的執(zhí)行原理,那么去擴(kuò)展mybatis generator就簡(jiǎn)單了咕宿,因?yàn)閙ybatis是通過(guò)namespace去綁定xml和對(duì)應(yīng)的Mapper接口币绩,那么在需要個(gè)性化需求的時(shí)候,我們可以將一些基礎(chǔ)不變的方法放到BaseMapper里面府阀,然后用個(gè)性化的CustomMapper去繼承那個(gè)BaseMapper缆镣,然后將基礎(chǔ)的xml放到一個(gè)base.xml,個(gè)性化的xml放到custom.xml,只要兩個(gè)xml的namespace都是對(duì)應(yīng)于CustomMapper试浙,那么mybatis在初始化的時(shí)候就都會(huì)將xx.xx.CustomMapper.xx這樣作為一個(gè)key董瞻,對(duì)應(yīng)的MappedStatement對(duì)象保存到map里面,在執(zhí)行的時(shí)候就能按照正常的mybatis執(zhí)行方式去執(zhí)行sql田巴。

按照這個(gè)結(jié)構(gòu)钠糊,我們可以將mybatis generator的生成在baseMapper和base.Xml里面,在將個(gè)性化的寫在CustomMapper和Custom.Xml里面壹哺,但是最終我們要使用的時(shí)候只需要CustomMapper就可以使用全部的方法抄伍。

例子:
基礎(chǔ)的baseXml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yue.dao.custom.mybatis.CustomMapper">
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
    -->
    delete from demo_table
    where `id` = #{id,jdbcType=BIGINT}
  </delete>
</mapper>

自定義的customXml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yue.dao.custom.mybatis.CustomMapper">
     <update id="updateByPrimaryKeySelective" parameterType="com.yue.domain.DemoTable">
        update demo_table
        <set>
            <if test="dbNo != null">
                `db_no` =  #{dbNo},
            </if>

            <if test="globalId != null">
                `global_id` =  #{globalId},
            </if>

            <if test="updatedBy != null">
                `updated_by` =  #{updatedBy},
            </if>


        </set>
        <where>
            `id` = #{id,jdbcType=BIGINT}
            <if test="versionNumber != null">
                and `version_number` = #{versionNumber}
            </if>
        </where>
    </update>
</mapper>

BaseMapper代碼:

public interface BaseMapper {

    int deleteByPrimaryKey(Long id);
    
    /**
     * This method was generated by MyBatis Generator. This method corresponds to the database table auth_menu
     *
     * @mbg.generated
     */
    int updateByPrimaryKey(AuthMenu record);
    
}

CustomMapper代碼:

package com.yue.dao.custom.mybatis;

import com.vip.fcs.app.ar.intfc.dao.mybatis.base.BaseMapper;

/**
 * 對(duì)應(yīng)表名:菜單 個(gè)性化處理
 */
public interface CustomMapper extends BaseMapper{

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市斗躏,隨后出現(xiàn)的幾起案子逝慧,更是在濱河造成了極大的恐慌,老刑警劉巖啄糙,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笛臣,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡隧饼,警方通過(guò)查閱死者的電腦和手機(jī)沈堡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)燕雁,“玉大人诞丽,你說(shuō)我怎么就攤上這事鲸拥。” “怎么了僧免?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵刑赶,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我懂衩,道長(zhǎ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
  • 文/蒼蘭香墨 我猛地睜開眼典徘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蟀苛!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起逮诲,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤帜平,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后梅鹦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體裆甩,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有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
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至田度,卻和暖如春妒御,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背镇饺。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工乎莉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奸笤。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓惋啃,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親监右。 傳聞我的和親對(duì)象是個(gè)殘疾皇子边灭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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

  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL健盒、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,453評(píng)論 0 4
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理绒瘦,服務(wù)發(fā)現(xiàn),斷路器扣癣,智...
    卡卡羅2017閱讀 134,628評(píng)論 18 139
  • Java數(shù)據(jù)持久化之mybatis 一. mybatis簡(jiǎn)介 1.1 原始的JDBC操作: Java 通過(guò) Jav...
    小Q逛逛閱讀 4,901評(píng)論 0 16
  • 1 引言# 本文主要講解JDBC怎么演變到Mybatis的漸變過(guò)程惰帽,重點(diǎn)講解了為什么要將JDBC封裝成Mybait...
    七寸知架構(gòu)閱讀 76,441評(píng)論 36 980
  • 今天6點(diǎn)多起來(lái)了,昨天晚上醒了又睡睡了又醒搏色,終于天亮了昨天晚上天黑基本就沒(méi)信號(hào)了善茎,今天也是,白天信號(hào)斷斷續(xù)續(xù)频轿,所以...
    楊穎__指數(shù)成長(zhǎng)閱讀 369評(píng)論 3 2