让 MyBatis Generator 用数据库注释作 Java 注释,并支持附加注解

栏目: 数据库 · 发布时间: 6年前

内容简介:这两天详细了解了想要自己定义注释生成器,先要找到 MyBatis Generator 提供的注释生成器接口。这个接口是拿起键盘就是干!哈哈,敲代码吧(

这两天详细了解了 MyBatis Generator 这款工具,它生成 POJO 以及 Mapper 的功能还是比较实用的,而且由于生成的代码也是工程代码的一部分,自定义起来相对库来说也会方便一些,因此我打算将其用于公司的工程中。但是,MyBatis Generator 也是有一些不足的,其默认生成的 Java 注释实在惨不忍睹,包含了大量重复的无效信息;此外,我想实现在数据库中写一次注释,在 Java 代码中复用这种效果,同时还需要在生成 Java POJO 的过程中附加上公司自研 RPC 框架的注解。这两点功能 MyBatis Generator 默认提供的注释生成器是不能很好的支持的。因此需要自定义一个注释生成器来实现这些特定的需求。

实现

想要自己定义注释生成器,先要找到 MyBatis Generator 提供的注释生成器接口。这个接口是 org.mybatis.generator.api.CommentGenerator 其默认实现为 org.mybatis.generator.internal.DefaultCommentGenerator (默认实现也是唯一实现),其实我翻了一翻 DefaultCommentGenerator 的代码,发现其默认是支持将数据库注释作为生成的 Java 类的注释的,只是功能默认是关闭的,但是附加注解,默认实现是不支持的,我们就来自己实现吧。由于 MyBatis Generator 并没有为我们提供抽象类级别的生成器,只有一个顶层借口,而且顶层接口包含了过量的方法,大部分我们都用不到,因此们需要自己实现一个抽象类,来屏蔽不需要的方法接口,然后让自定义注释生成器继承抽象类(当然也可以直接继承默认实现,但我觉得那不是好的选择)。

拿起键盘就是干!哈哈,敲代码吧( 工程 在我的 GitHub 上):

package io.github.since1986.mybatis.comment.generator;

import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.XmlElement;

import java.util.Properties;
import java.util.Set;

// 抽象类,屏蔽掉接口中过多的不需要实现的方法,不需要的什么都不做就好了
public abstract class AbstractCommentGenerator implements CommentGenerator {

    @Override
    public void addConfigurationProperties(Properties properties) {

    }

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

    }

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

    }

    @Override
    public void addModelClassComment(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {

    }

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

    }

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

    }

    @Override
    public void addEnumComment(InnerEnum innerEnum, 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 addGeneralMethodComment(Method method, IntrospectedTable introspectedTable) {

    }

    @Override
    public void addJavaFileComment(CompilationUnit compilationUnit) {

    }

    @Override
    public void addComment(XmlElement xmlElement) {

    }

    @Override
    public void addRootComment(XmlElement rootElement) {

    }

    @Override
    public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable, Set<FullyQualifiedJavaType> imports) {

    }

    @Override
    public void addGeneralMethodAnnotation(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {

    }

    @Override
    public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable, Set<FullyQualifiedJavaType> imports) {

    }

    @Override
    public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {

    }

    @Override
    public void addClassAnnotation(InnerClass innerClass, IntrospectedTable introspectedTable, Set<FullyQualifiedJavaType> imports) {

    }
}
package io.github.since1986.mybatis.comment.generator;

import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.internal.util.StringUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;

// 这个是我们具体的实现,可以借鉴默认实现的一些办法去写
public class CommentWithAnnotationCommentGenerator extends AbstractCommentGenerator {

    private static final Logger LOGGER = LoggerFactory.getLogger(CommentWithAnnotationCommentGenerator.class);

    private static final String SUPPRESS_ALL_ANNOTATIONS = "suppressAllAnnotations";
    private static final String FIELD_ANNOTATION_FULLY_QUALIFIED_NAMES = "fieldAnnotationFullyQualifiedNames";
    private static final String CLASS_ANNOTATION_FULLY_QUALIFIED_NAMES = "classAnnotationFullyQualifiedNames";

    private static final String FIELD_COMMENT_TEMPLATE = "/** %s */";
    private static final String CLASS_COMMENT_TEMPLATE = "/** %s */";

    private Properties properties = new Properties();

    // 保留官方的重要设置属性,这样更符合直觉
    private boolean suppressAllComments;

    private boolean suppressAllAnnotations;
    private List<String> fieldAnnotationFullyQualifiedNames = new LinkedList<>();
    private List<String> classAnnotationFullyQualifiedNames = new LinkedList<>();

    @Override
    public void addConfigurationProperties(Properties properties) {
        this.properties.putAll(properties);
        suppressAllComments = StringUtility.isTrue(this.properties.getProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_ALL_COMMENTS));
        suppressAllAnnotations = StringUtility.isTrue(this.properties.getProperty(SUPPRESS_ALL_ANNOTATIONS));
        fieldAnnotationFullyQualifiedNames.addAll(Util.toList(this.properties.getProperty(FIELD_ANNOTATION_FULLY_QUALIFIED_NAMES)));
        classAnnotationFullyQualifiedNames.addAll(Util.toList(this.properties.getProperty(CLASS_ANNOTATION_FULLY_QUALIFIED_NAMES)));
    }

    @Override
    public void addFieldComment(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
        if (!suppressAllComments && Util.isNotBlank(introspectedColumn.getRemarks())) {
            LOGGER.debug(String.format("field = %s, column = %s, columnRemarks = %s", field.getName(), introspectedColumn.getActualColumnName(), introspectedColumn.getRemarks()));
            field.addJavaDocLine(String.format(FIELD_COMMENT_TEMPLATE, introspectedColumn.getRemarks()));
        }
        if (!suppressAllAnnotations) {
            LOGGER.debug(String.format("fieldAnnotationFullyQualifiedNames = %s]", fieldAnnotationFullyQualifiedNames));
            fieldAnnotationFullyQualifiedNames.forEach(fieldAnnotationFullyQualifiedName -> {
                field.addJavaDocLine(Util.atAnnotationName(fieldAnnotationFullyQualifiedName));
            });
        } else {
            LOGGER.debug(String.format("suppressAllComments = %b, suppressAllAnnotations = %b", suppressAllComments, suppressAllAnnotations));
        }
    }

    @Override
    public void addModelClassComment(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        if (!suppressAllComments && Util.isNotBlank(introspectedTable.getRemarks())) {
            LOGGER.debug(String.format("class = %s, table = %s, tableRemarks = %s", topLevelClass.getType(), introspectedTable.getFullyQualifiedTable(), introspectedTable.getRemarks()));
            topLevelClass.addJavaDocLine(String.format(CLASS_COMMENT_TEMPLATE, introspectedTable.getRemarks()));
        }
        if (!suppressAllAnnotations) {
            LOGGER.debug(String.format("classAnnotationFullyQualifiedNames = %s]", classAnnotationFullyQualifiedNames));
            classAnnotationFullyQualifiedNames.forEach(classAnnotationFullyQualifiedName -> {
                topLevelClass.addJavaDocLine(Util.atAnnotationName(classAnnotationFullyQualifiedName));
                topLevelClass.addImportedType(new FullyQualifiedJavaType(classAnnotationFullyQualifiedName));
            });
            fieldAnnotationFullyQualifiedNames.forEach(fieldAnnotationFullyQualifiedName -> {
                topLevelClass.addImportedType(new FullyQualifiedJavaType(fieldAnnotationFullyQualifiedName));
            });
        } else {
            LOGGER.debug(String.format("suppressAllComments = %b, suppressAllAnnotations = %b", suppressAllComments, suppressAllAnnotations));
        }
    }

    static class Util {

        static boolean isBlank(String string) {
            return string == null || string.trim().length() == 0;
        }

        static boolean isNotBlank(String string) {
            return !isBlank(string);
        }

        static List<String> toList(String commaSeparatedString) {
            List<String> list = new LinkedList<>();
            assert isNotBlank(commaSeparatedString);
            StringTokenizer stringTokenizer = new StringTokenizer(commaSeparatedString, ",");
            String token;
            while (stringTokenizer.hasMoreTokens()) {
                token = stringTokenizer.nextToken();
                list.add(token);
            }
            if (list.size() == 0) {
                list.add(commaSeparatedString);
            }
            return list;
        }

        static String atAnnotationName(String annotationFullyQualifiedName) {
            int lastIndexOfDot = annotationFullyQualifiedName.lastIndexOf(".");
            assert lastIndexOfDot != -1;
            String atAnnotationName = annotationFullyQualifiedName.substring(lastIndexOfDot + 1);
            assert isNotBlank(atAnnotationName);
            return String.format("@%s", atAnnotationName);
        }
    }
}

上边两个类是我们自定义注释生成器的所有核心内容,我在实现的时候踩了一个小小的坑,在注释生成器接口中有几个 addFieldAnnotation addClassAnnotation 命名中包含注解字眼的方法,我一开始想当然的以为附加注解需要在这里面实现,后来才发现,两者没什么关系

/**
     * Adds a @Generated annotation to a class.
     *
     * @param innerClass
     *            the class
     * @param introspectedTable
     *            the introspected table
     * @param imports
     *   the comment generator may add a required imported type to this list
     * 
     * @since 1.3.6
     */
     void addClassAnnotation(InnerClass innerClass, IntrospectedTable introspectedTable,
                 Set<FullyQualifiedJavaType> imports);

上边是 addClassAnnotation 的注释,可以看到,其根 @Generated 有关。我们自己想要附加注解,实际上还是在附加注释那一步以字符串的形式附加上去的,与这几个 *Annotation 方法无关。

使用

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <properties resource="package/to/your/data_source.properties"/>
    <context id="context_mysql" defaultModelType="flat" targetRuntime="MyBatis3">
        <property name="javaFileEncoding" value="UTF-8"/>

        <!-- CommentWithAnnotationCommentGenerator -->
        <commentGenerator type="io.github.since1986.mybatis.comment.generator.CommentWithAnnotationCommentGenerator">
            <property name="fieldAnnotationFullyQualifiedNames" value="one.your.customer.FieldAnnotationClass,another.your.customer.FieldAnnotationClass"/>
            <property name="classAnnotationFullyQualifiedNames" value="one.your.customer.ClassAnnotationClass,another.your.customer.ClassAnnotationClass"/>
        </commentGenerator>

        <jdbcConnection driverClass="${driverClass}" connectionURL="${connectionURL}" userId="${userId}" password="${password}">
            <!-- better keep this -->
            <property name="useInformationSchema" value="true"/>
        </jdbcConnection>

        <javaModelGenerator targetPackage="your.model" targetProject="/path/to/your/project/src/main/java">
        </javaModelGenerator>


        <sqlMapGenerator targetPackage="your.mapper" targetProject="src/main/resources">
        </sqlMapGenerator>


        <javaClientGenerator targetPackage="your.mapper" type="ANNOTATEDMAPPER" targetProject="src/main/java">
        </javaClientGenerator>

        <table tableName="your_table" domainObjectName="YourDomain">
        </table>
    </context>
</generatorConfiguration>

在生成的 Java 中会将数据库注释作为注释,诠释了 DRY 原则,另外付加入了我们需要的框架注解,不用再手工处理重复劳动了,解放了生产力,可以早点下班回家,多休息休息了(做梦中…)


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Cascading Style Sheets 2.0 Programmer's Reference

Cascading Style Sheets 2.0 Programmer's Reference

Eric A. Meyer / McGraw-Hill Osborne Media / 2001-03-20 / USD 19.99

The most authoritative quick reference available for CSS programmers. This handy resource gives you programming essentials at your fingertips, including all the new tags and features in CSS 2.0. You'l......一起来看看 《Cascading Style Sheets 2.0 Programmer's Reference》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

html转js在线工具
html转js在线工具

html转js在线工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具