这篇文章将为大家详细讲解有关Mybatis中如何使用Generator插件,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
这个很多博客上都有,也就不再重复论了,贴一下 code
public class LombokPlugin extends PluginAdapter { private final Set<Annotations> annotations; /** * LombokPlugin constructor */ public LombokPlugin() { annotations = new LinkedHashSet<>(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,@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<String> 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<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)) { 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(); } } }
主要是为了便于自动生成 Service 类
public class ServicePlugin extends PluginAdapter { private String targetProject; private String targetPackage; @Override public boolean validate(List<String> 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<GeneratedJavaFile> 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; } }
使用官方 mybatis generator 1.3.7 版本自动生产的实体类映射,是不能区分 Unsigned 和 Signed mysql 8.x(针对 java 项目,很容易出现类型字段溢出),尤其实际项目中,接手老的项目,之前一些数据库字段的设置,留下来的一些“坑”;
官方方案:(github 上也有一些小伙伴提了相应的 issue,好像没有 fixed)
<columnOverride>
元素覆盖指定的字段
缺点:在针对很多表的时候,都需要手动设置这些字段,而且很容易丢失(可能导致线上问题)
解决方案:
通过 download 官方 mybatis generator 源码,发现是可以自动识别 Unsigned 和 Signed 字段属性的,故修改了源码,打包,重新编译,达到了想要的目的,不需要针对指定的字段每一个修改(在 github 上已经和官方作者沟通,接受了 issue,将于下一个版本 1.4.0 做增强),由于官方作者一直没发布 1.4.0,故公司内部打包如下 Jar 版本:mybatis-generator-maven-plugin-1.3.7.2.jar
之前是打算直接写插件直接自动生成,但是考虑到有些项目还是用的 jdk8 以前的时间,老的项目也不能强制要求 jdk8 时间版本,故下面 ???? 只是展示如何处理.
在生成的实体类中加入下面处理
<columnOverride column="created_time" property="createdTime" typeHandler="org.apache.ibatis.type.LocalDateTimeTypeHandler" jdbcType="OTHER" javaType="java.time.LocalDateTime"/> <columnOverride column="updated_time" property="updatedTime" typeHandler="org.apache.ibatis.type.LocalDateTimeTypeHandler" jdbcType="OTHER" javaType="java.time.LocalDateTime"/>
注:在 mybatis 使用 jdk8 时间时,需要注意的是,mybatis 3.5.1+和 druid 1.1.20 是不支持 jdk8 的时间戳的druid-jdk8,不过,现在已经修复了,等下一个版本估计就能解决了(当然,也可以切换到 HikariCP 数据源)
市场上已经有很多 gradle 集成 mybatis generator,选择了其中一个比较好用的插件mybatis-generator-plugin(尊重原创作者),当然在公司内部处理,也针对这个插件做了相应的修改定制
针对上面 ???? 的一些处理,生成的结构图如下:
关于Mybatis中如何使用Generator插件就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。