MyBatisGenerator修改源码实现model类增加Lombok注解及实现数据库注释
1. 增加自定义Lombok插件类
/**
* Copyright 2006-2020 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.plugins;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
/**
* @author 2356999674@qq.com
*/
public class LombokPlugin extends PluginAdapter {
private final Collection<Annotations> annotations;
/**
* LombokPlugin constructor
*/
public LombokPlugin() {
annotations = new LinkedHashSet<Annotations>(Annotations.values().length);
}
/**
* @param warnings list of warnings
* @return always true
*/
@Override
public boolean validate(List<String> warnings) {
return true;
}
/**
* Intercepts base record class generation
*
* @param topLevelClass the generated base record class
* @param introspectedTable The class containing information about the table as
* introspected from the database
* @return always true
*/
@Override
public boolean modelBaseRecordClassGenerated(
TopLevelClass topLevelClass,
IntrospectedTable introspectedTable
) {
addAnnotations(topLevelClass);
return true;
}
/**
* Intercepts primary key class generation
*
* @param topLevelClass the generated primary key class
* @param introspectedTable The class containing information about the table as
* introspected from the database
* @return always true
*/
@Override
public boolean modelPrimaryKeyClassGenerated(
TopLevelClass topLevelClass,
IntrospectedTable introspectedTable
) {
addAnnotations(topLevelClass);
return true;
}
/**
* Intercepts "record with blob" class generation
*
* @param topLevelClass the generated record with BLOBs class
* @param introspectedTable The class containing information about the table as
* introspected from the database
* @return always true
*/
@Override
public boolean modelRecordWithBLOBsClassGenerated(
TopLevelClass topLevelClass,
IntrospectedTable introspectedTable
) {
addAnnotations(topLevelClass);
return true;
}
/**
* Prevents all getters from being generated.
* See SimpleModelGenerator
*
* @param method the getter, or accessor, method generated for the specified
* column
* @param topLevelClass the partially implemented model class
* @param introspectedColumn The class containing information about the column related
* to this field as introspected from the database
* @param introspectedTable The class containing information about the table as
* introspected from the database
* @param modelClassType the type of class that the field is generated for
*/
@Override
public boolean modelGetterMethodGenerated(
Method method,
TopLevelClass topLevelClass,
IntrospectedColumn introspectedColumn,
IntrospectedTable introspectedTable,
ModelClassType modelClassType
) {
return false;
}
/**
* Prevents all setters from being generated
* See SimpleModelGenerator
*
* @param method the setter, or mutator, method generated for the specified
* column
* @param topLevelClass the partially implemented model class
* @param introspectedColumn The class containing information about the column related
* to this field as introspected from the database
* @param introspectedTable The class containing information about the table as
* introspected from the database
* @param modelClassType the type of class that the field is generated for
* @return always false
*/
@Override
public boolean modelSetterMethodGenerated(
Method method,
TopLevelClass topLevelClass,
IntrospectedColumn introspectedColumn,
IntrospectedTable introspectedTable,
ModelClassType modelClassType
) {
return false;
}
/**
* Adds the lombok annotations' imports and annotations to the class
*
* @param topLevelClass the partially implemented model class
*/
private void addAnnotations(TopLevelClass topLevelClass) {
for (Annotations annotation : annotations) {
topLevelClass.addImportedType(annotation.javaType);
topLevelClass.addAnnotation(annotation.asAnnotation());
}
}
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
//@Data is default annotation
annotations.add(Annotations.DATA);
annotations.add(Annotations.EQUALS_AND_HASHCODE);
for (String annotationName : properties.stringPropertyNames()) {
if (annotationName.contains(".")) {
// Not an annotation name
continue;
}
String value = properties.getProperty(annotationName);
if (!Boolean.parseBoolean(value)) {
// The annotation is disabled, skip it
continue;
}
Annotations annotation = Annotations.getValueOf(annotationName);
if (annotation == null) {
continue;
}
String optionsPrefix = annotationName + ".";
for (String propertyName : properties.stringPropertyNames()) {
if (!propertyName.startsWith(optionsPrefix)) {
// A property not related to this annotation
continue;
}
String propertyValue = properties.getProperty(propertyName);
annotation.appendOptions(propertyName, propertyValue);
annotations.add(annotation);
annotations.addAll(Annotations.getDependencies(annotation));
}
}
}
public boolean clientGenerated(
Interface interfaze,
TopLevelClass topLevelClass,
IntrospectedTable introspectedTable
) {
interfaze.addImportedType(new FullyQualifiedJavaType(
"org.apache.ibatis.annotations.Mapper"));
interfaze.addAnnotation("@Mapper");
return true;
}
private enum Annotations {
DATA("data", "@Data", "lombok.Data"),
EQUALS_AND_HASHCODE("equalsAndHashCode", "@EqualsAndHashCode", "lombok.EqualsAndHashCode"),
BUILDER("builder", "@Builder", "lombok.Builder"),
ALL_ARGS_CONSTRUCTOR("allArgsConstructor", "@AllArgsConstructor", "lombok.AllArgsConstructor"),
NO_ARGS_CONSTRUCTOR("noArgsConstructor", "@NoArgsConstructor", "lombok.NoArgsConstructor"),
ACCESSORS("accessors", "@Accessors", "lombok.experimental.Accessors"),
TO_STRING("toString", "@ToString", "lombok.ToString");
private final String paramName;
private final String name;
private final FullyQualifiedJavaType javaType;
private final List<String> options;
Annotations(String paramName, String name, String className) {
this.paramName = paramName;
this.name = name;
this.javaType = new FullyQualifiedJavaType(className);
this.options = new ArrayList<String>();
}
private static Annotations getValueOf(String paramName) {
for (Annotations annotation : Annotations.values())
if (String.CASE_INSENSITIVE_ORDER.compare(paramName, annotation.paramName) == 0)
return annotation;
return null;
}
private static Collection<Annotations> getDependencies(Annotations annotation) {
if (annotation == ALL_ARGS_CONSTRUCTOR)
return Collections.singleton(NO_ARGS_CONSTRUCTOR);
else
return Collections.emptyList();
}
// A trivial quoting.
// Because Lombok annotation options type is almost String or boolean.
private static String quote(String value) {
if (Boolean.TRUE.toString().equals(value) || Boolean.FALSE.toString().equals(value))
// case of boolean, not passed as an array.
return value;
return value.replaceAll("[\\w]+", "\"$0\"");
}
private void appendOptions(String key, String value) {
String keyPart = key.substring(key.indexOf(".") + 1);
String valuePart = value.contains(",") ? String.format("{%s}", value) : value;
this.options.add(String.format("%s=%s", keyPart, quote(valuePart)));
}
private String asAnnotation() {
if (options.isEmpty()) {
return name;
}
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append("(");
boolean first = true;
for (String option : options) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(option);
}
sb.append(")");
return sb.toString();
}
}
}
2. 增加自定义注释生成类
/**
* Copyright 2006-2020 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.internal;
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.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.XmlElement;
import java.time.LocalDateTime;
import java.util.Properties;
/**
* @author 2356999674@qq.com
*/
public class MyCommentGenerator extends DefaultCommentGenerator {
private boolean suppressAllComments;
private boolean addRemarkComments;
private Properties properties;
public MyCommentGenerator() {
properties = new Properties();
}
@Override
public void addConfigurationProperties(Properties properties) {
// 获取自定义的 properties
this.properties.putAll(properties);
}
/**
* model类注释
*/
@Override
public void addModelClassComment(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
String author = properties.getProperty("author");
// 获取表注释
String remarks = introspectedTable.getRemarks();
topLevelClass.addJavaDocLine("/**");
topLevelClass.addJavaDocLine(" * " + remarks);
topLevelClass.addJavaDocLine(" *");
topLevelClass.addJavaDocLine(" * @author " + author);
topLevelClass.addJavaDocLine(" * @date " + LocalDateTime.now());
topLevelClass.addJavaDocLine(" */");
}
/**
* serialVersionUID注释
*/
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable) {
if (suppressAllComments) {
return;
}
field.addJavaDocLine("/**"); //$NON-NLS-1$
field.addJavaDocLine(" * 序列化 ID"); //$NON-NLS-1$
field.addJavaDocLine(" */"); //$NON-NLS-1$
}
/**
* 字段属性注释
*/
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
if (suppressAllComments) {
return;
}
StringBuilder sb = new StringBuilder();
field.addJavaDocLine("/**");
sb.append(" * ");
sb.append(introspectedColumn.getRemarks());
field.addJavaDocLine(sb.toString());
field.addJavaDocLine(" */");
}
/**
* Mapper接口方法注释
*/
@Override
public void addGeneralMethodComment(Method method, IntrospectedTable introspectedTable) {
if (suppressAllComments) {
return;
}
}
/**
* get方法注释
*/
@Override
public void addGetterComment(Method method, IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
if (suppressAllComments) {
return;
}
}
/**
* set注释
*/
@Override
public void addSetterComment(Method method, IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
if (suppressAllComments) {
return;
}
}
/**
* 添加xml文件的注释("完全反人类 没用")
*/
@Override
public void addComment(XmlElement xmlElement) {
if (suppressAllComments) {
return;
}
}
}
3. 重新打包
将GitHub上的mybatis/generator
项目clone到本地,在mybatis-generator-core目录下,分别将两个新加的类添加,添加之后重新打包生成mybatis-generator-core.jar即可
4. 配置generatorConf.xml
<?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>
<!--数据库驱动-->
<classPathEntry location="mysql-connector-java-5.1.16-bin.jar"/>
<context id="DB2Tables" targetRuntime="MyBatis3" defaultModelType="flat">
<!-- 生成的Java文件的编码 -->
<property name="javaFileEncoding" value="UTF-8"/>
<!-- 格式化java代码 -->
<property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter"/>
<!-- 格式化XML代码 -->
<property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter"/>
<!-- 为生成的Java模型类添加序列化接口 -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<!-- 启用自定义的插件 -->
<plugin type="org.mybatis.generator.plugins.LombokPlugin"/>
<!--type配置为自定义的注释生成类路径-->
<commentGenerator type="org.mybatis.generator.internal.MyCommentGenerator">
<!-- 去除自动生成的时间戳,避免重复提交SVN -->
<property name="suppressDate" value="true"/>
<!--<property name="suppressAllComments" value="false"/>-->
<property name="addRemarkComments" value="true"/>
<!-- 设置author,为model类注释中的author -->
<property name="author" value="xxx"/>
</commentGenerator>
<!--数据库链接地址账号密码-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8"
userId="root" password="123456">
<property name="useInformationSchema" value="true" />
</jdbcConnection>
<javaTypeResolver>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<property name="forceBigDecimals" value="true"/>
</javaTypeResolver>
<!--生成Model类存放位置-->
<javaModelGenerator targetPackage="com.test.model"
targetProject="src/main/java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!--生成XML映射文件存放位置-->
<sqlMapGenerator targetPackage="com.test.mapper"
targetProject="src/main/resources">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
<property name="trimStrings" value="true"/>
</sqlMapGenerator>
<!--生成Dao类存放位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.test.mapper"
targetProject="src/main/java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<!--生成对应表及类名-->
<table tableName="%" schema="test" domainObjectName="Test"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
<!-- 字段id为主键,并且自动递增 -->
<generatedKey column="id" sqlStatement="Mysql" identity="true"/>
<property name="useActualColumnNames" value="true"/>
<!--<ignoreColumn column="FRED"/>
<columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR"/>-->
</table>
</context>
</generatorConfiguration>
5. 运行MyBatisGenerator生成代码
在配置文件及jar包所在目录下打开命令行,运行命令
java -jar mybatis-generator-core-1.4.1-SNAPSHOT.jar -configfile generatorConfig.xml -overwrite