内容简介:这两天详细了解了想要自己定义注释生成器,先要找到 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
原则,另外付加入了我们需要的框架注解,不用再手工处理重复劳动了,解放了生产力,可以早点下班回家,多休息休息了(做梦中…)
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
付费:互联网知识经济的兴起
方军 / 机械工业出版社 / 2017-6-1 / CNY 59.00
关于互联网知识付费的首部作品 知识工作正在被重塑,知识经济正在开启互联网时代下半场 为你展现互联网知识经济全景大图,解读新物种的前世今生 内容简介 一个产业解读 三个分析工具 一组知识卡片 书是最早的知识载体,已有2000多年的付费历史,随着移动互联网的普及,新的知识经 济在今天爆发,知识的创造者和传播者从书后走到了书前,互联网知识经济正在拉开帷幕。知识的......一起来看看 《付费:互联网知识经济的兴起》 这本书的介绍吧!