自定义注释
自定义注解
指定xml文件模式
上一篇文章详细阐述了xml配置文件的各种标签及其含义。其实从<context/>标签开始,每一个标签都对应一个实体类。context.class对应<context/>标签,而每一个子标签都对应一个属性;如图:。有了实体类的关系,那么自定义起来还不是易如反掌。
自定义添加注释
以用数据库中字段的注释,作为Model属性的注释为例:
针对注解的标签是<commentGenerator/>,对应的接口实体类是CommentGenerator
,该类中封装了针对GetterComment、SetterComment、FieldComment、ClassComment以及xml中的注解,这些方法在生成实体类时,会通过Context被调用;该接口默认有默认的实现类DefaultCommentGenerator
。切入点就是该DefaultCommentGenerator
:
- 去除原始的Getter、Setter方法的默认注释,只要实现
addSetterComment
、addGetterComment
方法,返回为空,原生注释就不会再生成; - 实现
addFieldComment
方法(管控Model属性的注释内容),具体如下:
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
StringBuffer sb = new StringBuffer();
field.addJavaDocLine("/**");
field.addJavaDocLine("*");
// 获取表的名称
sb.append("* TableName: " + introspectedTable.getFullyQualifiedTable() + "\n\t");
//获取该表中属性的名称
sb.append("* ColumnName: " + introspectedColumn.getActualColumnName() + "\n\t");
sb.append("*<pre>" + "\n\t");
//该字段的comment 信息
sb.append("*" + introspectedColumn.getRemarks());
field.addJavaDocLine(sb.toString());
field.addJavaDocLine("*</pre>");
field.addJavaDocLine("**/");
}
解释:
addFieldComment
方法有重载,意思是是否标注当前属性对应数据库的那个字段,看个人需要选择性实现。IntrospectedTable
实体类封装了数据库表对应的原始信息,相应的IntrospectedColumn
则是封装了表中字段的原始信息,所有我们通过该对象获取到字段的注释,在添加到实体类的属性上.(我这个地方没有封装,源码中是封装了生成所有注释的通用方法,实际运用中可统一封装)。MyBatis将java中类、方法、接口、枚举、内部类、内部类枚举抽象成
JavaElement
对象,而Field
则是针对类中属性的封装。本人感觉xml文件中的注释多余,就实现
addComment
方法,不做任何的实现,去除xml中的原生注释;
自定义注解
在目前我们开发中,注解使用的非常广发,大大简化了我们的开发,比如:
LomBook
针对实体类对象各种注解,最常用的@Data
(@Setter
,@Getter
)、@NoArgsConstructor
等。那能否再Model上自动添加上我们需要的注解呢?我们知道Spring Boot中Mapper接口对应的注解是
@Repository
注解,但原生生成时不会有该注解的,那要一个一个类手动添加吗?
针对以上需求,我们详细聊聊PluginAdapter
。通过Idea查看该类的结构,可以看到该中方法非常之多,但可分为:
clientxxxx相关的方法控制Mapper接口生成规则,细粒度到每个能够生成的方法上
sqlMapxxxx相关方法控制xml文件生成的方式,细粒度到每个能够生成的方法上;
modelxxxx相关方法控制Model类的生成规则。model类的生成规则有三种,所以model相关的方法也是关于三种规则生成时的相关方法;
针对以上需求,我们逐个击破:
- 生成的Model上增加
Lombook
相关的注释,继承PluginAdapter类,重写modelBaseRecordClassGenerated
方法,具体如下:
@Override
public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
javaTypes().forEach(item->topLevelClass.addImportedType(item));
addAnnons().forEach(item->topLevelClass.addAnnotation(item));
return super.modelBaseRecordClassGenerated(topLevelClass, introspectedTable);
}
//注解对应的完整的类路径
private List<FullyQualifiedJavaType> javaTypes(){
List<FullyQualifiedJavaType> javaTypes = new ArrayList<>(2);
javaTypes.add(new FullyQualifiedJavaType("lombok.Data"));
javaTypes.add(new FullyQualifiedJavaType("lombok.NoArgsConstructor"));
return javaTypes;
}
//需要添加的注解
private List<String> addAnnons(){
List<String> annons = new ArrayList<>(2);
annons.add("@Data");
annons.add("@NoArgsConstructor");
return annons;
}
通过上面方法,在生成Model时会自动添加上相应的注解;再比如:Spring Boot的Swagger也是通过注解,如果返回前端的实体类也是通过Mybatis Generator自动生成,那么就可以将@ApiModel
以及@ApiModelProperty
注解添加到生成的类中;是不是特别的方便;
xml文件的两种模式
在MyBatis生成的xml默认是append的方式,比如:随着需求版本的迭代,数据库中的字段改变,对应的实体类以及xml需要重新生成,那么默认新生成的xml内容,会追加到旧的版本中,这时启动项目会报错(resultMap标签重复),所以这时需要将旧的覆盖掉;
此时我们要干预xml文件的生成方式,那么继承PluginAdaptor
重写sqlMapGenerated
方法。该方法中GeneratedXmlFile
参数是封装xml文件的所有属性,其中私有属性isMergeable
即是否合并的意思,默认为false,那么我们现在就要改变该属性的值。可惜的是该属性没有公开,需要通过反射改变,具体如下:
@Override
public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
try {
Field field = sqlMap.getClass().getDeclaredField("isMergeable");
field.setAccessible(true);
field.setBoolean(sqlMap, false);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
return true;
}
在生成时就会覆盖原来的旧文件。那如果自定义的sql也写在该文件,那么也会被覆盖,目前我实践的解决方案有两种:
- 通过版本管理工具控制。比如git,被覆盖掉的方法,都会有提示或者能够重新回退到最近的一个版本,方法就可以再找回来;
- 分两个xml文件,同一个Mapper接口对应两个xml,一个是自动生成的,永远保存自动生成的,另外在创建一个自定义的sql语句的xml文件。
个人推荐第二种,最不易出错。