再解析一個(gè)MyBatis Generator 插件

【原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明原文章地址甥厦,謝謝纺铭!】

Paste_Image.png

在本文中,我們?cè)賮砜匆粋€(gè)MyBatis Generator插件:
org.mybatis.generator.plugins.MapperConfigPlugin刀疙。同樣舶赔,首先來看一下這個(gè)插件的使用方式,
前面我們已經(jīng)介紹了ToStringPlugin插件的作用谦秧,是為KeyClass顿痪,Record Class和BlobClass提供toString方法的。首先我們了解一下ToStringPlugin的用法油够。
在generatorConfig.xml中加上配置:

<plugin type="org.mybatis.generator.plugins.MapperConfigPlugin">
        <property name="targetPackage" value="com._520it.mybatis"/>
        <property name="targetProject" value="src/main/resources"/>
    </plugin>

首先蚁袭,這個(gè)插件的運(yùn)行必須要配置targetPackage和targetProject兩個(gè)參數(shù),所以我們可以通過這個(gè)插件的代碼閱讀石咬,學(xué)習(xí)到參數(shù)的使用揩悄,并且學(xué)到在MBG中對(duì)文件路徑和package的使用;

運(yùn)行MBG鬼悠,在resources下面生成了com._520it.mybatis包删性,并且正確添加了一個(gè)MapperConfig.xml(如果你還記得,這個(gè)文件的名字是可以通過fileName參數(shù)來設(shè)置的焕窝,默認(rèn)的文件名就是MapperConfig.xml)蹬挺,生成的文件如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration >
  <!--
This file is generated by MyBatis Generator.
This file is the shell of a Mapper Config file - in many cases you     will need to add
  to this file before it is usable by MyBatis.
This file was generated on Thu Sep 03 22:14:48 CST 2015.
  -->
  <mappers >
    <mapper resource="com/_520it/mybatis/mapper/AccountMapper.xml" />
  </mappers>
</configuration>

可以看到,這僅僅只是一個(gè)非乘啵空的mybatisConfig.xml文件巴帮,僅僅只添加了本次生成的Mapper.xml文件的mapper元素溯泣;
同樣的,我們先明確我們看代碼之前的目標(biāo):

  • 前面已經(jīng)說到榕茧,要查看參數(shù)的傳遞和處理方法垃沦;
  • 學(xué)習(xí)處理文件路徑相關(guān)方法;
  • 學(xué)習(xí)為MBG添加額外生成的文件用押;
  • 學(xué)習(xí)MBG中XML包裝DOM的使用肢簿;
  • 學(xué)習(xí)獲得本次生成的所有mapper.xml文件的方法;

同理蜻拨,我們先思考一下這個(gè)插件的完成方式:

  • 在plugin生命周期的時(shí)候已經(jīng)提到了池充,MBG會(huì)執(zhí)行一個(gè)生成額外文件的方法contextGenerateAdditionalXmlFiles,可以通過那個(gè)方法來添加額外的文件缎讼;
  • 生成XML的DOM對(duì)象收夸,很明顯,這個(gè)DOM肯定也是MBG自己封裝的休涤;
  • 指定一個(gè)文件路徑咱圆,并添加給MBG笛辟;
  • 肯定有一個(gè)方法能夠獲得本次生成的所有mapper.xml文件功氨,保存這些文件,并且輸出到生成的mappers元素中手幢;

OK捷凄,那么我們進(jìn)入代碼:

public class MapperConfigPlugin extends PluginAdapter {

首先,該類仍然繼承PluginAdapter围来;

private List<String> mapperFiles;
public MapperConfigPlugin() {
    mapperFiles = new ArrayList<String>();
}

接著跺涤,該類創(chuàng)建了一個(gè)String的集合,根據(jù)名字很明顯监透,這里面肯定存放的是本次MBG生成的所有mapper.xml文件的路徑桶错;

public boolean validate(List<String> warnings) {
    boolean valid = true;
    //stringHasValue方法是通過靜態(tài)引入工具類org.mybatis.generator.internal.util.StringUtility的;
    //該方法用于判斷傳入的參數(shù)中是否含有targetProject這個(gè)參數(shù)胀蛮;
    //這里要注意兩個(gè)點(diǎn)院刁,第一,我們?cè)跀U(kuò)展或者使用別人的框架的時(shí)候粪狼,比如stringHasValue這種方法退腥,我們完全可以自己寫一個(gè)hasLength方法,
    //但是再榄,使用框架中已經(jīng)存在的API來完成這些功能狡刘,是一個(gè)擴(kuò)展框架的一個(gè)良好的實(shí)踐,這可以保證框架在API級(jí)別的一致性困鸥;
    //第二嗅蔬,properties屬性是Plugin在創(chuàng)建的時(shí)候,通過setProperties方法傳入的,是一個(gè)Properties類型數(shù)據(jù)购城;
    if (!stringHasValue(properties
            .getProperty("targetProject"))) { //$NON-NLS-1$
        //如果沒有傳入必填的參數(shù)吕座,就把警告信息添加到傳入的warnings列表中,該列表的內(nèi)容會(huì)在MBG運(yùn)行過程中統(tǒng)一日志瘪板;
        //這里需要注意的是getString方法吴趴,該方法是通過靜態(tài)引入org.mybatis.generator.internal.util.messages.Messages
        //這個(gè)Messages類是MBG對(duì)國際化消息的一個(gè)封裝,在后面擴(kuò)展時(shí)候會(huì)講到MBG的代碼結(jié)構(gòu)侮攀;
        warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                "MapperConfigPlugin", //$NON-NLS-1$
                "targetProject")); //$NON-NLS-1$
        valid = false;
    }
    //同理锣枝,判斷是否傳入了targetPackage參數(shù)
    if (!stringHasValue(properties
            .getProperty("targetPackage"))) { //$NON-NLS-1$
        warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                "MapperConfigPlugin", //$NON-NLS-1$
                "targetPackage")); //$NON-NLS-1$
        valid = false;
    }
    return valid;
}

因?yàn)檫@個(gè)插件需要傳入?yún)?shù),并且有兩個(gè)必填參數(shù)兰英,所以在validate方法中做了驗(yàn)證撇叁,通過這個(gè)方法,其實(shí)我們基本就能明白validate方法怎么書寫了畦贸;

接著看:

public List<GeneratedXmlFile> contextGenerateAdditionalXmlFiles() {

確實(shí)實(shí)現(xiàn)了contextGenerateAdditionalXmlFiles方法來為MBG添加額外需要生成的文件陨闹;進(jìn)入這個(gè)方法:

   public List<GeneratedXmlFile> contextGenerateAdditionalXmlFiles() {
    //創(chuàng)建一個(gè)XML文檔,注意這個(gè)Document不是JAVA DOM的薄坏,而是org.mybatis.generator.api.dom.xml.Document
    //在這里傳入了兩個(gè)靜態(tài)常量趋厉,這兩個(gè)常量就是mybatis配置文件需要用到的DTD,
    //在XmlConstants里面還有很多常量胶坠,比如MYBATIS3_MAPPER_SYSTEM_ID和MYBATIS3_MAPPER_PUBLIC_ID(看名字應(yīng)該知道是什么內(nèi)容吧~)
    Document document = new Document(
            XmlConstants.MYBATIS3_MAPPER_CONFIG_PUBLIC_ID,
            XmlConstants.MYBATIS3_MAPPER_CONFIG_SYSTEM_ID);
    
    //接著創(chuàng)建根目錄君账,<configuration>,和JavaDOM基本一樣沈善,就不啰嗦了乡数;
    XmlElement root = new XmlElement("configuration"); //$NON-NLS-1$
    document.setRootElement(root);

    //添加注釋,這里做的有點(diǎn)不太規(guī)范闻牡,最好還是使用MBG提供的context.getCommentGenerator的addComment(XmlElement xmlElement)方法來統(tǒng)一生成注釋
    //可能作者想做更多的個(gè)性化的注釋吧净赴;
    root.addElement(new TextElement("<!--")); //$NON-NLS-1$
    root.addElement(new TextElement("  This file is generated by MyBatis Generator.")); //$NON-NLS-1$
    root.addElement(new TextElement("  This file is the shell of a Mapper Config file - in many cases you will need to add")); //$NON-NLS-1$
    root.addElement(new TextElement("    to this file before it is usable by MyBatis.")); //$NON-NLS-1$
    StringBuilder sb = new StringBuilder();
    sb.append("  This file was generated on "); //$NON-NLS-1$
    sb.append(new Date());
    sb.append('.');
    root.addElement(new TextElement(sb.toString()));
    root.addElement(new TextElement("-->")); //$NON-NLS-1$

    //創(chuàng)建mappers節(jié)點(diǎn);
    XmlElement mappers = new XmlElement("mappers"); //$NON-NLS-1$
    root.addElement(mappers);
    
    //準(zhǔn)備根據(jù)搜集到的本次生成的mapper.xml文件罩润,為mappers生成mapper子元素
    XmlElement mapper;
    //為每一個(gè)mapper.xml文件生成一個(gè)對(duì)應(yīng)的mapper子元素玖翅;從這里就可以明確的看出,在mapperFiles集合中保存的確實(shí)是mapper.xml文件的路徑哨啃;
    for (String mapperFile : mapperFiles) {
        mapper = new XmlElement("mapper"); //$NON-NLS-1$
        mapper.addAttribute(new Attribute("resource", mapperFile)); //$NON-NLS-1$
        mappers.addElement(mapper);
    }
    
    //信息量非常大的一句代碼烧栋,通過這句代碼可以看出:
    //1,MBG使用GeneratedXmlFile對(duì)象來包裝一個(gè)要生成的XML文件的所有相關(guān)內(nèi)容拳球;
    //2审姓,該對(duì)象的構(gòu)造方法包含了所有需要的信息
    //3,第一個(gè)參數(shù)祝峻,是該XML文件的內(nèi)容魔吐,即Document扎筒;
    //4,第二個(gè)參數(shù)酬姆,是該XML文件的文件名嗜桌,可以很清楚的看到,先得到fileName參數(shù)辞色,否則使用默認(rèn)的MapperConfig.xml命名(所以骨宠,后綴名是要自己傳給MBG的)
    //5,第三個(gè)參數(shù)和第四個(gè)參數(shù)相满,分別是生成XML文件的targetPackage和targetProject层亿;所以,可以看到MBG把文件的具體生成過程完全包裝立美,只需要我們提供package和project即可匿又;
    //6,第四個(gè)參數(shù)代表是否合并建蹄,
    //7碌更,最后一個(gè)參數(shù)是提供一個(gè)XML文件格式化工具,直接使用上下文的xmlFormatter即可(這個(gè)是可以在<context>元素中配置的哦~~)
    GeneratedXmlFile gxf = new GeneratedXmlFile(document, properties
            .getProperty("fileName", "MapperConfig.xml"), //$NON-NLS-1$ //$NON-NLS-2$
            properties.getProperty("targetPackage"), //$NON-NLS-1$
            properties.getProperty("targetProject"), //$NON-NLS-1$
            false, context.getXmlFormatter());
    
    //最后返回要生成的這個(gè)文件洞慎,交給MBG去生成痛单;
    List<GeneratedXmlFile> answer = new ArrayList<GeneratedXmlFile>(1);
    answer.add(gxf);

    return answer;
}

詳細(xì)的解釋已經(jīng)在注釋中寫明,其實(shí)可以通過這個(gè)代碼看出來拢蛋,MBG對(duì)Plugin的封裝還是非常到位的桦他,其API中各個(gè)類的職責(zé)還是非常明確的蔫巩;這些代碼對(duì)我們要生成我們自己的XML文件谆棱,是有非常大的指導(dǎo)作用的;

最后圆仔,那肯定就是搜集本次生成的mapper.xml文件的代碼了:

 /**
 * sqlMapGenerated方法垃瞧,是在本次context中,生成每一個(gè)(注意是每一個(gè))mapper.xml文件之后都會(huì)回調(diào)的方法坪郭;
 * 第一個(gè)參數(shù)GeneratedXmlFile即本次生成的mapper.xml文件對(duì)應(yīng)的XML文件封裝對(duì)象个从;
 */
@Override
public boolean sqlMapGenerated(GeneratedXmlFile sqlMap,
        IntrospectedTable introspectedTable) {
    StringBuilder sb = new StringBuilder();
    //得到目標(biāo)package;
    sb.append(sqlMap.getTargetPackage());
    //添加一個(gè).然后把所有的.替換成/歪沃,就變成了mapper.xml文件的目錄(原來并沒有方法直接得到嗦锐,還是要自己通過package去替換)
    sb.append('.');
    String temp = sb.toString();
    sb.setLength(0);
    sb.append(temp.replace('.', '/'));
    //接著拼上xml文件的文件名(還記得文件名是包含了后綴的吧),就創(chuàng)建好了這個(gè)mapper.xml文件的路徑了
    sb.append(sqlMap.getFileName());
    //再添加到mapperFiles中
    mapperFiles.add(sb.toString());
    return true;
}

其實(shí)比較重要的也就這個(gè)方法的調(diào)用時(shí)機(jī)沪曙;通過這個(gè)方法的調(diào)用奕污,我們應(yīng)該對(duì)Plugin生命周期中類似modelBaseRecordClassGenerated的方法會(huì)有很深入的理解了。好了液走,準(zhǔn)備工作完成碳默,我們來做一個(gè)非常簡(jiǎn)單的自定義插件贾陷;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嘱根,隨后出現(xiàn)的幾起案子髓废,更是在濱河造成了極大的恐慌,老刑警劉巖该抒,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慌洪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡凑保,警方通過查閱死者的電腦和手機(jī)蒋譬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愉适,“玉大人犯助,你說我怎么就攤上這事∥蹋” “怎么了剂买?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長癌蓖。 經(jīng)常有香客問我瞬哼,道長,這世上最難降的妖魔是什么租副? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任坐慰,我火速辦了婚禮,結(jié)果婚禮上用僧,老公的妹妹穿的比我還像新娘结胀。我一直安慰自己,他們只是感情好责循,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布糟港。 她就那樣靜靜地躺著,像睡著了一般院仿。 火紅的嫁衣襯著肌膚如雪秸抚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天歹垫,我揣著相機(jī)與錄音剥汤,去河邊找鬼。 笑死排惨,一個(gè)胖子當(dāng)著我的面吹牛吭敢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播若贮,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼省有,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼痒留!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蠢沿,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤伸头,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后舷蟀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恤磷,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年野宜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扫步。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡匈子,死狀恐怖河胎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情虎敦,我是刑警寧澤游岳,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布逛漫,位于F島的核電站谅畅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏汇恤。R本人自食惡果不足惜唾那,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一访锻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧闹获,春花似錦期犬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灾馒。三九已至茎用,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間睬罗,已是汗流浹背轨功。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留容达,地道東北人古涧。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像花盐,于是被迫代替她去往敵國和親羡滑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子菇爪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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

  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL柒昏、存儲(chǔ)過程以及高級(jí)映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,454評(píng)論 0 4
  • 1 引言# 本文主要講解JDBC怎么演變到Mybatis的漸變過程凳宙,重點(diǎn)講解了為什么要將JDBC封裝成Mybait...
    七寸知架構(gòu)閱讀 76,442評(píng)論 36 980
  • 11 MyBatis一級(jí)緩存實(shí)現(xiàn)# 11.1 什么是一級(jí)緩存? 為什么使用一級(jí)緩存职祷?## 每當(dāng)我們使用MyBati...
    七寸知架構(gòu)閱讀 10,837評(píng)論 12 143
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,770評(píng)論 25 707
  • 這兩天讀了一本秦朝時(shí)代的書《素書》氏涩,據(jù)傳說是黃石公所作,就是那位教張良的老頭有梆。 芒格曾說過是尖,其實(shí)我能夠做到這些成就...
    秦家炎閱讀 276評(píng)論 1 3