mybatis-generator xml,java合併問題

mybatis-generator 簡介

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 問題

(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_***裏面,這樣再次生成就不會被覆蓋了。那麼問題來了:

這裏寫圖片描述

從圖中能夠看到,這無疑使得咱們的文件更繁多,維護更加複雜。

 

問題解決-xml合併

出現這個問題的緣由,咱們主要來看一下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新的覆蓋舊的,而且保留舊的文件裏本身寫的方法,達到合併的效果。

 

問題解決-java合併

//和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

使用方法:(下載jar包,替換你本地的pom使用的jar包 mybatis-generator-core-version.jar)

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 使用

相關文章
相關標籤/搜索