mybatis-generator扩展

在新公司的新项目想用mybatis-generator来生成DAO层,有同事提出一些改进意见,遂获得源码后进行小幅改造。

目标:

  • 中文注释,精简注释
  • Model类使用lombok简化
  • 增加selectOneByExample方法(较多的使用唯一索引查询场景)

首先获得源码

在任一项目中引入jar包,使用maven download sources获取源码,最新版本1.3.7

<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.7</version>
</dependency>

构建项目

新建一个maven项目,设置groupId和artifactId

<groupId>com.mine.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.0-SNAPSHOT</version>

把解压后的源码复制到目录

在pom.xml中添加项目依赖


<dependencies>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <scope>provided</scope>
            <version>1.2.17</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <scope>provided</scope>
            <version>1.7.25</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <scope>provided</scope>
            <version>2.11.0</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <scope>provided</scope>
            <version>1.2</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <scope>provided</scope>
            <version>1.10.4</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
            <version>5.2.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <version>2.4.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.github.javaparser</groupId>
            <version>3.6.12</version>
            <artifactId>javaparser-core</artifactId>
        </dependency>
</dependencies>

View Code

功能拓展

model中文注释、精简其他注释

修改生成注释默认用的DefaultCommentGenerator


package org.mybatis.generator.internal;

import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.internal.util.StringUtility;

import java.text.SimpleDateFormat;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Properties;
import java.util.Set;

/**
 * @author Jeff Butler
 */
public class DefaultCommentGenerator implements CommentGenerator {
    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public DefaultCommentGenerator() {
        super();
    }

    @Override
    public void addJavaFileComment(CompilationUnit compilationUnit) {
    }

    @Override
    public void addComment(XmlElement xmlElement) {
    }

    @Override
    public void addRootComment(XmlElement rootElement) {
    }

    @Override
    public void addConfigurationProperties(Properties properties) {
    }

    protected String getDateString() {
        return dateFormat.format(new Date());
    }

    @Override
    public void addClassComment(InnerClass innerClass,
                                IntrospectedTable introspectedTable) {
    }

    @Override
    public void addClassComment(InnerClass innerClass,
                                IntrospectedTable introspectedTable, boolean markAsDoNotDelete) {
    }

    @Override
    public void addModelClassComment(TopLevelClass topLevelClass,
                                     IntrospectedTable introspectedTable) {
        topLevelClass.addJavaDocLine("/**");
        topLevelClass.addJavaDocLine(" * 生成日期:" + getDateString());
        topLevelClass.addJavaDocLine(" * 表名: " + introspectedTable.getFullyQualifiedTable().toString());
        topLevelClass.addJavaDocLine(" */");
    }

    @Override
    public void addEnumComment(InnerEnum innerEnum,
                               IntrospectedTable introspectedTable) {
    }

    @Override
    public void addFieldComment(Field field,
                                IntrospectedTable introspectedTable,
                                IntrospectedColumn introspectedColumn) {
        field.addJavaDocLine("/**");
        field.addJavaDocLine(" * " + introspectedColumn.getRemarks());
        field.addJavaDocLine(" */");
    }

    @Override
    public void addFieldComment(Field field, IntrospectedTable introspectedTable) {
    }

    @Override
    public void addGeneralMethodComment(Method method,
                                        IntrospectedTable introspectedTable) {
    }

    @Override
    public void addGetterComment(Method method,
                                 IntrospectedTable introspectedTable,
                                 IntrospectedColumn introspectedColumn) {
    }

    @Override
    public void addSetterComment(Method method,
                                 IntrospectedTable introspectedTable,
                                 IntrospectedColumn introspectedColumn) {
    }

    @Override
    public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable,
                                           Set<FullyQualifiedJavaType> imports) {
        imports.add(new FullyQualifiedJavaType("javax.annotation.Generated"));
        String comment = "Source Table: " + introspectedTable.getFullyQualifiedTable().toString();
        method.addAnnotation(getGeneratedAnnotation(comment));
    }

    @Override
    public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable,
                                           IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {
        imports.add(new FullyQualifiedJavaType("javax.annotation.Generated"));
        String comment = "Source field: "
                + introspectedTable.getFullyQualifiedTable().toString()
                + "."
                + introspectedColumn.getActualColumnName();
        method.addAnnotation(getGeneratedAnnotation(comment));
    }

    @Override
    public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable,
                                   Set<FullyQualifiedJavaType> imports) {
        imports.add(new FullyQualifiedJavaType("javax.annotation.Generated"));
        String comment = "Source Table: " + introspectedTable.getFullyQualifiedTable().toString();
        field.addAnnotation(getGeneratedAnnotation(comment));
    }

    @Override
    public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable,
                                   IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {
        imports.add(new FullyQualifiedJavaType("javax.annotation.Generated"));
        String comment = "Source field: "
                + introspectedTable.getFullyQualifiedTable().toString()
                + "."
                + introspectedColumn.getActualColumnName();
        field.addAnnotation(getGeneratedAnnotation(comment));

        String remarks = introspectedColumn.getRemarks();
        if (StringUtility.stringHasValue(remarks)) {
            field.addJavaDocLine("/**");
            field.addJavaDocLine(" * Database Column Remarks:");
            String[] remarkLines = remarks.split(System.getProperty("line.separator"));
            for (String remarkLine : remarkLines) {
                field.addJavaDocLine(" *   " + remarkLine);
            }
            field.addJavaDocLine(" */");
        }
    }

    @Override
    public void addClassAnnotation(InnerClass innerClass, IntrospectedTable introspectedTable,
                                   Set<FullyQualifiedJavaType> imports) {
        imports.add(new FullyQualifiedJavaType("javax.annotation.Generated"));
        String comment = "Source Table: " + introspectedTable.getFullyQualifiedTable().toString();
        innerClass.addAnnotation(getGeneratedAnnotation(comment));
    }

    private String getGeneratedAnnotation(String comment) {
        StringBuilder buffer = new StringBuilder();
        buffer.append("@Generated(");
        buffer.append("value=\"");

        buffer.append(MyBatisGenerator.class.getName());
        buffer.append('\"');

        buffer.append(", date=\"");
        buffer.append(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now()));
        buffer.append('\"');

        buffer.append(", comments=\"");
        buffer.append(comment);
        buffer.append('\"');

        buffer.append(')');
        return buffer.toString();
    }
}

View Code

Model类使用lombok简化

修改BaseRecordGenerator的getCompilationUnits的方法即可

  public List<CompilationUnit> getCompilationUnits() {
        FullyQualifiedTable table = introspectedTable.getFullyQualifiedTable();
        progressCallback.startTask(getString(
                "Progress.8", table.toString())); //$NON-NLS-1$
        Plugin plugins = context.getPlugins();
        CommentGenerator commentGenerator = context.getCommentGenerator();

        FullyQualifiedJavaType type = new FullyQualifiedJavaType(
                introspectedTable.getBaseRecordType());
        TopLevelClass topLevelClass = new TopLevelClass(type);
        topLevelClass.addImportedType("lombok.Getter");// 导入lombok类
        topLevelClass.addImportedType("lombok.Setter");

        topLevelClass.setVisibility(JavaVisibility.PUBLIC);
        commentGenerator.addJavaFileComment(topLevelClass);

        FullyQualifiedJavaType superClass = getSuperClass();
        if (superClass != null) {
            topLevelClass.setSuperClass(superClass);
            topLevelClass.addImportedType(superClass);
        }
        commentGenerator.addModelClassComment(topLevelClass, introspectedTable);
        topLevelClass.addAnnotation("@Getter");// 增加lombok注解
        topLevelClass.addAnnotation("@Setter");
        List<IntrospectedColumn> introspectedColumns = getColumnsInThisClass();

        if (introspectedTable.isConstructorBased()) {
            addParameterizedConstructor(topLevelClass, introspectedTable.getNonBLOBColumns());

            if (includeBLOBColumns()) {
                addParameterizedConstructor(topLevelClass, introspectedTable.getAllColumns());
            }

            if (!introspectedTable.isImmutable()) {
                addDefaultConstructor(topLevelClass);
            }
        }

        String rootClass = getRootClass();
        for (IntrospectedColumn introspectedColumn : introspectedColumns) {
            if (RootClassInfo.getInstance(rootClass, warnings)
                    .containsProperty(introspectedColumn)) {
                continue;
            }

            Field field = getJavaBeansField(introspectedColumn, context, introspectedTable);
            if (plugins.modelFieldGenerated(field, topLevelClass,
                    introspectedColumn, introspectedTable,
                    Plugin.ModelClassType.BASE_RECORD)) {
                topLevelClass.addField(field);
                topLevelClass.addImportedType(field.getType());
            }

        }

        List<CompilationUnit> answer = new ArrayList<>();
        if (context.getPlugins().modelBaseRecordClassGenerated(
                topLevelClass, introspectedTable)) {
            answer.add(topLevelClass);
        }
        return answer;
  }

增加selectOneByExample

首先查看类似的方法selectByExample是怎么生成的,然后参考增加这个方法。

在IntrospectedTable中增加方法

  public String getSelectOneByExampleStatementId() {
        return "selectOneByExample";//可以跟其他源码一样设置成常量,这里简单处理
  }

增加一个SelectOneByExampleMethodGenerator


/**
 *    Copyright 2006-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.mybatis.generator.codegen.mybatis3.javamapper.elements;

import org.mybatis.generator.api.dom.java.*;

import java.util.Set;
import java.util.TreeSet;

/**
 *
 * @author Jeff Butler
 *
 */
public class SelectOneByExampleMethodGenerator extends
        AbstractJavaMapperMethodGenerator {

    public SelectOneByExampleMethodGenerator() {
        super();
    }

    @Override
    public void addInterfaceElements(Interface interfaze) {
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        FullyQualifiedJavaType type = new FullyQualifiedJavaType(
                introspectedTable.getExampleType());
        importedTypes.add(type);
        importedTypes.add(FullyQualifiedJavaType.getNewListInstance());

        Method method = new Method();
        method.setVisibility(JavaVisibility.PUBLIC);

        FullyQualifiedJavaType returnType = introspectedTable.getRules()
                .calculateAllFieldsClass();
        method.setReturnType(returnType);

        method.setName(introspectedTable.getSelectOneByExampleStatementId());
        method.addParameter(new Parameter(type, "example")); //$NON-NLS-1$

        context.getCommentGenerator().addGeneralMethodComment(method,
                introspectedTable);

        addMapperAnnotations(interfaze, method);

        if (context.getPlugins()
                .clientSelectByExampleWithBLOBsMethodGenerated(method, interfaze,
                        introspectedTable)) {
            addExtraImports(interfaze);
            interfaze.addImportedTypes(importedTypes);
            interfaze.addMethod(method);
        }
    }

    public void addMapperAnnotations(Interface interfaze, Method method) {
    }

    public void addExtraImports(Interface interfaze) {
    }
}

View Code

增加一个SelectOneByExampleElementGenerator


/**
 *    Copyright 2006-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.mybatis.generator.codegen.mybatis3.xmlmapper.elements;

import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

/**
 *
 * @author Jeff Butler
 *
 */
public class SelectOneByExampleElementGenerator extends
        AbstractXmlElementGenerator {

    public SelectOneByExampleElementGenerator() {
        super();
    }

    @Override
    public void addElements(XmlElement parentElement) {
        String fqjt = introspectedTable.getExampleType();

        XmlElement answer = new XmlElement("select"); //$NON-NLS-1$

        answer.addAttribute(new Attribute("id", //$NON-NLS-1$
                introspectedTable.getSelectOneByExampleStatementId()));
        answer.addAttribute(new Attribute(
                "resultMap", introspectedTable.getBaseResultMapId())); //$NON-NLS-1$
        answer.addAttribute(new Attribute("parameterType", fqjt)); //$NON-NLS-1$

        context.getCommentGenerator().addComment(answer);

        answer.addElement(new TextElement("select")); //$NON-NLS-1$
        XmlElement ifElement = new XmlElement("if"); //$NON-NLS-1$
        ifElement.addAttribute(new Attribute("test", "distinct")); //$NON-NLS-1$ //$NON-NLS-2$
        ifElement.addElement(new TextElement("distinct")); //$NON-NLS-1$
        answer.addElement(ifElement);

        StringBuilder sb = new StringBuilder();
        if (stringHasValue(introspectedTable
                .getSelectByExampleQueryId())) {
            sb.append('\'');
            sb.append(introspectedTable.getSelectByExampleQueryId());
            sb.append("' as QUERYID,"); //$NON-NLS-1$
            answer.addElement(new TextElement(sb.toString()));
        }
        answer.addElement(getBaseColumnListElement());

        sb.setLength(0);
        sb.append("from "); //$NON-NLS-1$
        sb.append(introspectedTable
                .getAliasedFullyQualifiedTableNameAtRuntime());
        answer.addElement(new TextElement(sb.toString()));
        answer.addElement(getExampleIncludeElement());

        ifElement = new XmlElement("if"); //$NON-NLS-1$
        ifElement.addAttribute(new Attribute("test", "orderByClause != null")); //$NON-NLS-1$ //$NON-NLS-2$
        ifElement.addElement(new TextElement("order by ${orderByClause}")); //$NON-NLS-1$
        answer.addElement(ifElement);

        if (context.getPlugins()
                .sqlMapSelectByExampleWithoutBLOBsElementGenerated(answer,
                        introspectedTable)) {
            parentElement.addElement(answer);
        }
        answer.addElement(new TextElement("limit 1"));
    }
}

View Code

修改JavaMapperGenerator,在getCompilationUnits方法中增加addSelectOneByExampleMethod(interfaze),这里顺带把生成的代码排了个序,并且把不常用的几个方法给注释掉了。

@Override
public List<CompilationUnit> getCompilationUnits() {
progressCallback.startTask(getString("Progress.17", //$NON-NLS-1$
introspectedTable.getFullyQualifiedTable().toString()));
CommentGenerator commentGenerator = context.getCommentGenerator();

FullyQualifiedJavaType type = new FullyQualifiedJavaType(
introspectedTable.getMyBatis3JavaMapperType());
Interface interfaze = new Interface(type);
interfaze.setVisibility(JavaVisibility.PUBLIC);
commentGenerator.addJavaFileComment(interfaze);

String rootInterface = introspectedTable
.getTableConfigurationProperty(PropertyRegistry.ANY_ROOT_INTERFACE);
if (!stringHasValue(rootInterface)) {
rootInterface = context.getJavaClientGeneratorConfiguration()
.getProperty(PropertyRegistry.ANY_ROOT_INTERFACE);
}

if (stringHasValue(rootInterface)) {
FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(
rootInterface);
interfaze.addSuperInterface(fqjt);
interfaze.addImportedType(fqjt);
}

addInsertSelectiveMethod(interfaze);
addDeleteByPrimaryKeyMethod(interfaze);
addDeleteByExampleMethod(interfaze);
addUpdateByPrimaryKeySelectiveMethod(interfaze);
addUpdateByExampleSelectiveMethod(interfaze);
addSelectByPrimaryKeyMethod(interfaze);
addSelectOneByExampleMethod(interfaze); //由addSelectByExampleWithoutBLOBsMethod改造而来
addSelectByExampleWithoutBLOBsMethod(interfaze);
addSelectByExampleWithBLOBsMethod(interfaze);
addCountByExampleMethod(interfaze);
//addInsertMethod(interfaze);
//addUpdateByExampleWithBLOBsMethod(interfaze);
//addUpdateByExampleWithoutBLOBsMethod(interfaze);
//addUpdateByPrimaryKeyWithBLOBsMethod(interfaze);
//addUpdateByPrimaryKeyWithoutBLOBsMethod(interfaze);

List<CompilationUnit> answer = new ArrayList<>();
if (context.getPlugins().clientGenerated(interfaze, null,
introspectedTable)) {
answer.add(interfaze);
}

List<CompilationUnit> extraCompilationUnits = getExtraCompilationUnits();
if (extraCompilationUnits != null) {
answer.addAll(extraCompilationUnits);
}

return answer;
}

//增加方法
protected void addSelectOneByExampleMethod(Interface interfaze) {   AbstractJavaMapperMethodGenerator methodGenerator = new SelectOneByExampleMethodGenerator();   initializeAndExecuteGenerator(methodGenerator, interfaze); }

 同样地,修改XMLMapperGenerator

protected XmlElement getSqlMapElement() {
        FullyQualifiedTable table = introspectedTable.getFullyQualifiedTable();
        progressCallback.startTask(getString(
                "Progress.12", table.toString())); //$NON-NLS-1$
        XmlElement answer = new XmlElement("mapper"); //$NON-NLS-1$
        String namespace = introspectedTable.getMyBatis3SqlMapNamespace();
        answer.addAttribute(new Attribute("namespace", //$NON-NLS-1$
                namespace));

        context.getCommentGenerator().addRootComment(answer);

        addResultMapWithoutBLOBsElement(answer);
        addResultMapWithBLOBsElement(answer);
        addExampleWhereClauseElement(answer);
        addMyBatis3UpdateByExampleWhereClauseElement(answer);
        addBaseColumnListElement(answer);
        addBlobColumnListElement(answer);
        addInsertSelectiveElement(answer);
        addDeleteByPrimaryKeyElement(answer);
        addDeleteByExampleElement(answer);
        addUpdateByPrimaryKeySelectiveElement(answer);
        addUpdateByExampleSelectiveElement(answer);
        addSelectByPrimaryKeyElement(answer);
        addSelectOneByExampleElement(answer);
        addSelectByExampleWithoutBLOBsElement(answer);
        addSelectByExampleWithBLOBsElement(answer);
        addCountByExampleElement(answer);
        //addSelectByExampleWithBLOBsElement(answer);
        //addInsertElement(answer);
        //addUpdateByExampleWithBLOBsElement(answer);
        //addUpdateByExampleWithoutBLOBsElement(answer);
        //addUpdateByPrimaryKeyWithBLOBsElement(answer);
        //addUpdateByPrimaryKeyWithoutBLOBsElement(answer);

        return answer;
}

//增加方法
protected void addSelectOneByExampleElement(XmlElement parentElement) {   if (introspectedTable.getRules().generateSelectByExampleWithoutBLOBs()) {     AbstractXmlElementGenerator elementGenerator = new SelectOneByExampleElementGenerator();     initializeAndExecuteGenerator(elementGenerator, parentElement);   } }

投入使用

deploy项目打包,然后在需要使用的项目中,修改pom.xml引入,再使用generator的generate命令即可

       <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.7</version>
                <dependencies>
                    <dependency>
                        <groupId>com.mine.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.0-SNAPSHOT</version>
                    </dependency>
                </dependencies>
            </plugin>