一.问题
用的mybatis.generator 1.3.6版本,版本没啥大关系.
目前的xml输出有两个主要的问题:
1.xml追加而不是覆盖
这样就导致一个xml mapper文件每个node都重复,肯定不行.
2.即使可以覆盖,如果需要对mapper增删改呢?
假如你数据库没配置外键,第一遍生成mapper肯定要手动加关联.
关联加好了,如果后期数据库加了几个字段,或者忘了给insert用
<generatedKey column="id" sqlStatement="MySql"/>
配置数据库自动生成主键.
数据库有二三十张表,挨个改不仅效率慢,而且容易出错或者漏掉.
但是因为改了mapper,重新生成就会覆盖(前面的1完成的),即使不覆盖追加,也要挨个删挨个合并.
二.需求
增
增加一个<sql>,但是合并的时候要求保留
删
删除自动生成的某些<sql>,其实这个没啥意义,如果非要删除在配置文件中
参考:http://blog.csdn.net/testcs_dn/article/details/77881776
改
因为数据库没有外键
配置关联关系,几乎全部的<sql>都要改.
但是重新生成时不要覆盖掉.
三.解决方案
package com.haitian.plugins; import org.mybatis.generator.api.GeneratedXmlFile; import org.mybatis.generator.api.IntrospectedTable; import org.mybatis.generator.api.PluginAdapter; import org.mybatis.generator.api.ShellCallback; import org.mybatis.generator.api.dom.xml.Element; import org.mybatis.generator.internal.DefaultShellCallback; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; import java.io.FileInputStream; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * User:zhangweixiao * Description: */ public class NoDoubleNodes extends PluginAdapter { @Override public boolean validate(List<String> warnings) { return true; } //mybatis_generator中类,用来根据TargetProject和TargetPackage生成File,自己写也不难. ShellCallback shellCallback=new DefaultShellCallback(false); //因为sqlMapDocumentGenerated先调用,要保存Document,用来在sqlMapGenerated中删除子node org.mybatis.generator.api.dom.xml.Document document; @Override public boolean sqlMapDocumentGenerated(org.mybatis.generator.api.dom.xml.Document document, IntrospectedTable introspectedTable) { this.document=document; return true; } @Override public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) { try { //将旧的xml mapper读取到Document中(注意这个Document和上面那个Document不是同一类型,上面那个是mybatis中的) // org.mybatis.generator.api.dom.xml.Document File directory = shellCallback.getDirectory(sqlMap.getTargetProject(), sqlMap.getTargetPackage()); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setValidating(false); DocumentBuilder db = dbf.newDocumentBuilder(); File xmlFile=new File(directory, sqlMap.getFileName()); if(directory.exists()==false||xmlFile.exists()==false) return true; Document doc = db.parse(new FileInputStream(xmlFile)); org.w3c.dom.Element rootElement = doc.getDocumentElement(); NodeList list = rootElement.getChildNodes(); List<Element> elements = document.getRootElement().getElements(); //从node表示的text中读取name和id属性的值,例如<sql id="insert">...</sql> // 可读取到sql和insert Pattern p=Pattern.compile("<(\w+)\s+id="(\w+)""); boolean findSameNode=false; // 遍历新的node,因为做删除操作,这里要用iterator,, for (Iterator<Element> elementIt = elements.iterator(); elementIt.hasNext();) { findSameNode=false; String newNodeName=""; String NewIdValue=""; Element element=elementIt.next(); Matcher m=p.matcher(element.getFormattedContent(0)); if(m.find()) { //获取新的node的name和id属性的值 newNodeName=m.group(1); NewIdValue=m.group(2); } //遍历旧的node,如果name和id属性的值相同,那么定义为重复,不进行覆盖,从elements中删除 for(int i=0;i<list.getLength();i++) { Node node = list.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { if(newNodeName.equals(node.getNodeName())) { NamedNodeMap attr = node.getAttributes(); for (int j = 0; j < attr.getLength(); j++) { Node attrNode = attr.item(j); if (attrNode.getNodeName().equals("id") &&attrNode.getNodeValue().equals(NewIdValue)) { //name和ndoe属性值相等,删除并break出去,elementIt->document->新的mapper.xml elementIt.remove(); findSameNode=true; break; } } if(findSameNode==true) break; } } } } } catch (Exception e) { e.printStackTrace(); } return true; } }
主要对比新生成的node和原先存在的node,
只比较mapper下的第一层node,正常是15个.
如果nodeName和id属性的值相同,定义为重复,不覆盖.
如果修改mapper文件修改乱了,把修改乱的哪个node备份,删除掉,重新生成就好了.
举例来说,我修改BaseResultMap改为BaseResultMap1,这样就等于删除了BaseResultMap并且新加了一个BaseResultMap1
然后修改了第三处,加了一个and(测试用的)
重新生成,会重新添加名为BaseResultMap的sql,但是BaseResultMap1和修改的都不会动
(只有缺少的会重新生成)
四.排除的方案
https://my.oschina.net/u/140938/blog/220006
https://my.oschina.net/u/137785/blog/736372
https://stackoverflow.com/questions/43245041/how-could-let-mybatis-generator-overwriting-the-already-generated-mapper-xml
实测:
第二个链接的使用反射修改isMeragale,的确可以解决追加问题,让新生成的覆盖掉,但也将自己新加和修改的给覆盖掉了.
第三个链接直接将旧文件给删除,解决追加问题.
五.使用方法
1.新建一个模块
不能用当前的模块,
如果用当前模块,
当前模块需要maven mybatis generate,而maven mybatis generate需要依赖这个插件类的包才能找到这个类.
所以mybatis需要依赖当前模块的包,但是当前模块的包打包的时候会报错,死循环了.
A在pom中依赖B,配置POM B依赖A,但是maven install A的时候提示B找不到A.
详细说太麻烦了,自己多试试就清楚了.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.haitian</groupId> <artifactId>mybatis_plugin</artifactId> <version>0.0.1</version> <packaging>jar</packaging> <name>mybatis_plugin</name> <description>Demo project for Spring Boot</description> <parent> <groupId>com.haitian</groupId> <artifactId>parent</artifactId> <version>0.0.1</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <mybatis-generator.version>1.3.6</mybatis-generator.version> <mysql.version>5.1.13</mysql.version> <mybatis.version>3.2.4</mybatis.version> </properties> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>${mybatis-generator.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> </dependencies> </project>
注意不要加plugin
2.添加插件类并install到本地版本库
3.在这里加插件配置
<context id="MBG" targetRuntime="MyBatis3" defaultModelType="conditional"> <!-- 这个插件给由MBG生成的Java模型对象增加了equals和hashCode方法 --> <!--<plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin" />--> <plugin type="com.haitian.plugins.NoDoubleNodes" /> <!--<plugin type="com.haitian.plugins.ForceIsMergeablePlugin" />--> <commentGenerator>
4.更新mybatis_generator模块的pom
更新dependency为刚才maven install的jar包
<plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>${mybatis-generator.version}</version> <configuration> <!-- 指定文件位置好像不起作用,始终默认读取src/main/resources/generatorConfig.xml文件 --> <configurationFile>src/main/resources/generatorConfig.xml</configurationFile> <verbose>true</verbose> <!--允许覆盖生成的文件--> <overwrite>true</overwrite> </configuration> <!-- 数据库驱动 --> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>com.haitian</groupId> <artifactId>mybatis_plugin</artifactId> <version>0.0.1</version> </dependency> </dependencies> <!-- 自动生成 --> <executions> <execution> <id>Generate MyBatis Artifacts</id> <goals> <goal>generate</goal> </goals> </execution> </executions> </plugin>
结构如下图所示
mybatis_plugin封装插件类并打包,
mybatis-generator-maven-plugin依赖这个包
mybatis_generator调用mybatis-generator-maven-plugin进行generate操作.
开发调试的时候最好把maven generate操作之前放上mybatis_plugin的maven打包操作
这样可以即使更新jar资源.
好像不能直接使用模块,只能找jar
六.全部源码和Demo
https://github.com/zwxbest/mybatis-generator-plugin