Mybatis屬於半自動ORM,在使用這個框架中,工做量最大的就是書寫Mapping的映射文件,因爲手動書寫很容易出錯,咱們能夠利用Mybatis-Generator來幫咱們自動生成文件。項目中經常使用的: StudentExample example = new StudentExample(); StudentExample.Criteria criteria = example.createCriteria(); deleteByExample insert insertSelective selectByExample ..等等諸如此類的方法,均是經過mybatis-genenrator能夠自動生成的 使得開發起來很方便。 mybatis-generator 生成的sql xml幾乎涵蓋了單表的全部增刪改查方法。很方便
(mybatis-generator默認合併指的是 123 , 234 合併完 是 123234,而咱們須要的結果是1234)java
1 生成一次 CourseMapper.java,CourseMapper.xml等文件。 2 如今須要加個批量插入方法,在CouseMapper.java里加 public int batchInsert(List<Course> list); 在CourseMapper.xml里加 <insert id="batchInsert" parameterType="cc.gukeer.smartBoard.persistence.entity.Course"> your sql </insert> 3 如今數據庫加了個字段a,須要對文件再次生成 咱們發現,xml裏面,updateByPrimaryKey 這樣的方法再次生成了一次,並且CourseMapper.java裏 的batchInsert 這個方法已經沒有了。以前寫的代碼也就被覆蓋了或者出現了問題。咱們必須得回滾。。 4 爲了解決這個問題,咱們不得不擴展 CourseMapper.xml 寫 A_CourseMapper.xml 對應 CourseMapper.xml 寫 A_CourseMapper.xml 對應 CourseMapper.xml 並把咱們的批量方法寫到A_***裏面,這樣再次生成就不會被覆蓋了。那麼問題來了:
從圖中能夠看到,這無疑使得咱們的文件更繁多,維護更加複雜。
出現這個問題的緣由,咱們主要來看一下generator的源碼: org.mybatis.generator.api.MyBatisGenerator 的generate方法,是生成文件的一個入口。 for (GeneratedXmlFile gxf : this.generatedXmlFiles){ this.projects.add(gxf.getTargetProject()); File targetFile; String source; try{ File directory = this.shellCallback.getDirectory(gxf.getTargetProject(), gxf.getTargetPackage()); targetFile = new File(directory, gxf.getFileName()); if (targetFile.exists()){ String source; if (gxf.isMergeable()){ source = XmlFileMergerJaxp.getMergedSource(gxf, targetFile); }else if (this.shellCallback.isOverwriteEnabled()){ String source = gxf.getFormattedContent(); this.warnings.add(Messages.getString("Warning.11", targetFile.getAbsolutePath())); }else{ String source = gxf.getFormattedContent(); targetFile = getUniqueFileName(directory, gxf.getFileName()); this.warnings.add(Messages.getString("Warning.2", targetFile.getAbsolutePath())); } }
其中 有對gxf(GeneratedXmlFile) 是否合併判斷,若是合併true,將新舊xml經過這個方法合併取合併獲得的結果字符串,寫到新的文件中。 XmlFileMergerJaxp.getMergedSourcenode
XmlFileMergerJaxp.getMergedSource 部分代碼以下:git
DocumentType newDocType = newDocument.getDoctype(); ... List<Node> nodesToDelete = new ArrayList(); 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); } } for (Node node : nodesToDelete) { existingRootElement.removeChild(node); }
這裏採用的是org.w3c.dom.* 來解析的新舊xml文件。咱們發現,這裏有對舊文件遍歷,判斷這個節點方法是否已經生成過。若是是已經生成 isGeneratedNode的節點,那麼加入到 nodesToDelete裏面,最後把它給移除。那麼問題就出在這個 isGeneratedNode判斷方法上github
private static boolean isGeneratedNode(Node node) { boolean rc = false; if ((node != null) && (node.getNodeType() == 1)) { Element element = (Element)node; String id = element.getAttribute("id"); if (id != null) { for (String prefix : MergeConstants.OLD_XML_ELEMENT_PREFIXES) { if (id.startsWith(prefix)) { rc = true; break; } } } if (!rc) { NodeList children = node.getChildNodes(); int length = children.getLength(); for (int i = 0; i < length; i++) { Node childNode = children.item(i); if (!isWhiteSpace(childNode)) { if (childNode.getNodeType() != 8) { break; } Comment comment = (Comment)childNode; String commentData = comment.getData(); for (String tag : MergeConstants.OLD_ELEMENT_TAGS) { if (commentData.contains(tag)) { rc = true; break; } } } } } } return rc; }
其實咱們的需求是,以前有個方法,又生成了
,咱們固然想用新的覆蓋舊的,可是這裏的判斷顯然不是根據這個id來判斷的,因此這個方法返回false,這個方法又會追加生成一次。sql
說到這裏,咱們只須要這樣稍微修改一下便可:將新生成的節點列表保存爲一個集合a。判斷舊節點的id是否包含在這個集合a裏面,若是在,那麼舊的節點就刪除。shell
具體代碼以下:數據庫
//獲取新生成的全部xml,全部element的id列表,刪除以前同名的結點 NodeList newMethods = newRootElement.getChildNodes(); List<String> methods = new ArrayList<String>(); for (int i = 0; i < newMethods.getLength(); i++) { Node node = newMethods.item(i); try { if (node instanceof DeferredTextImpl) { continue; } Element ele = (Element) node; methods.add(ele.getAttribute("id")); } catch (Exception e) { //#text節點轉換會異常 continue; } if (i == newMethods.getLength() - 1) { if (isWhiteSpace(node)) { break; } } } private static boolean isGeneratedNode(Node node, List<String> methods) { boolean rc = false; if (node != null && node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element) node; String id = element.getAttribute("id"); //$NON-NLS-1$ if (methods.contains(id)) { return true; } if (id != null) { for (String prefix : MergeConstants.OLD_XML_ELEMENT_PREFIXES) { if (id.startsWith(prefix)) { rc = true; break; } } } 這樣咱們就能實現 xml新的覆蓋舊的,而且保留舊的文件裏本身寫的方法,達到合併的效果。
//和xml相似,這個是java代碼合併判斷代碼 for (GeneratedJavaFile gjf : this.generatedJavaFiles) { this.projects.add(gjf.getTargetProject()); try { File directory = this.shellCallback.getDirectory(gjf.getTargetProject(), gjf.getTargetPackage()); File targetFile = new File(directory, gjf.getFileName()); String source; if (targetFile.exists()) { String source; if (this.shellCallback.isMergeSupported()){ source = this.shellCallback.mergeJavaFile(gjf.getFormattedContent(), targetFile.getAbsolutePath(), MergeConstants.OLD_ELEMENT_TAGS, gjf.getFileEncoding()); }else if (this.shellCallback.isOverwriteEnabled()){ String source = gjf.getFormattedContent(); this.warnings.add(Messages.getString("Warning.11", targetFile.getAbsolutePath())); } else{ String source = gjf.getFormattedContent(); targetFile = getUniqueFileName(directory, gjf.getFileName()); this.warnings.add(Messages.getString("Warning.2", targetFile.getAbsolutePath())); } }
咱們來看看java代碼合併的實現:this.shellCallback.mergeJavaFileapi
public String mergeJavaFile(String newFileSource, String existingFileFullPath, String[] javadocTags, String fileEncoding) throws ShellException { throw new UnsupportedOperationException(); }
很遺憾,它並不支持java合併。咱們須要本身實現java合併。此處參考:http://blog.csdn.net/w980994974/article/details/76904587
直接上代碼:mybatis
package org.mybatis.generator.internal; import com.github.javaparser.JavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.ImportDeclaration; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.FieldDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.TypeDeclaration; import org.mybatis.generator.config.MergeConstants; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.mybatis.generator.api.dom.OutputUtilities.newLine; public class JavaFileMergerJaxp { public String getNewJavaFile(String newFileSource, String existingFileFullPath) throws FileNotFoundException { CompilationUnit newCompilationUnit = JavaParser.parse(newFileSource); CompilationUnit existingCompilationUnit = JavaParser.parse(new File(existingFileFullPath)); return mergerFile(newCompilationUnit, existingCompilationUnit); } public String mergerFile(CompilationUnit newCompilationUnit, CompilationUnit existingCompilationUnit) { System.out.println("合併java代碼"); StringBuilder sb = new StringBuilder(newCompilationUnit.getPackageDeclaration().get().toString()); newCompilationUnit.removePackageDeclaration(); //合併imports NodeList<ImportDeclaration> imports = newCompilationUnit.getImports(); imports.addAll(existingCompilationUnit.getImports()); Set importSet = new HashSet<ImportDeclaration>(); importSet.addAll(imports); NodeList<ImportDeclaration> newImports = new NodeList<ImportDeclaration>(); newImports.addAll(importSet); newCompilationUnit.setImports(newImports); for (ImportDeclaration i : newCompilationUnit.getImports()) { sb.append(i.toString()); } newLine(sb); NodeList<TypeDeclaration<?>> types = newCompilationUnit.getTypes(); NodeList<TypeDeclaration<?>> oldTypes = existingCompilationUnit.getTypes(); for (int i = 0; i < types.size(); i++) { //截取Class String classNameInfo = types.get(i).toString().substring(0, types.get(i).toString().indexOf("{") + 1); sb.append(classNameInfo); newLine(sb); newLine(sb); //合併fields List<FieldDeclaration> fields = types.get(i).getFields(); List<FieldDeclaration> oldFields = oldTypes.get(i).getFields(); List<FieldDeclaration> newFields = new ArrayList<FieldDeclaration>(); HashSet<FieldDeclaration> fieldDeclarations = new HashSet<FieldDeclaration>(); fieldDeclarations.addAll(fields); fieldDeclarations.addAll(oldFields); newFields.addAll(fieldDeclarations); for (FieldDeclaration f : newFields) { sb.append("\t" + f.toString()); newLine(sb); newLine(sb); } //合併methods List<MethodDeclaration> methods = types.get(i).getMethods(); List<MethodDeclaration> existingMethods = oldTypes.get(i).getMethods(); for (MethodDeclaration f : methods) { String res = f.toString().replaceAll("\r\n", "\r\n\t"); sb.append("\t" + res); newLine(sb); newLine(sb); } List<String> methodList = new ArrayList<String>(); for (MethodDeclaration m : methods) { methodList.add(m.getName().toString()); } methodList.add("toString"); methodList.add("hashCode"); methodList.add("equals"); for (MethodDeclaration m : existingMethods) { if (methodList.contains(m.getName().toString())) { continue; } boolean flag = true; for (String tag : MergeConstants.OLD_ELEMENT_TAGS) { if (m.toString().contains(tag)) { flag = false; break; } } if (flag) { String res = m.toString().replaceAll("\r\n", "\r\n\t"); sb.append("\t" + res); newLine(sb); newLine(sb); } } //判斷是否有內部類 types.get(i).getChildNodes(); for (Node n : types.get(i).getChildNodes()) { if (n.toString().contains("static class")) { String res = n.toString().replaceAll("\r\n", "\r\n\t"); sb.append("\t" + res); } } } return sb.append(System.getProperty("line.separator") + "}").toString(); } }
參考 上面網址獲得的代碼稍微加了一些新舊方法的判斷,邏輯和xml合併相似。再也不贅述。app
ps:其中java,xml是覆蓋模式,仍是合併模式,經過配置實現,
<context id="MySQLTables" targetRuntime="MyBatis3"> <!--true爲合併,false爲覆蓋,新生成的文件會徹底覆蓋舊文件--> <property name="xmlMergeable" value="true" /> <property name="javaMergeable" value="true" />
# 總結
源碼修改好了以後,jar包下載地址:
http://download.csdn.net/download/zjy1211079133/10159514
pom.xml配置:
<plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <configurationFile>src/main/resources/mybatis-generator.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin>
mybatis-generator.xml 配置:(其餘公用配置自行百度)
<context id="MySQLTables" targetRuntime="MyBatis3"> <!--true爲合併,false爲覆蓋,新生成的文件會徹底覆蓋舊文件--> <property name="xmlMergeable" value="true" /> <property name="javaMergeable" value="true" />
生成便可。具體參照:http://blog.csdn.net/isea533/article/details/42102297 使用