一般情况下我们项目以实体为中心,所以我们考虑到我们项目在迭代过程中,实体新增字段,不会影响以往的代码,所以我们需要将mapper文件分成两个并且属于同一个命名空间下,然后关闭全局null字段更新,避免迭代之后,其它业务的更新影响新增的字段值。
所以mapper.xml的目录结构如下,default中存放生成的xml,立马只有resultmap预计basecolumnList。自定义的mapper全部放在sql.mapper目录下,避免被新生成的xml文件覆盖。
└───resources
│ │ └───sql
│ │ ├───default
│ │ └───mapper
public class CodeGenerator {
public static class DataSourceProperties {
public static final String jdbcUrl = "jdbc:mysql://127.0.0.1:3306/user?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8";
public static final String username = "root";
public static final String password = "root";
}
public static class Config {
//项目根目录
public static String rootPath=System.getProperty("user.dir");
public static String author="xxxx";
//xml文件路径
public static StringBuilder xmlPath=new StringBuilder();
//java文件生成根路径
public static StringBuilder outputDir=new StringBuilder();
//自定义一个公共PO用于存放公共字段,并且标记为序列化
public static String superEntity="com.qimo.framework.mybatis.BasePO";
//公用的字段
public static final String[] commonFields = {"update_time","create_time","update_user_code","create_user_code",
"update_username","create_username","deleted","available"};
//开发人员可以在这里修改各自项目的配置
//todo 如果是多模块,需要填写模块名,如果不需要可以为空串
public static String module="模块名称";
//todo 代码根包名
public static String parent_package="com.qimo.user";
//todo 需要生成的表
public static String[] tables=new String[]{""};
//todo 表的前缀,如果不需要填空
public static String[] prefix=new String[]{""};
static{
outputDir.append(rootPath).append(File.separator).append(module).append("/src/main/java");
xmlPath.append(rootPath).append(File.separator).append(module).append("/src/main/resources/sql/default/");
}
}
public static void main(String[] args) {
FastAutoGenerator.create(DataSourceProperties.jdbcUrl, DataSourceProperties.username, DataSourceProperties.password)
//全局配置
.globalConfig(builder -> {
builder.author(Config.author)
.enableSwagger()
.outputDir(Config.outputDir.toString());
})
//类型映射配置
.dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
//自定义类型转换配置
if (typeCode == Types.SMALLINT || typeCode == Types.TINYINT) {
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
}))
//包配置
.packageConfig(builder -> {
builder.parent(Config.parent_package)
.pathInfo(Collections.singletonMap(OutputFile.xml, Config.xmlPath.toString()));
})
//自定义配置
.injectionConfig(builder -> {
builder.customFile(consumer-> consumer
.fileName("Mapper.xml")
.filePath(Config.xmlPath.toString())
.enableFileOverride()
.templatePath("/templates/mapper.xml.ftl"))
;
})
//具体的生成文件的策略配置
.strategyConfig(builder -> {
builder.addInclude(Arrays.asList(Config.tables))
// .enableSkipView()
.addTablePrefix(Arrays.asList(Config.prefix))
.entityBuilder()
.superClass(Config.superEntity)
.addIgnoreColumns(Config.commonFields)
.enableFileOverride()
.enableLombok()
//controller配置启用rest风格才会使用@RestController注解
.controllerBuilder()
.enableRestStyle()
//开启生成mapper
.mapperBuilder()
.enableBaseResultMap()
.enableBaseColumnList()
//由于此版本Mapper.java与xml文件是一起生成的,所以适配我们的规范,此处不开启mapper覆盖,由自定义配置进行生成我们的xml文件
// .enableFileOverride()
;
})
//模板配置,如果你没有自定义的一些模板配置,这里直接使用默认即可。
.templateConfig(config->config.entity("/templates/entity"))
//模板引擎配置
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}
所有PO的父类,公共字段,并设置填充注解
@Data
public class BasePO implements Serializable{
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 创建日期
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新用户编码
*/
@TableField(fill = FieldFill.UPDATE)
private String updateUserCode;
/**
* 创建用户编码
*/
@TableField(fill = FieldFill.INSERT)
private String createUserCode;
/**
* 更新用户名
*/
@TableField(fill = FieldFill.UPDATE)
private String updateUsername;
/**
* 创建用户名
*/
@TableField(fill = FieldFill.INSERT)
private String createUsername;
/**
* 是否删除 0-正常 1-已删除
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
/**
* 是否可用,0-不可用,1-可用
*/
// @TableField(fill = FieldFill.INSERT)
private Integer available;
}
公共字段填充,SystemContext 各自项目的请求上下文。
public class BasePOMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
LocalDateTime now = LocalDateTime.now();
this.setFieldValByName(create_time, now,metaObject);
this.setFieldValByName(update_time, now,metaObject);
this.setFieldValByName(create_user_code,
//SystemContext.getUserInfo().getUserCode() 此处注意空指针
StringUtils.defaultIfEmpty(SystemContext.getUserInfo().getUserCode(),"0"),metaObject);
this.setFieldValByName(create_username,StringUtils.defaultIfEmpty(SystemContext.getUserInfo().getUserName(),"system"),metaObject);
this.setFieldValByName(deleted,0,metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName(update_time, LocalDateTime.now(),metaObject);
this.setFieldValByName(update_user_code,StringUtils.defaultIfEmpty(SystemContext.getUserInfo().getUserCode(),"0"),metaObject);
this.setFieldValByName(update_username,StringUtils.defaultIfEmpty(SystemContext.getUserInfo().getUserName(),"system"),metaObject);
}
}
我们可以将代码生成器直接放在项目的test目录下,方便开发对代码的迭代升级。直接执行生成器的main方法即可。
对开发而言是十分便利的,不要小看这一点点的便利性,它能提升开发人员的工作效率。并且降低开发人员的烦躁情绪以及降低出错率。
对应的配置文件如下
#配置.xml文件所在路径
mybatis-plus.mapper-locations = classpath*:/sql/**/*.xml
mybatis-plus.typeAliasesPackage = com.qimo.user.entity
mybatis-plus.global-config.db-config.id-type = AUTO
#填充策略设置成not_null
mybatis-plus.global-config.db-config.field-strategy = NOT_NULL
mybatis-plus.global-config.db-config.column-underline = true
mybatis-plus.global-config.db-config.refresh-mapper = true
#逻辑删除值设置搭配注解@TableLogic使用
mybatis-plus.global-config.db-config.logic-delete-value = 1
mybatis-plus.global-config.db-config.logic-not-delete-value = 0
mybatis-plus.global-config.banner = false
mybatis-plus.configuration.map-underscore-to-camel-case = true
mybatis-plus.configuration.cache-enabled = false
#Map查询时值为null是否反回对应的投影字段
mybatis-plus.configuration.call-setters-on-nulls = true
正对需要进行null更新的业务类型可以看我的另一篇文章【mybatis-plus支持null字段全量更新】