千家信息网

Mybatis中如何使用Generator插件

发表于:2025-01-24 作者:千家信息网编辑
千家信息网最后更新 2025年01月24日,这篇文章将为大家详细讲解有关Mybatis中如何使用Generator插件,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。1. LombokPlugin
千家信息网最后更新 2025年01月24日Mybatis中如何使用Generator插件

这篇文章将为大家详细讲解有关Mybatis中如何使用Generator插件,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

1. LombokPlugin 插件

这个很多博客上都有,也就不再重复论了,贴一下 code

public class LombokPlugin extends PluginAdapter {    private final Set annotations;    /**     * LombokPlugin constructor     */    public LombokPlugin() {        annotations = new LinkedHashSet<>(Annotations.values().length);    }    /**     * @param warnings list of warnings     * @return always true     */    @Override    public boolean validate(List 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,@Builder,@NoArgsConstructor,@AllArgsConstructor is default annotation        annotations.add(Annotations.DATA);        annotations.add(Annotations.BUILDER);        annotations.add(Annotations.NO_ARGS_CONSTRUCTOR);        annotations.add(Annotations.ALL_ARGS_CONSTRUCTOR);        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));            }        }    }    private enum Annotations {        DATA("data", "@Data", "lombok.Data"),        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 options;        Annotations(String paramName, String name, String className) {            this.paramName = paramName;            this.name = name;            javaType = new FullyQualifiedJavaType(className);            options = new ArrayList<>();        }        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 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)) {                return value;            }            // case of boolean, not passed as an array.            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;            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. ServicePlugin 插件

主要是为了便于自动生成 Service 类

public class ServicePlugin extends PluginAdapter {    private String targetProject;    private String targetPackage;    @Override    public boolean validate(List list) {        return true;    }    @Override    public void setProperties(Properties properties) {        super.setProperties(properties);        String targetProject = this.properties.getProperty("targetProject");        if (StringUtility.stringHasValue(targetProject)) {            this.targetProject = targetProject;        } else {            throw new RuntimeException("targetProject 属性不能为空!");        }        String targetPackage = this.properties.getProperty("targetPackage");        if (StringUtility.stringHasValue(targetPackage)) {            this.targetPackage = targetPackage;        } else {            throw new RuntimeException("targetPackage 属性不能为空!");        }    }    /**     * @param introspectedTable     * @return     */    @Override    public List contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {        FullyQualifiedJavaType entityType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());        String domainObjectName = introspectedTable.getFullyQualifiedTable().getDomainObjectName();        String service = targetPackage + "." + domainObjectName + "Service";        TopLevelClass topLevelClass = new TopLevelClass(new FullyQualifiedJavaType(service));        topLevelClass.addImportedType(entityType);        topLevelClass.addImportedType(new FullyQualifiedJavaType(service));        topLevelClass.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Service"));        topLevelClass.addImportedType(new FullyQualifiedJavaType("org.springframework.beans.factory.annotation" +                ".Autowired"));        topLevelClass.addImportedType(new FullyQualifiedJavaType("cn.mwee.service.base_framework.mysql.service" +                ".BaseService"));        topLevelClass.addAnnotation("@Service(\"" + firstLetterLowerCase(domainObjectName + "Service") + "\")");        topLevelClass.setVisibility(JavaVisibility.PUBLIC);        topLevelClass.setSuperClass(new FullyQualifiedJavaType("BaseService<" + entityType.getShortName() + ">"));        setMapperField(introspectedTable, topLevelClass);        ElementUtil.addAuthorTag(topLevelClass);        return Arrays.asList(new GeneratedJavaFile(topLevelClass, targetProject, new DefaultJavaFormatter()));    }    /**     * @param introspectedTable     * @param clazz     */    private void setMapperField(IntrospectedTable introspectedTable, TopLevelClass clazz) {        // 实体类的类名        String domainObjectName = introspectedTable.getFullyQualifiedTable().getDomainObjectName();        // Mapper类所在包的包名        String mapperPackage = introspectedTable.getContext().getJavaClientGeneratorConfiguration().getTargetPackage();        Field mapperField = new Field();        // 设置Field的注解        mapperField.addAnnotation("@Autowired");        mapperField.setVisibility(JavaVisibility.PRIVATE);        // 设置Field的类型        mapperField.setType(new FullyQualifiedJavaType(domainObjectName + "Mapper"));        // 设置Field的名称        mapperField.setName(firstLetterLowerCase(domainObjectName) + "Mapper");        // 将Field添加到对应的类中        clazz.addField(mapperField);        // 对应的类需要import Mapper类(使用全限定类名)        clazz.addImportedType(new FullyQualifiedJavaType(mapperPackage + "." + domainObjectName + "Mapper"));    }    private String firstLetterLowerCase(String name) {        char c = name.charAt(0);        if (c >= 'A' && c <= 'Z') {            String temp = String.valueOf(c);            return name.replaceFirst(temp, temp.toLowerCase());        }        return name;    }}
3. 解决数据库 Unsigned 类型字段

使用官方 mybatis generator 1.3.7 版本自动生产的实体类映射,是不能区分 Unsigned 和 Signed mysql 8.x(针对 java 项目,很容易出现类型字段溢出),尤其实际项目中,接手老的项目,之前一些数据库字段的设置,留下来的一些"坑";

  • 官方方案:(github 上也有一些小伙伴提了相应的 issue,好像没有 fixed)

    元素覆盖指定的字段
    缺点:在针对很多表的时候,都需要手动设置这些字段,而且很容易丢失(可能导致线上问题)

  • 解决方案:

    通过 download 官方 mybatis generator 源码,发现是可以自动识别 Unsigned 和 Signed 字段属性的,故修改了源码,打包,重新编译,达到了想要的目的,不需要针对指定的字段每一个修改(在 github 上已经和官方作者沟通,接受了 issue,将于下一个版本 1.4.0 做增强),由于官方作者一直没发布 1.4.0,故公司内部打包如下 Jar 版本:mybatis-generator-maven-plugin-1.3.7.2.jar

4. 如何自动生成 jdk8 时间映射

之前是打算直接写插件直接自动生成,但是考虑到有些项目还是用的 jdk8 以前的时间,老的项目也不能强制要求 jdk8 时间版本,故下面 ???? 只是展示如何处理.

在生成的实体类中加入下面处理

注:在 mybatis 使用 jdk8 时间时,需要注意的是,mybatis 3.5.1+和 druid 1.1.20 是不支持 jdk8 的时间戳的druid-jdk8,不过,现在已经修复了,等下一个版本估计就能解决了(当然,也可以切换到 HikariCP 数据源)

5. 集成 gradle 插件

市场上已经有很多 gradle 集成 mybatis generator,选择了其中一个比较好用的插件mybatis-generator-plugin(尊重原创作者),当然在公司内部处理,也针对这个插件做了相应的修改定制

6. 附录

针对上面 ???? 的一些处理,生成的结构图如下:


关于Mybatis中如何使用Generator插件就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

0