【原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明原文章地址甥厦,謝謝纺铭!】
在本文中,我們?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)單的自定義插件贾陷;