0%

mybatis generator 插件生成xml重复代码问题

我们知道,通过使用Mybatis Generator,我们可以快速生成数据库已有表的增删改查方法,极大的提高开发效率。

然而我们可能会遇到一个问题,我们继承AbstractXmlElementGenerator方法所实现的自定义mapper方法,每次运行都会重复添加,固然我们可以手动删除,但工具不就是让人们得以偷懒吗,而且作为一个强迫症患者兼程序员,这是不可容忍的。

遇事第一步当然是使用搜索引擎了,也查到通过继承类 PluginAdapter ,重写 sqlMapGenerated 方法,改 GeneratedXmlFile 中成员变量 isMergeable 的值为 false 的解决方法。但经过尝试后发现,使用此种方式,自己新定义的方法就全没了,这可不行,还不如不改呢。

没办法,看来得研究新的解决办法。通过 IDEA 的快捷键 Ctrl + B, 可以发现在类 org.mybatis.generator.api.MyBatisGenerator 的 writeGeneratedXmlFile 方法中调用了 GeneratedXmlFile 的 isMergeable 方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private void writeGeneratedXmlFile(GeneratedXmlFile gxf, ProgressCallback callback)
throws InterruptedException, IOException {
File targetFile;
String source;
try {
File directory = shellCallback.getDirectory(gxf
.getTargetProject(), gxf.getTargetPackage());
targetFile = new File(directory, gxf.getFileName());
if (targetFile.exists()) {
if (gxf.isMergeable()) {
source = XmlFileMergerJaxp.getMergedSource(gxf,
targetFile);
} else if (shellCallback.isOverwriteEnabled()) {
source = gxf.getFormattedContent();
warnings.add(getString("Warning.11", //$NON-NLS-1$
targetFile.getAbsolutePath()));
} else {
source = gxf.getFormattedContent();
targetFile = getUniqueFileName(directory, gxf
.getFileName());
warnings.add(getString(
"Warning.2", targetFile.getAbsolutePath())); //$NON-NLS-1$
}
} else {
source = gxf.getFormattedContent();
}

callback.checkCancel();
callback.startTask(getString(
"Progress.15", targetFile.getName())); //$NON-NLS-1$
writeFile(targetFile, source, "UTF-8"); //$NON-NLS-1$
} catch (ShellException e) {
warnings.add(e.getMessage());
}
}

其中对 gxf(GeneratedXmlFile) 进行是否合并判断, 如果返回值为 true,那么将调用 XmlFileMergerJaxp.getMergedSource(gxf,targetFile) 方法,而默认值即为 true。

固需要了解 getMergedSource 方法做了什么,查看代码发现这里调用了重载方法,而在重载方法中我们可以找到如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// remove the old generated elements and any
// white space before the old nodes
List<Node> nodesToDelete = new ArrayList<Node>();
NodeList children = existingRootElement.getChildNodes();
int length = children.getLength();
for (int i = 0; i < length; i++) {
Node node = children.item(i);
if (isGeneratedNode(node)) {
nodesToDelete.add(node);
} else if (isWhiteSpace(node)
&& isGeneratedNode(children.item(i + 1))) {
nodesToDelete.add(node);
}
}

通过注解知道,Mybatis Generator 会删除它自身生成的 mapper 方法,这也是为什么只有我们自定义的 mapper 方法才会有重复的情况。

再看看代码,发现这里通过 isGeneratedNode 方法判断是否需要删除 mapper 方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
private static boolean isGeneratedNode(Node node) {
boolean rc = false;

if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
String id = element.getAttribute("id"); //$NON-NLS-1$
if (id != null) {
for (String prefix : MergeConstants.OLD_XML_ELEMENT_PREFIXES) {
if (id.startsWith(prefix)) {
rc = true;
break;
}
}
}

if (rc == false) {
// check for new node format - if the first non-whitespace node
// is an XML comment, and the comment includes
// one of the old element tags,
// then it is a generated node
NodeList children = node.getChildNodes();
int length = children.getLength();
for (int i = 0; i < length; i++) {
Node childNode = children.item(i);
if (isWhiteSpace(childNode)) {
continue;
} else if (childNode.getNodeType() == Node.COMMENT_NODE) {
Comment comment = (Comment) childNode;
String commentData = comment.getData();
for (String tag : MergeConstants.OLD_ELEMENT_TAGS) {
if (commentData.contains(tag)) {
rc = true;
break;
}
}
} else {
break;
}
}
}
}

return rc;
}

在 rc==false 的情况下,有如下关键代码:

1
2
3
4
5
6
for (String tag : MergeConstants.OLD_ELEMENT_TAGS) {
if (commentData.contains(tag)) {
rc = true;
break;
}
}

联系前文可以知道,只要 mapper 中包含 MergeConstants.OLD_ELEMENT_TAGS 的内容,合并时就会被删除,这不正是我们需要的吗,再看看 MergeConstants.OLD_ELEMENT_TAGS 又是什么

1
2
3
4
5
6
public static final String NEW_ELEMENT_TAG = "@mbg.generated"; //$NON-NLS-1$
public static final String[] OLD_ELEMENT_TAGS = {
"@ibatorgenerated", //$NON-NLS-1$
"@abatorgenerated", //$NON-NLS-1$
"@mbggenerated", //$NON-NLS-1$
"@mbg.generated" }; //$NON-NLS-1$

这只是一些字符串,这还不容易吗,让我们试验一下,我在自定义的 AbstractXmlElementGenerator 实现类添加了以下代码

1
2
newXmlElement.addElement(new TextElement("<!-- ELEMENT FOR GENERATOR MERGE - " 
+ MergeConstants.NEW_ELEMENT_TAG + " -->"));

其中 newXmlElement 为 XmlElement 的实例,因为 NEW_ELEMENT_TAG 也包含在 OLD_ELEMENT_TAGS中,所以这里直接使用 NEW_ELEMENT_TAG ,最后通过 parentElement.addElement(newXmlElement) 添加到父元素中。

运行发现,果然成功了,旧的被删除了,新的进来,后写的 mapper 方法还在,至此大功告成。

再次发现,遇到问题查源码是多么重要。

By the way,这里用的 mybatis-generator-core 的版本是 1.3.5。